@forgeax/engine-runtime 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (606) hide show
  1. package/README.md +49 -83
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts +2 -0
  4. package/dist/__tests__/animation-graph-collect-roundtrip.test.d.ts.map +1 -0
  5. package/dist/__tests__/animation-graph-serialize.unit.test.d.ts +2 -0
  6. package/dist/__tests__/animation-graph-serialize.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts +2 -0
  8. package/dist/__tests__/asset-brand-and-name-retirement.test.d.ts.map +1 -0
  9. package/dist/__tests__/asset-error-exhaustive.test-d.d.ts +11 -0
  10. package/dist/__tests__/asset-error-exhaustive.test-d.d.ts.map +1 -0
  11. package/dist/__tests__/asset-registry-guid-reverse.test.d.ts +2 -0
  12. package/dist/__tests__/asset-registry-guid-reverse.test.d.ts.map +1 -0
  13. package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts +2 -0
  14. package/dist/__tests__/asset-registry-hdr-equirect.test.d.ts.map +1 -0
  15. package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts +2 -0
  16. package/dist/__tests__/asset-registry-mesh-pack-payload.dawn.test.d.ts.map +1 -0
  17. package/dist/__tests__/asset-registry-mounts.test.d.ts +2 -0
  18. package/dist/__tests__/asset-registry-mounts.test.d.ts.map +1 -0
  19. package/dist/__tests__/asset-registry.browser.test.d.ts +2 -0
  20. package/dist/__tests__/asset-registry.browser.test.d.ts.map +1 -0
  21. package/dist/__tests__/asset-registry.dawn.test.d.ts +2 -0
  22. package/dist/__tests__/asset-registry.dawn.test.d.ts.map +1 -0
  23. package/dist/__tests__/asset-registry.listCatalog.test.d.ts +2 -0
  24. package/dist/__tests__/asset-registry.listCatalog.test.d.ts.map +1 -0
  25. package/dist/__tests__/asset-registry.mesh-bin.test.d.ts +2 -0
  26. package/dist/__tests__/asset-registry.mesh-bin.test.d.ts.map +1 -0
  27. package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
  28. package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
  29. package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts +2 -0
  30. package/dist/__tests__/asset-registry.raw-container-failfast.test.d.ts.map +1 -0
  31. package/dist/__tests__/asset-registry.recursive.spec.d.ts +2 -0
  32. package/dist/__tests__/asset-registry.recursive.spec.d.ts.map +1 -0
  33. package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
  34. package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
  35. package/dist/__tests__/asset.unit.test.d.ts +2 -0
  36. package/dist/__tests__/asset.unit.test.d.ts.map +1 -0
  37. package/dist/__tests__/audio-load-by-guid.unit.test.d.ts +2 -0
  38. package/dist/__tests__/audio-load-by-guid.unit.test.d.ts.map +1 -0
  39. package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
  40. package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
  41. package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts +2 -0
  42. package/dist/__tests__/basis-catalog-dispatch.integration.test.d.ts.map +1 -0
  43. package/dist/__tests__/builtin-asset-registry.unit.test.d.ts +2 -0
  44. package/dist/__tests__/builtin-asset-registry.unit.test.d.ts.map +1 -0
  45. package/dist/__tests__/collect-scene-asset.test.d.ts +1 -2
  46. package/dist/__tests__/collect-scene-asset.test.d.ts.map +1 -1
  47. package/dist/__tests__/collect-scene-mount-collapse.test.d.ts +2 -0
  48. package/dist/__tests__/collect-scene-mount-collapse.test.d.ts.map +1 -0
  49. package/dist/__tests__/collect-transient-roundtrip.test.d.ts +1 -1
  50. package/dist/__tests__/collect-transient-roundtrip.test.d.ts.map +1 -1
  51. package/dist/__tests__/collect-transient-skip.test.d.ts +1 -2
  52. package/dist/__tests__/collect-transient-skip.test.d.ts.map +1 -1
  53. package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts +2 -0
  54. package/dist/__tests__/compressed-texture-physical-extent.integration.test.d.ts.map +1 -0
  55. package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts +2 -0
  56. package/dist/__tests__/dawn/mesh-update-dawn.test.d.ts.map +1 -0
  57. package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts +2 -0
  58. package/dist/__tests__/dawn/upload-texture.dawn.test.d.ts.map +1 -0
  59. package/dist/__tests__/draw-published.d.ts +2 -2
  60. package/dist/__tests__/draw-published.d.ts.map +1 -1
  61. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
  62. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
  63. package/dist/__tests__/errors.unit.test.d.ts +2 -0
  64. package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
  65. package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts +2 -0
  66. package/dist/__tests__/extract-skin-mismatch.unit.test.d.ts.map +1 -0
  67. package/dist/__tests__/fetch-binary-decompress.test.d.ts +2 -0
  68. package/dist/__tests__/fetch-binary-decompress.test.d.ts.map +1 -0
  69. package/dist/__tests__/four-path-compression.integration.test.d.ts +2 -0
  70. package/dist/__tests__/four-path-compression.integration.test.d.ts.map +1 -0
  71. package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts +2 -0
  72. package/dist/__tests__/fullscreen-feature-host.dawn.test.d.ts.map +1 -0
  73. package/dist/__tests__/guid-for-asset.unit.test.d.ts +2 -0
  74. package/dist/__tests__/guid-for-asset.unit.test.d.ts.map +1 -0
  75. package/dist/__tests__/handle-migration.test.d.ts +2 -0
  76. package/dist/__tests__/handle-migration.test.d.ts.map +1 -0
  77. package/dist/__tests__/helpers/register-runtime-components.d.ts.map +1 -1
  78. package/dist/__tests__/host-custom-kind-contract.test-d.d.ts +2 -0
  79. package/dist/__tests__/host-custom-kind-contract.test-d.d.ts.map +1 -0
  80. package/dist/__tests__/host-custom-kind-contract.test.d.ts +2 -0
  81. package/dist/__tests__/host-custom-kind-contract.test.d.ts.map +1 -0
  82. package/dist/__tests__/inspect-name.test.d.ts +2 -0
  83. package/dist/__tests__/inspect-name.test.d.ts.map +1 -0
  84. package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts +2 -0
  85. package/dist/__tests__/instantiate-mount-deferred-wiring.test.d.ts.map +1 -0
  86. package/dist/__tests__/loadbyguid-payload.unit.test.d.ts +2 -0
  87. package/dist/__tests__/loadbyguid-payload.unit.test.d.ts.map +1 -0
  88. package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts +2 -0
  89. package/dist/__tests__/loadbyguid-texture-intern.unit.test.d.ts.map +1 -0
  90. package/dist/__tests__/loader-extract-graceful.test.d.ts +2 -0
  91. package/dist/__tests__/loader-extract-graceful.test.d.ts.map +1 -0
  92. package/dist/__tests__/loader-name-survival.test.d.ts +2 -0
  93. package/dist/__tests__/loader-name-survival.test.d.ts.map +1 -0
  94. package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
  95. package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
  96. package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts +2 -0
  97. package/dist/__tests__/mesh-bin-skin-roundtrip.unit.test.d.ts.map +1 -0
  98. package/dist/__tests__/mesh-bin.test.d.ts +2 -0
  99. package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
  100. package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts +2 -0
  101. package/dist/__tests__/mesh-loader-skin-contract.unit.test.d.ts.map +1 -0
  102. package/dist/__tests__/new-kind-refs-contract.test.d.ts +2 -0
  103. package/dist/__tests__/new-kind-refs-contract.test.d.ts.map +1 -0
  104. package/dist/__tests__/package-promotion.test.d.ts +2 -0
  105. package/dist/__tests__/package-promotion.test.d.ts.map +1 -0
  106. package/dist/__tests__/package-rename.test.d.ts +2 -0
  107. package/dist/__tests__/package-rename.test.d.ts.map +1 -0
  108. package/dist/__tests__/recover-error-exhaustive.test-d.d.ts +1 -1
  109. package/dist/__tests__/recover-error-exhaustive.test-d.d.ts.map +1 -1
  110. package/dist/__tests__/render-error-exhaustive.test-d.d.ts +1 -1
  111. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
  112. package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts +10 -0
  113. package/dist/__tests__/render-feature-prepared-graphics.fixture.d.ts.map +1 -0
  114. package/dist/__tests__/render-system-extract.unit.test.d.ts +2 -0
  115. package/dist/__tests__/render-system-extract.unit.test.d.ts.map +1 -0
  116. package/dist/__tests__/renderer-test-utils.d.ts +10 -0
  117. package/dist/__tests__/renderer-test-utils.d.ts.map +1 -0
  118. package/dist/__tests__/renderer.test-d.d.ts +2 -0
  119. package/dist/__tests__/renderer.test-d.d.ts.map +1 -0
  120. package/dist/__tests__/resolve-asset-handle-err.test.d.ts +2 -0
  121. package/dist/__tests__/resolve-asset-handle-err.test.d.ts.map +1 -0
  122. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
  123. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
  124. package/dist/__tests__/resolveName-fallback.test.d.ts +2 -0
  125. package/dist/__tests__/resolveName-fallback.test.d.ts.map +1 -0
  126. package/dist/__tests__/resolveName-null-package.test.d.ts +2 -0
  127. package/dist/__tests__/resolveName-null-package.test.d.ts.map +1 -0
  128. package/dist/__tests__/resolveName-unified-entry.test.d.ts +2 -0
  129. package/dist/__tests__/resolveName-unified-entry.test.d.ts.map +1 -0
  130. package/dist/__tests__/resolveName.test.d.ts +2 -0
  131. package/dist/__tests__/resolveName.test.d.ts.map +1 -0
  132. package/dist/__tests__/roots-to-scene-asset.test.d.ts +1 -1
  133. package/dist/__tests__/roots-to-scene-asset.test.d.ts.map +1 -1
  134. package/dist/__tests__/roundtrip-primitive.test.d.ts +2 -0
  135. package/dist/__tests__/roundtrip-primitive.test.d.ts.map +1 -0
  136. package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts +2 -0
  137. package/dist/__tests__/scene-bare-guid-refs-instantiate.test.d.ts.map +1 -0
  138. package/dist/__tests__/scene-instance-authoring.integration.test.d.ts +2 -0
  139. package/dist/__tests__/scene-instance-authoring.integration.test.d.ts.map +1 -0
  140. package/dist/__tests__/scene-mount-fixed-point.test.d.ts +2 -0
  141. package/dist/__tests__/scene-mount-fixed-point.test.d.ts.map +1 -0
  142. package/dist/__tests__/scene-mount-roundtrip.test.d.ts +2 -0
  143. package/dist/__tests__/scene-mount-roundtrip.test.d.ts.map +1 -0
  144. package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts +2 -0
  145. package/dist/__tests__/scene-mount-wrapper-roundtrip.test.d.ts.map +1 -0
  146. package/dist/__tests__/scene-sugar-instantiate.test.d.ts +2 -0
  147. package/dist/__tests__/scene-sugar-instantiate.test.d.ts.map +1 -0
  148. package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts +2 -0
  149. package/dist/__tests__/serialize-mounts-roundtrip.test.d.ts.map +1 -0
  150. package/dist/__tests__/serialize-scene-asset.test.d.ts +1 -1
  151. package/dist/__tests__/serialize-scene-asset.test.d.ts.map +1 -1
  152. package/dist/__tests__/template-game-default-api-contract.test.d.ts +2 -0
  153. package/dist/__tests__/template-game-default-api-contract.test.d.ts.map +1 -0
  154. package/dist/__tests__/tileset-asset-validate.test.d.ts +2 -0
  155. package/dist/__tests__/tileset-asset-validate.test.d.ts.map +1 -0
  156. package/dist/__tests__/transcode-fallback.unit.test.d.ts +2 -0
  157. package/dist/__tests__/transcode-fallback.unit.test.d.ts.map +1 -0
  158. package/dist/__tests__/transient-runtime-behavior.test.d.ts +2 -0
  159. package/dist/__tests__/transient-runtime-behavior.test.d.ts.map +1 -0
  160. package/dist/__tests__/video-loader-consumption.test.d.ts +2 -0
  161. package/dist/__tests__/video-loader-consumption.test.d.ts.map +1 -0
  162. package/dist/__tests__/video-loader-smoke.dawn.test.d.ts +2 -0
  163. package/dist/__tests__/video-loader-smoke.dawn.test.d.ts.map +1 -0
  164. package/dist/backend-selection.d.ts +13 -0
  165. package/dist/backend-selection.d.ts.map +1 -0
  166. package/dist/collect-scene-asset.d.ts +3 -3
  167. package/dist/collect-scene-asset.d.ts.map +1 -1
  168. package/dist/createRenderer.d.ts +14 -2
  169. package/dist/createRenderer.d.ts.map +1 -1
  170. package/dist/errors/environment.d.ts +1 -1
  171. package/dist/errors/environment.d.ts.map +1 -1
  172. package/dist/index.d.ts +19 -81
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.mjs +105 -108
  175. package/dist/index.mjs.map +1 -1
  176. package/dist/renderer-host.d.ts +9 -0
  177. package/dist/renderer-host.d.ts.map +1 -0
  178. package/dist/renderer-host.mjs +62 -0
  179. package/dist/renderer-host.mjs.map +1 -0
  180. package/dist/scene-instances/index.d.ts +2 -0
  181. package/dist/scene-instances/index.d.ts.map +1 -0
  182. package/dist/scene-utils/mount-override-fold.d.ts +1 -2
  183. package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
  184. package/dist/systems/sprite-animation-tick.d.ts +1 -1
  185. package/dist/systems/sprite-animation-tick.d.ts.map +1 -1
  186. package/dist/systems/transparent-sort.d.ts +1 -2
  187. package/dist/systems/transparent-sort.d.ts.map +1 -1
  188. package/dist/tilemap-register-validate.d.ts.map +1 -1
  189. package/package.json +34 -33
  190. package/src/__tests__/active-camera.unit.test.ts +19 -9
  191. package/src/__tests__/animation-graph-collect-roundtrip.test.ts +213 -0
  192. package/src/__tests__/animation-graph-serialize.unit.test.ts +171 -0
  193. package/src/__tests__/append-injection.test.ts +1 -1
  194. package/src/__tests__/asset-brand-and-name-retirement.test.ts +102 -0
  195. package/src/__tests__/asset-error-exhaustive.test-d.ts +62 -0
  196. package/src/__tests__/asset-registry-guid-reverse.test.ts +575 -0
  197. package/src/__tests__/asset-registry-hdr-equirect.test.ts +78 -0
  198. package/src/__tests__/asset-registry-mesh-pack-payload.dawn.test.ts +232 -0
  199. package/src/__tests__/asset-registry-mounts.test.ts +806 -0
  200. package/src/__tests__/asset-registry.browser.test.ts +81 -0
  201. package/src/__tests__/asset-registry.dawn.test.ts +94 -0
  202. package/src/__tests__/asset-registry.listCatalog.test.ts +170 -0
  203. package/src/__tests__/asset-registry.mesh-bin.test.ts +316 -0
  204. package/src/__tests__/asset-registry.pack-cache.test.ts +969 -0
  205. package/src/__tests__/asset-registry.raw-container-failfast.test.ts +225 -0
  206. package/src/__tests__/asset-registry.recursive.spec.ts +1397 -0
  207. package/src/__tests__/asset-registry.unit.test.ts +1030 -0
  208. package/src/__tests__/asset.unit.test.ts +7107 -0
  209. package/src/__tests__/audio-load-by-guid.unit.test.ts +126 -0
  210. package/src/__tests__/backend-selection.integration.test.ts +11 -0
  211. package/src/__tests__/basis-catalog-dispatch.integration.test.ts +320 -0
  212. package/src/__tests__/bind-group-cache-keying.test.ts +4 -4
  213. package/src/__tests__/block-upload-math.unit.test.ts +2 -2
  214. package/src/__tests__/builtin-asset-registry.unit.test.ts +112 -0
  215. package/src/__tests__/builtin-shader-register-e2e.test.ts +1 -1
  216. package/src/__tests__/cache-false-eviction.test.ts +2 -1
  217. package/src/__tests__/cache-isolation-matrix.test.ts +2 -1
  218. package/src/__tests__/camera-clear-snapshot-equivalence.unit.test.ts +5 -6
  219. package/src/__tests__/clamp-to-last.e2e.browser.test.ts +29 -19
  220. package/src/__tests__/collect-scene-asset.test.ts +49 -52
  221. package/src/__tests__/collect-scene-mount-collapse.test.ts +391 -0
  222. package/src/__tests__/collect-subtree.unit.test.ts +1 -2
  223. package/src/__tests__/collect-transient-children-absent.test.ts +24 -10
  224. package/src/__tests__/collect-transient-roundtrip.test.ts +44 -20
  225. package/src/__tests__/collect-transient-skip.test.ts +24 -13
  226. package/src/__tests__/components.test-d.ts +1 -6
  227. package/src/__tests__/components.unit.test.ts +20 -19
  228. package/src/__tests__/composite-skybox-cross-world.browser.test.ts +59 -27
  229. package/src/__tests__/compressed-texture-physical-extent.integration.test.ts +139 -0
  230. package/src/__tests__/create-renderer-assembly.integration.test.ts +42 -29
  231. package/src/__tests__/createRenderer-dispose.test.ts +35 -402
  232. package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +25 -30
  233. package/src/__tests__/createRenderer.unit.test.ts +1 -1
  234. package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +27 -63
  235. package/src/__tests__/dawn/ibl-precompute-readback.dawn.test.ts +56 -47
  236. package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +49 -29
  237. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +26 -15
  238. package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +13 -14
  239. package/src/__tests__/dawn/material-alpha.dawn.test.ts +23 -31
  240. package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +0 -18
  241. package/src/__tests__/dawn/{mesh-update.dawn.test.ts → mesh-update-dawn.test.ts} +42 -134
  242. package/src/__tests__/dawn/mipmap-srgb-linear.dawn.test.ts +14 -7
  243. package/src/__tests__/dawn/per-entity-material-texture-isolation.dawn.test.ts +16 -28
  244. package/src/__tests__/dawn/sprite-nineslice-mesh-bind.dawn.test.ts +23 -39
  245. package/src/__tests__/dawn/upload-texture.dawn.test.ts +198 -0
  246. package/src/__tests__/dawn/user-handle-mesh-render.dawn.test.ts +12 -14
  247. package/src/__tests__/dawn/user-mesh-upload.dawn.test.ts +27 -22
  248. package/src/__tests__/dawn/zero-manifest-mesh-fail.dawn.test.ts +25 -28
  249. package/src/__tests__/dawn/zero-manifest-ready.dawn.test.ts +10 -11
  250. package/src/__tests__/demo-sortscope-migration.unit.test.ts +5 -5
  251. package/src/__tests__/derive-bgl-integration.test.ts +34 -44
  252. package/src/__tests__/derive-render-data-skin.unit.test.ts +1 -1
  253. package/src/__tests__/draw-published.ts +11 -4
  254. package/src/__tests__/dynamic-texture-store.unit.test.ts +190 -0
  255. package/src/__tests__/equirect-bc6h.integration.test.ts +144 -21
  256. package/src/__tests__/errors.unit.test.ts +1203 -0
  257. package/src/__tests__/extract-frames-graceful-degradation.test.ts +3 -7
  258. package/src/__tests__/extract-frames-merge.test.ts +35 -22
  259. package/src/__tests__/extract-record-no-hardcoded-texture-fields.test.ts +5 -16
  260. package/src/__tests__/extract-skin-mismatch.unit.test.ts +322 -0
  261. package/src/__tests__/fetch-binary-decompress.test.ts +356 -0
  262. package/src/__tests__/four-path-compression.integration.test.ts +395 -0
  263. package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +1 -1
  264. package/src/__tests__/fullscreen-feature-host.dawn.test.ts +80 -0
  265. package/src/__tests__/fullscreen-post-bgl-depth.test.ts +4 -2
  266. package/src/__tests__/fullscreen-post-depth-params-arity.test.ts +3 -3
  267. package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +39 -23
  268. package/src/__tests__/fullscreen-post-process-pass.unit.test.ts +4 -2
  269. package/src/__tests__/fxaa-pixel-diff.dawn.test.ts +28 -17
  270. package/src/__tests__/fxaa-validation-and-resize.dawn.test.ts +29 -18
  271. package/src/__tests__/fxaa-zero-overhead.dawn.test.ts +16 -12
  272. package/src/__tests__/geometry.unit.test.ts +525 -159
  273. package/src/__tests__/glyph-tilemap-worldid.test.ts +1 -1
  274. package/src/__tests__/gpu-resource-cow-survivor.test.ts +6 -4
  275. package/src/__tests__/guid-for-asset.unit.test.ts +102 -0
  276. package/src/__tests__/handle-migration.test.ts +136 -0
  277. package/src/__tests__/helpers/register-runtime-components.ts +4 -2
  278. package/src/__tests__/helpers/register-scene-components.ts +1 -1
  279. package/src/__tests__/host-custom-kind-contract.test-d.ts +47 -0
  280. package/src/__tests__/host-custom-kind-contract.test.ts +578 -0
  281. package/src/__tests__/inspect-name.test.ts +139 -0
  282. package/src/__tests__/instances.test-d.ts +1 -1
  283. package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +112 -0
  284. package/src/__tests__/light-casters-9-light.browser.test.ts +73 -76
  285. package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +7 -10
  286. package/src/__tests__/light-snapshot.test-d.ts +2 -2
  287. package/src/__tests__/lights-vec-negative.unit.test.ts +1 -1
  288. package/src/__tests__/lights.unit.test.ts +16 -500
  289. package/src/__tests__/loadbyguid-payload.unit.test.ts +117 -0
  290. package/src/__tests__/loadbyguid-texture-intern.unit.test.ts +213 -0
  291. package/src/__tests__/loader-extract-graceful.test.ts +146 -0
  292. package/src/__tests__/loader-name-survival.test.ts +213 -0
  293. package/src/__tests__/m3-vec-negative.unit.test.ts +3 -2
  294. package/src/__tests__/material-texture-uv-scale.unit.test.ts +101 -31
  295. package/src/__tests__/materials.unit.test.ts +10 -27
  296. package/src/__tests__/mesh-bin-contract.test.ts +28 -0
  297. package/src/__tests__/mesh-bin-skin-roundtrip.unit.test.ts +149 -0
  298. package/src/__tests__/mesh-bin.test.ts +38 -0
  299. package/src/__tests__/mesh-loader-skin-contract.unit.test.ts +120 -0
  300. package/src/__tests__/mesh-ssbo-grow.unit.test.ts +3 -3
  301. package/src/__tests__/mip-gate.unit.test.ts +1 -1
  302. package/src/__tests__/msaa-sprite-pixel-diff.dawn.test.ts +29 -68
  303. package/src/__tests__/new-kind-refs-contract.test.ts +132 -0
  304. package/src/__tests__/node-shims.d.ts +4 -6
  305. package/src/__tests__/package-promotion.test.ts +88 -0
  306. package/src/__tests__/package-rename.test.ts +103 -0
  307. package/src/__tests__/pass-selector.unit.test.ts +2 -2
  308. package/src/__tests__/pbr-pipeline.unit.test.ts +14 -21
  309. package/src/__tests__/pipeline-builder-msaa.unit.test.ts +6 -9
  310. package/src/__tests__/pipeline-cache-keying.unit.test.ts +6 -9
  311. package/src/__tests__/pipeline-spec.test.ts +16 -177
  312. package/src/__tests__/pipeline.unit.test.ts +154 -1693
  313. package/src/__tests__/point-light-shadow.dawn.test.ts +33 -42
  314. package/src/__tests__/point-light-shadow.unit.test.ts +19 -60
  315. package/src/__tests__/post-process-error-codes.unit.test.ts +4 -1
  316. package/src/__tests__/post-process-params-component.unit.test.ts +1 -1
  317. package/src/__tests__/post-process-params-extract.unit.test.ts +2 -5
  318. package/src/__tests__/postprocess-camera-consistency.test.ts +4 -7
  319. package/src/__tests__/profiler-assembly.integration.test.ts +1 -1
  320. package/src/__tests__/propagate-transforms.dawn.test.ts +12 -11
  321. package/src/__tests__/record-worldid-isolation.test.ts +7 -29
  322. package/src/__tests__/recover-error-exhaustive.test-d.ts +1 -1
  323. package/src/__tests__/render-error-exhaustive.test-d.ts +81 -71
  324. package/src/__tests__/render-feature-assembly.test-d.ts +20 -9
  325. package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +8 -6
  326. package/src/__tests__/render-feature-prepared-graphics.browser.test.ts +60 -558
  327. package/src/__tests__/render-feature-prepared-graphics.dawn.test.ts +57 -544
  328. package/src/__tests__/render-feature-prepared-graphics.fixture.ts +104 -0
  329. package/src/__tests__/render-feature-prepared-graphics.integration.test.ts +40 -248
  330. package/src/__tests__/render-feature-prepared-graphics.regression.integration.test.ts +83 -340
  331. package/src/__tests__/render-feature-recover.integration.test.ts +9 -49
  332. package/src/__tests__/render-feature-runtime-surface.test-d.ts +17 -20
  333. package/src/__tests__/render-schema-consumer.integration.test.ts +20 -13
  334. package/src/__tests__/render-schema-owner-boundary.unit.test.ts +2 -6
  335. package/src/__tests__/render-system-cross-world-overlay-queue.test.ts +5 -5
  336. package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +16 -31
  337. package/src/__tests__/render-system-extract.test.ts +36 -51
  338. package/src/__tests__/render-system-extract.unit.test.ts +360 -0
  339. package/src/__tests__/render-system-fold-bucket.unit.test.ts +3 -5
  340. package/src/__tests__/render-system-fold-fallback.unit.test.ts +6 -3
  341. package/src/__tests__/render-system-fold-material.unit.test.ts +3 -5
  342. package/src/__tests__/render-system-fold-metrics.unit.test.ts +5 -5
  343. package/src/__tests__/render-system-fold-modegate.unit.test.ts +6 -6
  344. package/src/__tests__/render-system-fold.dawn.test.ts +7 -5
  345. package/src/__tests__/render-system-mega.test.ts +292 -190
  346. package/src/__tests__/render-system-owner-split-contract.test.ts +10 -32
  347. package/src/__tests__/render-system-record-multi-material-textureview.test.ts +59 -45
  348. package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +23 -29
  349. package/src/__tests__/render-system-record.test.ts +398 -235
  350. package/src/__tests__/render-system-record.unit.test.ts +25 -26
  351. package/src/__tests__/render-system-skin-bg.unit.test.ts +8 -12
  352. package/src/__tests__/renderer-consumer.test-d.ts +16 -3
  353. package/src/__tests__/renderer-health.unit.test.ts +26 -916
  354. package/src/__tests__/renderer-lifecycle.integration.test.ts +19 -6
  355. package/src/__tests__/renderer-recover.unit.test.ts +49 -1069
  356. package/src/__tests__/renderer-result.test-d.ts +19 -9
  357. package/src/__tests__/renderer-surface.unit.test.ts +3 -5
  358. package/src/__tests__/renderer-test-utils.ts +56 -0
  359. package/src/__tests__/renderer.test-d.ts +59 -0
  360. package/src/__tests__/resolve-asset-handle-err.test.ts +111 -0
  361. package/src/__tests__/resolve-asset-handle.unit.test.ts +64 -0
  362. package/src/__tests__/resolve-depth-only-view.test.ts +1 -1
  363. package/src/__tests__/resolveName-fallback.test.ts +64 -0
  364. package/src/__tests__/resolveName-null-package.test.ts +38 -0
  365. package/src/__tests__/resolveName-unified-entry.test.ts +65 -0
  366. package/src/__tests__/resolveName.test.ts +64 -0
  367. package/src/__tests__/rhi-null-command-flow.unit.test.ts +43 -261
  368. package/src/__tests__/rhi-null-renderer-lifecycle.unit.test.ts +41 -169
  369. package/src/__tests__/roots-to-scene-asset.test.ts +199 -93
  370. package/src/__tests__/roundtrip-primitive.test.ts +351 -0
  371. package/src/__tests__/scene-bare-guid-refs-instantiate.test.ts +123 -0
  372. package/src/__tests__/scene-equirect-roundtrip.test.ts +103 -16
  373. package/src/__tests__/scene-instance-authoring.integration.test.ts +8 -0
  374. package/src/__tests__/scene-instance-bridge.integration.test.ts +1 -1
  375. package/src/__tests__/scene-instance-component.test.ts +1 -1
  376. package/src/__tests__/scene-mount-fixed-point.test.ts +257 -0
  377. package/src/__tests__/scene-mount-roundtrip.test.ts +987 -0
  378. package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +202 -0
  379. package/src/__tests__/scene-sugar-instantiate.test.ts +324 -0
  380. package/src/__tests__/serialize-mounts-roundtrip.test.ts +533 -0
  381. package/src/__tests__/serialize-scene-asset.test.ts +219 -22
  382. package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -3
  383. package/src/__tests__/shadow-csm-cascade-loadop.test.ts +37 -17
  384. package/src/__tests__/shadow-csm-component.test.ts +3 -5
  385. package/src/__tests__/shadow-csm-extract.test.ts +2 -100
  386. package/src/__tests__/shadow-csm-orthoz.test.ts +2 -16
  387. package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +19 -14
  388. package/src/__tests__/shadow-csm-tile-consistency.test.ts +32 -26
  389. package/src/__tests__/shadow-csm-ubo.dawn.test.ts +8 -49
  390. package/src/__tests__/shadow-csm-ubo.test.ts +3 -25
  391. package/src/__tests__/shadow-csm-viewport.browser.test.ts +1 -1
  392. package/src/__tests__/shadow-fields-observable.dawn.test.ts +67 -56
  393. package/src/__tests__/shadow-m1.dawn.test.ts +8 -9
  394. package/src/__tests__/shadow-mesh-ssbo-order.test.ts +11 -27
  395. package/src/__tests__/skin-errors.unit.test.ts +1 -1
  396. package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +7 -5
  397. package/src/__tests__/skylight-lazy-projection.test.ts +36 -15
  398. package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +5 -5
  399. package/src/__tests__/sprite-cullmode-flip.dawn.test.ts +47 -64
  400. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +12 -11
  401. package/src/__tests__/sprite-lit-extract-branch.test.ts +1 -1
  402. package/src/__tests__/sprite-lit-pipeline-cache-key.test.ts +1 -1
  403. package/src/__tests__/ssao-bgl.test.ts +16 -9
  404. package/src/__tests__/ssao-buffers.test.ts +2 -2
  405. package/src/__tests__/ssao-data.test.ts +2 -2
  406. package/src/__tests__/ssao-errors.test.ts +1 -1
  407. package/src/__tests__/ssao-passes.test.ts +64 -392
  408. package/src/__tests__/ssao-pipelines.test.ts +1 -1
  409. package/src/__tests__/ssao-uniform-layout.test.ts +8 -6
  410. package/src/__tests__/stencil-outline-pixel.dawn.test.ts +25 -17
  411. package/src/__tests__/swap-chain-format.unit.test.ts +5 -7
  412. package/src/__tests__/systems.unit.test.ts +536 -1326
  413. package/src/__tests__/template-game-default-api-contract.test.ts +362 -0
  414. package/src/__tests__/text.unit.test.ts +56 -33
  415. package/src/__tests__/tilemap-childed-despawn.test.ts +8 -14
  416. package/src/__tests__/tilemap-chunk-extract.overhang-bounds.test.ts +8 -12
  417. package/src/__tests__/tilemap-chunk-extract.propagate-order.test.ts +2 -1
  418. package/src/__tests__/tilemap-chunk-extract.test.ts +13 -17
  419. package/src/__tests__/tilemap-dirty-rebuild.test.ts +7 -11
  420. package/src/__tests__/tilemap-material-cache.test.ts +8 -13
  421. package/src/__tests__/tilemap-multi-atlas.test.ts +12 -32
  422. package/src/__tests__/tilemap-owner-boundary.unit.test.ts +5 -2
  423. package/src/__tests__/tilemap-region-pack.test.ts +8 -12
  424. package/src/__tests__/tilemap-spawn-flip-pivot.test.ts +8 -12
  425. package/src/__tests__/tilemap-uv-inset.test.ts +11 -14
  426. package/src/__tests__/tilemap-validate.test.ts +1 -1
  427. package/src/__tests__/tileset-asset-validate.test.ts +638 -0
  428. package/src/__tests__/transcode-fallback.unit.test.ts +207 -0
  429. package/src/__tests__/transform-hierarchy-pixel-diff.dawn.test.ts +36 -28
  430. package/src/__tests__/transient-fields-collect.test.ts +28 -21
  431. package/src/__tests__/transient-runtime-behavior.test.ts +227 -0
  432. package/src/__tests__/transparent-sort-tilemap-sprite-interleave.test.ts +4 -4
  433. package/src/__tests__/variant-selection.unit.test.ts +1 -1
  434. package/src/__tests__/video-extract-bindgroup.dawn.test.ts +89 -136
  435. package/src/__tests__/video-loader-consumption.test.ts +86 -0
  436. package/src/__tests__/video-loader-smoke.dawn.test.ts +60 -0
  437. package/src/__tests__/view-ubo-layout.browser.test.ts +0 -22
  438. package/src/__tests__/world-entity-key.test.ts +1 -1
  439. package/src/__tests__/world-scene-despawn.test.ts +1 -1
  440. package/src/__tests__/world-scene-instantiate.test.ts +2 -36
  441. package/src/__tests__/world-scene-nested.test.ts +1 -1
  442. package/src/__tests__/world-scene-override.test.ts +1 -1
  443. package/src/backend-selection.ts +69 -0
  444. package/src/collect-scene-asset.ts +28 -23
  445. package/src/components/__tests__/camera.test-d.ts +1 -1
  446. package/src/components/__tests__/camera.test.ts +1 -1
  447. package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
  448. package/src/components/__tests__/tile-layer.test-d.ts +1 -1
  449. package/src/components/__tests__/tile-layer.unit.test.ts +2 -2
  450. package/src/createRenderer.ts +24 -36
  451. package/src/errors/environment.ts +1 -1
  452. package/src/index.ts +47 -113
  453. package/src/renderer-host.ts +33 -0
  454. package/src/scene-instances/__tests__/post-spawn-resolve-joints.unit.test.ts +1 -1
  455. package/src/scene-instances/index.ts +4 -0
  456. package/src/scene-utils/mount-override-fold.ts +3 -7
  457. package/src/systems/__tests__/transparent-sort.bench.ts +3 -3
  458. package/src/systems/sprite-animation-tick.ts +1 -1
  459. package/src/systems/transparent-sort.ts +38 -38
  460. package/src/tilemap-register-validate.ts +4 -4
  461. package/dist/__tests__/bind-group-cache-binding.test.d.ts +0 -2
  462. package/dist/__tests__/bind-group-cache-binding.test.d.ts.map +0 -1
  463. package/dist/__tests__/bind-group-cache-frame.test.d.ts +0 -2
  464. package/dist/__tests__/bind-group-cache-frame.test.d.ts.map +0 -1
  465. package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts +0 -2
  466. package/dist/__tests__/dawn/mesh-update.dawn.test.d.ts.map +0 -1
  467. package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts +0 -2
  468. package/dist/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.d.ts.map +0 -1
  469. package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts +0 -2
  470. package/dist/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.d.ts.map +0 -1
  471. package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts +0 -2
  472. package/dist/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.d.ts.map +0 -1
  473. package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts +0 -2
  474. package/dist/__tests__/dispatch-fullscreen-pass-params.unit.test.d.ts.map +0 -1
  475. package/dist/__tests__/fog-producer-manifest.d.ts +0 -131
  476. package/dist/__tests__/fog-producer-manifest.d.ts.map +0 -1
  477. package/dist/__tests__/fog-producer-pixel-probe.d.ts +0 -42
  478. package/dist/__tests__/fog-producer-pixel-probe.d.ts.map +0 -1
  479. package/dist/__tests__/fog-producer-pixel.browser.test.d.ts +0 -2
  480. package/dist/__tests__/fog-producer-pixel.browser.test.d.ts.map +0 -1
  481. package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts +0 -2
  482. package/dist/__tests__/fog-producer-pixel.dawn.test.d.ts.map +0 -1
  483. package/dist/__tests__/fog-producer-real-fixture.d.ts +0 -5
  484. package/dist/__tests__/fog-producer-real-fixture.d.ts.map +0 -1
  485. package/dist/__tests__/fog-producer-vfx-effect.d.ts +0 -3
  486. package/dist/__tests__/fog-producer-vfx-effect.d.ts.map +0 -1
  487. package/dist/__tests__/fog-scene-asset.integration.test.d.ts +0 -2
  488. package/dist/__tests__/fog-scene-asset.integration.test.d.ts.map +0 -1
  489. package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts +0 -2
  490. package/dist/__tests__/gpu-resource-store-cubemap-status.test.d.ts.map +0 -1
  491. package/dist/__tests__/gpu-resource.test.d.ts +0 -2
  492. package/dist/__tests__/gpu-resource.test.d.ts.map +0 -1
  493. package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts +0 -2
  494. package/dist/__tests__/hdrp-bgl-slots.browser.test.d.ts.map +0 -1
  495. package/dist/__tests__/hdrp-buffers.unit.test.d.ts +0 -2
  496. package/dist/__tests__/hdrp-buffers.unit.test.d.ts.map +0 -1
  497. package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts +0 -2
  498. package/dist/__tests__/hdrp-execute-filter.unit.test.d.ts.map +0 -1
  499. package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts +0 -2
  500. package/dist/__tests__/hdrp-gbuffer-attachments.browser.test.d.ts.map +0 -1
  501. package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts +0 -2
  502. package/dist/__tests__/hdrp-pipeline-asset-type.test-d.d.ts.map +0 -1
  503. package/dist/__tests__/hdrp-ssao-boundary.test.d.ts +0 -2
  504. package/dist/__tests__/hdrp-ssao-boundary.test.d.ts.map +0 -1
  505. package/dist/__tests__/hdrp-ssao-falsification.test.d.ts +0 -2
  506. package/dist/__tests__/hdrp-ssao-falsification.test.d.ts.map +0 -1
  507. package/dist/__tests__/helpers/primitive-world.d.ts +0 -8
  508. package/dist/__tests__/helpers/primitive-world.d.ts.map +0 -1
  509. package/dist/__tests__/helpers/render-asset-port.d.ts +0 -15
  510. package/dist/__tests__/helpers/render-asset-port.d.ts.map +0 -1
  511. package/dist/__tests__/helpers/scene-asset-port.d.ts +0 -9
  512. package/dist/__tests__/helpers/scene-asset-port.d.ts.map +0 -1
  513. package/dist/__tests__/material-derived-coordinates.integration.test.d.ts +0 -2
  514. package/dist/__tests__/material-derived-coordinates.integration.test.d.ts.map +0 -1
  515. package/dist/__tests__/pipeline-layout-selection.test.d.ts +0 -2
  516. package/dist/__tests__/pipeline-layout-selection.test.d.ts.map +0 -1
  517. package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts +0 -2
  518. package/dist/__tests__/post-process-reads-narrowing.test-d.d.ts.map +0 -1
  519. package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts +0 -2
  520. package/dist/__tests__/post-process-register-roundtrip.dawn.test.d.ts.map +0 -1
  521. package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts +0 -2
  522. package/dist/__tests__/render-feature-zero-feature.browser.test.d.ts.map +0 -1
  523. package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts +0 -2
  524. package/dist/__tests__/render-pipeline-trivial.dawn.test.d.ts.map +0 -1
  525. package/dist/__tests__/shadow-csm-caster-membership.test.d.ts +0 -2
  526. package/dist/__tests__/shadow-csm-caster-membership.test.d.ts.map +0 -1
  527. package/dist/__tests__/shadow-csm-pssm.test.d.ts +0 -2
  528. package/dist/__tests__/shadow-csm-pssm.test.d.ts.map +0 -1
  529. package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts +0 -2
  530. package/dist/__tests__/shadow-csm-temporal.browser.test.d.ts.map +0 -1
  531. package/dist/__tests__/shadow-csm-viewport.test.d.ts +0 -2
  532. package/dist/__tests__/shadow-csm-viewport.test.d.ts.map +0 -1
  533. package/dist/__tests__/shadow-m2.dawn.test.d.ts +0 -2
  534. package/dist/__tests__/shadow-m2.dawn.test.d.ts.map +0 -1
  535. package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts +0 -2
  536. package/dist/__tests__/shadow-m3-calibrate-run.dawn.test.d.ts.map +0 -1
  537. package/dist/__tests__/shadow-m3-calibrate.d.ts +0 -83
  538. package/dist/__tests__/shadow-m3-calibrate.d.ts.map +0 -1
  539. package/dist/__tests__/shadow-m3.dawn.test.d.ts +0 -2
  540. package/dist/__tests__/shadow-m3.dawn.test.d.ts.map +0 -1
  541. package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts +0 -2
  542. package/dist/__tests__/shadow-probe-control-flow.dawn.test.d.ts.map +0 -1
  543. package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts +0 -2
  544. package/dist/__tests__/shadow-quality-metrics.dawn.test.d.ts.map +0 -1
  545. package/dist/__tests__/temporal-off-path.unit.test.d.ts +0 -2
  546. package/dist/__tests__/temporal-off-path.unit.test.d.ts.map +0 -1
  547. package/dist/__tests__/video-cache-regression.unit.test.d.ts +0 -2
  548. package/dist/__tests__/video-cache-regression.unit.test.d.ts.map +0 -1
  549. package/dist/asset-kind.d.ts +0 -4
  550. package/dist/asset-kind.d.ts.map +0 -1
  551. package/dist/errors/sprite-animation.d.ts +0 -3
  552. package/dist/errors/sprite-animation.d.ts.map +0 -1
  553. package/dist/scene-asset-port.d.ts +0 -17
  554. package/dist/scene-asset-port.d.ts.map +0 -1
  555. package/dist/scene-collect-errors.d.ts +0 -22
  556. package/dist/scene-collect-errors.d.ts.map +0 -1
  557. package/dist/shadow-m3-calibrate.d.ts +0 -40
  558. package/dist/shadow-m3-calibrate.d.ts.map +0 -1
  559. package/src/__tests__/bind-group-cache-binding.test.ts +0 -2513
  560. package/src/__tests__/bind-group-cache-frame.test.ts +0 -1511
  561. package/src/__tests__/dispatch-fullscreen-pass-composite-swapchain.dawn.test.ts +0 -204
  562. package/src/__tests__/dispatch-fullscreen-pass-custom-reads.dawn.test.ts +0 -297
  563. package/src/__tests__/dispatch-fullscreen-pass-depth-composite.dawn.test.ts +0 -271
  564. package/src/__tests__/dispatch-fullscreen-pass-params.unit.test.ts +0 -321
  565. package/src/__tests__/fog-producer-manifest.ts +0 -162
  566. package/src/__tests__/fog-producer-pixel-probe.ts +0 -263
  567. package/src/__tests__/fog-producer-pixel.browser.test.ts +0 -26
  568. package/src/__tests__/fog-producer-pixel.dawn.test.ts +0 -55
  569. package/src/__tests__/fog-producer-real-fixture.ts +0 -805
  570. package/src/__tests__/fog-producer-vfx-effect.ts +0 -272
  571. package/src/__tests__/fog-scene-asset.integration.test.ts +0 -123
  572. package/src/__tests__/gpu-resource-store-cubemap-status.test.ts +0 -207
  573. package/src/__tests__/gpu-resource.test.ts +0 -1206
  574. package/src/__tests__/hdrp-bgl-slots.browser.test.ts +0 -50
  575. package/src/__tests__/hdrp-buffers.unit.test.ts +0 -70
  576. package/src/__tests__/hdrp-execute-filter.unit.test.ts +0 -200
  577. package/src/__tests__/hdrp-gbuffer-attachments.browser.test.ts +0 -64
  578. package/src/__tests__/hdrp-pipeline-asset-type.test-d.ts +0 -139
  579. package/src/__tests__/hdrp-ssao-boundary.test.ts +0 -237
  580. package/src/__tests__/hdrp-ssao-falsification.test.ts +0 -289
  581. package/src/__tests__/helpers/primitive-world.ts +0 -91
  582. package/src/__tests__/helpers/render-asset-port.ts +0 -65
  583. package/src/__tests__/helpers/scene-asset-port.ts +0 -23
  584. package/src/__tests__/material-derived-coordinates.integration.test.ts +0 -100
  585. package/src/__tests__/pipeline-layout-selection.test.ts +0 -298
  586. package/src/__tests__/post-process-reads-narrowing.test-d.ts +0 -52
  587. package/src/__tests__/post-process-register-roundtrip.dawn.test.ts +0 -230
  588. package/src/__tests__/render-feature-zero-feature.browser.test.ts +0 -152
  589. package/src/__tests__/render-pipeline-trivial.dawn.test.ts +0 -525
  590. package/src/__tests__/shadow-csm-caster-membership.test.ts +0 -232
  591. package/src/__tests__/shadow-csm-pssm.test.ts +0 -142
  592. package/src/__tests__/shadow-csm-temporal.browser.test.ts +0 -145
  593. package/src/__tests__/shadow-csm-viewport.test.ts +0 -74
  594. package/src/__tests__/shadow-m2.dawn.test.ts +0 -394
  595. package/src/__tests__/shadow-m3-calibrate-run.dawn.test.ts +0 -248
  596. package/src/__tests__/shadow-m3-calibrate.ts +0 -203
  597. package/src/__tests__/shadow-m3.dawn.test.ts +0 -288
  598. package/src/__tests__/shadow-probe-control-flow.dawn.test.ts +0 -264
  599. package/src/__tests__/shadow-quality-metrics.dawn.test.ts +0 -324
  600. package/src/__tests__/temporal-off-path.unit.test.ts +0 -69
  601. package/src/__tests__/video-cache-regression.unit.test.ts +0 -186
  602. package/src/asset-kind.ts +0 -7
  603. package/src/errors/sprite-animation.ts +0 -5
  604. package/src/scene-asset-port.ts +0 -32
  605. package/src/scene-collect-errors.ts +0 -30
  606. package/src/shadow-m3-calibrate.ts +0 -171
@@ -1,1075 +1,55 @@
1
- // @forgeax/engine-runtime — recover() single idempotent rebuild tests (M3)
2
- //
3
- // feat-20260622-s5-device-surface-self-heal-recover M3. Covers:
4
- // w10 recover() injectable factory success rebuild (A-AC-06)
5
- // w11 recover() adapter-unavailable failure code (A-AC-07)
6
- // w12 — recover() device-unavailable failure code (A-AC-07)
7
- // w13 — recover() idempotent + alive sentinel + no-background-state (A-AC-08)
8
- // w14 recover() preserves CPU POD cache (A-AC-12)
9
- // w19 — RecoverErrorCode exhaustiveness grep gate + switch (A-AC-09)
10
- //
11
- // recover() reuses tryCreateWebGPURenderer / gpuStore.destroyAll /
12
- // ensureContextConfigured (plan-strategy D-1). A2/T1 precedent: the device
13
- // factory is injectable via the mocked RHI backend pack so the rebuild path
14
- // runs without a real GPU (A-OOS-7 honest downgrade; real wgpu device-lost
15
- // end-to-end stays a local-only gate).
16
-
17
- import { readFileSync } from 'node:fs';
18
- import { fileURLToPath } from 'node:url';
19
- import type { RenderError, RenderFeature } from '@forgeax/engine-render';
20
- import type { HealthSnapshot } from '@forgeax/engine-render/internal';
21
- import { RecoverError, type RecoverErrorCode } from '@forgeax/engine-render/internal';
22
- import type { Result, RhiInstance } from '@forgeax/engine-rhi';
23
- import { ok, RenderQueue } from '@forgeax/engine-types';
24
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
25
- import type { SubmissionPlan } from '../../../render/src/gpu-driven/batch-topology';
26
- import { executeCpuMultiViewVisibility } from '../../../render/src/gpu-driven/view-gpu';
27
- import { classifyGpuDrivenView } from '../../../render/src/gpu-driven/view-reference';
28
- import { GpuScene } from '../../../render/src/gpu-scene';
29
- import {
30
- RenderSceneProjection,
31
- type RenderSceneProjectionSlot,
32
- } from '../../../render/src/render-scene-projection';
33
-
34
- // ── Controllable mock state (module-level so vi.mock factories can read) ──────
35
- //
36
- // recover() re-invokes pack.rhi.requestAdapter -> adapter.requestDevice. These
37
- // flags let a single test flip the next acquisition to fail on the adapter or
38
- // device step, exercising the two add-only RecoverErrorCode members.
39
-
40
- const recoverMock = {
41
- adapterReturnsNull: false,
42
- deviceThrows: false,
43
- deviceRefusalRemaining: 0,
44
- requestAdapterCalls: 0,
45
- requestDeviceCalls: 0,
46
- requestDeviceOptions: [] as unknown[],
47
- shaderModuleLabels: [] as string[],
48
- deviceRequestGate: undefined as Promise<void> | undefined,
49
- };
50
-
51
- function resetRecoverMock(): void {
52
- recoverMock.adapterReturnsNull = false;
53
- recoverMock.deviceThrows = false;
54
- recoverMock.deviceRefusalRemaining = 0;
55
- recoverMock.requestAdapterCalls = 0;
56
- recoverMock.requestDeviceCalls = 0;
57
- recoverMock.requestDeviceOptions = [];
58
- recoverMock.shaderModuleLabels = [];
59
- recoverMock.deviceRequestGate = undefined;
60
- }
61
-
62
- let testDeviceLostResolve: ((info: unknown) => void) | null = null;
63
- const testDeviceLostResolvers: Array<(info: unknown) => void> = [];
64
-
65
- function makeFakeRhiDevice(): Record<string, unknown> {
66
- let resolveLost!: (info: unknown) => void;
67
- const lost = new Promise<unknown>((res) => {
68
- resolveLost = res;
69
- });
70
- testDeviceLostResolvers.push(resolveLost);
71
- testDeviceLostResolve = resolveLost;
72
- return {
73
- __brand: 'RhiDevice',
74
- lost,
75
- features: new Set<string>(),
76
- limits: {} as Record<string, number>,
77
- caps: {},
78
- queue: {
79
- submit: () => ({ ok: true, value: undefined }),
80
- writeBuffer: () => ({ ok: true, value: undefined }),
81
- writeTexture: () => ({ ok: true, value: undefined }),
82
- onSubmittedWorkDone: async () => undefined,
83
- },
84
- createBuffer: () => ({ ok: true, value: { __brand: 'Buffer' } }),
85
- createTexture: () => ({ ok: true, value: { __brand: 'Texture' } }),
86
- destroyTexture: () => ({ ok: true, value: undefined }),
87
- destroyBuffer: () => ({ ok: true, value: undefined }),
88
- createBindGroupLayout: () => ({ ok: true, value: { __brand: 'BindGroupLayout' } }),
89
- createBindGroup: () => ({ ok: true, value: { __brand: 'BindGroup' } }),
90
- createPipelineLayout: () => ({ ok: true, value: { __brand: 'PipelineLayout' } }),
91
- createRenderPipeline: () => ({
92
- ok: true,
93
- value: {
94
- __brand: 'RenderPipeline',
95
- getBindGroupLayout: () => ({ __brand: 'BindGroupLayout' }),
96
- },
97
- }),
98
- createComputePipeline: () => ({
99
- ok: true,
100
- value: {
101
- __brand: 'ComputePipeline',
102
- getBindGroupLayout: () => ({ __brand: 'BindGroupLayout' }),
103
- },
104
- }),
105
- createSampler: () => ({ ok: true, value: { __brand: 'Sampler' } }),
106
- createShaderModule: () => ({
107
- ok: true,
108
- value: { __brand: 'ShaderModule', getCompilationInfo: async () => ({ messages: [] }) },
109
- }),
110
- createTextureView: () => ({ ok: true, value: { __brand: 'TextureView' } }),
111
- createCommandEncoder: () => ({
112
- ok: true,
113
- value: {
114
- beginRenderPass: () => ({
115
- setPipeline: () => undefined,
116
- setBindGroup: () => undefined,
117
- setVertexBuffer: () => undefined,
118
- setIndexBuffer: () => undefined,
119
- setViewport: () => undefined,
120
- setScissorRect: () => undefined,
121
- draw: () => undefined,
122
- drawIndexed: () => undefined,
123
- end: () => undefined,
124
- }),
125
- beginComputePass: () => ({
126
- setPipeline: () => undefined,
127
- setBindGroup: () => undefined,
128
- dispatchWorkgroups: () => undefined,
129
- end: () => undefined,
130
- }),
131
- copyTextureToTexture: () => undefined,
132
- copyBufferToBuffer: () => undefined,
133
- finish: () => ({ ok: true, value: { __brand: 'CommandBuffer' } }),
134
- },
135
- }),
136
- };
137
- }
138
-
139
- function makeExplicitRhi(): RhiInstance {
140
- const adapter = {
141
- features: new Set<string>(['texture-compression-bc']),
142
- limits: {},
143
- requestDevice: async (options?: unknown) => {
144
- recoverMock.requestDeviceCalls += 1;
145
- recoverMock.requestDeviceOptions.push(options);
146
- await recoverMock.deviceRequestGate;
147
- if (recoverMock.deviceRefusalRemaining > 0) {
148
- recoverMock.deviceRefusalRemaining -= 1;
149
- throw new Error('mock transient requestDevice refusal (M40 red reproducer)');
150
- }
151
- if (recoverMock.deviceThrows) {
152
- throw new Error('mock requestDevice failure (recover-device-unavailable path)');
153
- }
154
- return { ok: true, value: makeFakeRhiDevice() };
155
- },
156
- };
157
- return {
158
- requestAdapter: async () => {
159
- recoverMock.requestAdapterCalls += 1;
160
- if (recoverMock.adapterReturnsNull) {
161
- return {
162
- ok: false,
163
- error: { code: 'adapter-unavailable', message: 'mock adapter null' },
164
- };
165
- }
166
- return { ok: true, value: adapter };
167
- },
168
- acquireCanvasContext: () => ({
169
- ok: true,
170
- value: {
171
- configure: () => ({ ok: true, value: undefined }),
172
- unconfigure: () => ({ ok: true, value: undefined }),
173
- getCurrentTexture: () => ({ ok: true, value: { __brand: 'TextureView' } }),
174
- },
175
- }),
176
- createShaderModule: async (_device: unknown, descriptor?: { readonly label?: string }) => {
177
- if (descriptor?.label !== undefined) recoverMock.shaderModuleLabels.push(descriptor.label);
178
- return { ok: true, value: { __brand: 'ShaderModule' } };
179
- },
180
- } as unknown as RhiInstance;
181
- }
182
-
183
- function makeMockCanvas(): HTMLCanvasElement {
184
- return {
185
- width: 800,
186
- height: 600,
187
- getContext(kind: string): unknown {
188
- if (kind === 'webgpu') {
189
- return {
190
- __mockTag: 'webgpu-canvas-context',
191
- configure: () => undefined,
192
- unconfigure: () => undefined,
193
- getCurrentTexture: () => ({ createView: () => ({}) }),
194
- };
195
- }
196
- if (kind === 'webgl2') {
197
- return {
198
- __mockTag: 'webgl2',
199
- getExtension: () => null,
200
- getParameter: () => 1,
201
- isContextLost: () => false,
202
- };
203
- }
204
- return null;
205
- },
206
- addEventListener: () => undefined,
207
- removeEventListener: () => undefined,
208
- } as Partial<HTMLCanvasElement> as HTMLCanvasElement;
209
- }
210
-
211
- vi.mock('@forgeax/engine-rhi-webgpu', async () => {
212
- const actualRhi =
213
- await vi.importActual<typeof import('@forgeax/engine-rhi')>('@forgeax/engine-rhi');
214
- const fakeAdapter = {
215
- features: new Set<string>(),
216
- limits: {} as Readonly<Record<string, number>>,
217
- async requestDevice() {
218
- recoverMock.requestDeviceCalls += 1;
219
- if (recoverMock.deviceThrows) {
220
- throw new Error('mock requestDevice failure (recover-device-unavailable path)');
221
- }
222
- return actualRhi.ok(makeFakeRhiDevice());
223
- },
224
- };
225
- return {
226
- rhi: {
227
- requestAdapter: async () => {
228
- recoverMock.requestAdapterCalls += 1;
229
- if (recoverMock.adapterReturnsNull) {
230
- return actualRhi.err({
231
- code: 'adapter-unavailable',
232
- message: 'mock adapter null (recover-adapter-unavailable path)',
233
- });
234
- }
235
- return actualRhi.ok(fakeAdapter);
236
- },
237
- acquireCanvasContext: (_canvas: unknown) =>
238
- actualRhi.ok({
239
- configure: () => actualRhi.ok(undefined),
240
- unconfigure: () => actualRhi.ok(undefined),
241
- getCurrentTexture: () => actualRhi.ok({ __brand: 'TextureView' }),
242
- }),
243
- },
244
- createShaderModule: async () => actualRhi.ok({ __brand: 'ShaderModule' } as unknown as object),
245
- };
246
- });
247
-
248
- vi.mock('@forgeax/engine-rhi-wgpu', async () => {
249
- const actualRhi =
250
- await vi.importActual<typeof import('@forgeax/engine-rhi')>('@forgeax/engine-rhi');
251
- const fakeAdapter = {
252
- features: new Set<string>(),
253
- limits: {} as Readonly<Record<string, number>>,
254
- async requestDevice() {
255
- recoverMock.requestDeviceCalls += 1;
256
- if (recoverMock.deviceThrows) {
257
- throw new Error('mock requestDevice failure (recover-device-unavailable path)');
258
- }
259
- return actualRhi.ok(makeFakeRhiDevice());
260
- },
261
- };
262
- return {
263
- rhi: {
264
- requestAdapter: async () => {
265
- recoverMock.requestAdapterCalls += 1;
266
- if (recoverMock.adapterReturnsNull) {
267
- return actualRhi.err({
268
- code: 'adapter-unavailable',
269
- message: 'mock adapter null (recover-adapter-unavailable path)',
270
- });
271
- }
272
- return actualRhi.ok(fakeAdapter);
273
- },
274
- acquireCanvasContext: (_canvas: unknown) =>
275
- actualRhi.ok({
276
- configure: () => actualRhi.ok(undefined),
277
- unconfigure: () => actualRhi.ok(undefined),
278
- getCurrentTexture: () => actualRhi.ok({ __brand: 'TextureView' }),
279
- }),
280
- },
281
- ensureReady: async () => undefined,
282
- };
283
- });
284
-
285
- function makeStubGPU() {
286
- return {
287
- requestAdapter: async () => ({
288
- features: new Set<string>(),
289
- limits: {},
290
- requestDevice: async () => makeFakeRhiDevice(),
291
- }),
292
- getPreferredCanvasFormat: () => 'bgra8unorm',
293
- };
294
- }
295
-
296
- // Shader manifest as a data URL (no HTTP). Carries the engine SSOT triple
297
- // (pbr/unlit/tonemap content markers) buildReadyWebGPU requires, mirroring the
298
- // production manifest shape — bind-group-cache-binding.test.ts:163. The recover()
299
- // rebuild re-runs buildReadyWebGPU against an already-populated ShaderRegistry,
300
- // so the manifest must satisfy the triple check on the rebuild path (a single-
301
- // entry manifest only passes the boot path because the registry is still empty
302
- // when boot's buildReadyWebGPU walks it).
303
- const RECOVER_TEST_MANIFEST_URL = (() => {
304
- const materialShaderStub = (identifier: string) => ({
305
- identifier,
306
- sourcePath: `${identifier}.wgsl`,
307
- composedWgsl: '/* stub */',
308
- paramSchema: '[]',
309
- variants: [],
310
- });
311
- const manifest = {
312
- schemaVersion: '1.0.0',
313
- entries: [
314
- { hash: 'pbr00000', wgsl: '/* pbr stub - calls f_schlick( */', glsl: '', bindings: '' },
315
- { hash: 'unlit000', wgsl: '/* unlit stub */', glsl: '', bindings: '' },
316
- {
317
- hash: 'tonemap0',
318
- wgsl: '/* tonemap stub - struct TonemapParams { exposure: f32 }; */',
319
- glsl: '',
320
- bindings: '',
321
- },
322
- ],
323
- materialShaders: [
324
- materialShaderStub('forgeax::default-standard-pbr'),
325
- materialShaderStub('forgeax::default-unlit'),
326
- materialShaderStub('forgeax::late-recovery-test'),
327
- ],
328
- };
329
- return `data:application/json,${encodeURIComponent(JSON.stringify(manifest))}`;
330
- })();
331
-
332
- type TestRenderer = {
333
- device: object;
334
- recover: () => Promise<Result<void, RecoverError>>;
335
- health: () => HealthSnapshot;
336
- store: { destroyAll: () => void };
337
- assets: { inspect?: () => unknown };
338
- onHealthChange: (listener: (snapshot: HealthSnapshot) => void) => () => void;
339
- ready: Promise<unknown>;
340
- installRenderFeature: (feature: RenderFeature<unknown>) => Promise<Result<void, RenderError>>;
341
- postProcess: {
342
- register(id: string, entry: { source: string }): () => void;
343
- };
344
- dispose: () => void;
345
- };
346
-
347
- async function makeRenderer(): Promise<TestRenderer> {
348
- const canvas = makeMockCanvas();
349
- const { createRenderer } = await import('../../../render/src/renderer/renderer-factory');
350
- const renderer = await createRenderer(
351
- canvas,
352
- { rhi: makeExplicitRhi() },
353
- { shaderManifestUrl: RECOVER_TEST_MANIFEST_URL },
354
- );
355
- return renderer as unknown as TestRenderer;
1
+ import { World } from '@forgeax/engine-ecs';
2
+ import type { FrameReceipt } from '@forgeax/engine-render';
3
+ import { rhi } from '@forgeax/engine-rhi-null';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { requireRenderer } from './renderer-test-utils';
6
+
7
+ function canvas(): HTMLCanvasElement {
8
+ return { width: 64, height: 64, getContext: () => null } as unknown as HTMLCanvasElement;
356
9
  }
357
10
 
358
- // Drive the renderer into device-lost via the mock device.lost resolver.
359
- // Uses reason 'unknown' (genuine unrecoverable loss) -- reason 'destroyed' is
360
- // INTENTIONAL teardown (device.destroy() on dispose / tab recycle / test-pool
361
- // reuse) and per the requirements A constraint must NOT flip health to
362
- // 'device-lost' (that is what the dedicated test below asserts).
363
- async function driveDeviceLost(renderer: TestRenderer): Promise<void> {
364
- if (!testDeviceLostResolve) throw new Error('device.lost resolver not set up');
365
- testDeviceLostResolve({ reason: 'unknown', message: 'recover-test device lost' });
366
- await vi.waitFor(() => {
367
- expect(renderer.health().reason).toBe('device-lost');
368
- });
369
- }
370
-
371
- // Each rebuild test boots a full renderer (createRenderer + ready) and then
372
- // runs recover()'s second full assembly. Two heavy createRenderer boots in one
373
- // test occasionally exceed vitest's 5 s default under max-parallel suite load
374
- // (same cold-boot contention the dawn project mitigates with a raised budget);
375
- // a per-test 30 s budget removes the flake without affecting the stable path.
376
- const RECOVER_BOOT_TIMEOUT_MS = 30000;
377
-
378
- describe('recover() single idempotent rebuild (M3)', () => {
379
- beforeEach(() => {
380
- resetRecoverMock();
381
- testDeviceLostResolve = null;
382
- testDeviceLostResolvers.length = 0;
383
- vi.stubGlobal('navigator', { ...navigator, gpu: makeStubGPU() });
384
- });
385
-
386
- afterEach(() => {
387
- vi.unstubAllGlobals();
388
- testDeviceLostResolve = null;
389
- testDeviceLostResolvers.length = 0;
390
- });
391
-
392
- // ── w10: injectable factory success rebuild (A-AC-06) ──────────────────────
393
-
394
- it(
395
- 'w10: device-lost -> recover() rebuilds via injected factory -> ok + alive',
396
- async () => {
397
- const renderer = await makeRenderer();
398
- await renderer.ready;
399
- const destroyAllSpy = vi.spyOn(renderer.store, 'destroyAll');
400
-
401
- await driveDeviceLost(renderer);
402
- expect(renderer.health().reason).toBe('device-lost');
403
-
404
- const adapterCallsBefore = recoverMock.requestAdapterCalls;
405
- const deviceCallsBefore = recoverMock.requestDeviceCalls;
406
-
407
- const result = await renderer.recover();
408
-
409
- expect(result.ok).toBe(true);
410
- expect(renderer.health().reason).toBe('alive');
411
- // destroyAll cleared GPU resources before rebuild.
412
- expect(destroyAllSpy).toHaveBeenCalled();
413
- // tryCreateWebGPURenderer re-invoked requestAdapter + requestDevice.
414
- expect(recoverMock.requestAdapterCalls).toBeGreaterThan(adapterCallsBefore);
415
- expect(recoverMock.requestDeviceCalls).toBeGreaterThan(deviceCallsBefore);
416
- // pipeline rebuild completes; subsequent ready resolves ok.
417
- await renderer.ready;
418
- },
419
- RECOVER_BOOT_TIMEOUT_MS,
420
- );
421
-
422
- it(
423
- 'fences a late loss notification from the replaced device after recovery',
424
- async () => {
425
- const renderer = await makeRenderer();
426
- await renderer.ready;
427
- const staleDeviceLostResolve = testDeviceLostResolvers[0];
428
- if (staleDeviceLostResolve === undefined) throw new Error('initial device resolver missing');
429
-
430
- await driveDeviceLost(renderer);
431
- expect((await renderer.recover()).ok).toBe(true);
432
- expect(renderer.health().reason).toBe('alive');
433
-
434
- staleDeviceLostResolve({ reason: 'unknown', message: 'late loss from replaced device' });
435
- await Promise.resolve();
436
- await Promise.resolve();
437
-
438
- expect(renderer.health().reason).toBe('alive');
439
- renderer.dispose();
440
- },
441
- RECOVER_BOOT_TIMEOUT_MS,
442
- );
443
-
444
- it(
445
- 'keeps unregister authoritative while recovery is acquiring the replacement device',
446
- async () => {
447
- const renderer = await makeRenderer();
448
- await renderer.ready;
449
- const unregister = renderer.postProcess.register('test::recover-race', {
450
- source: 'fn main() {}',
451
- });
452
- await driveDeviceLost(renderer);
453
-
454
- let releaseDevice!: () => void;
455
- recoverMock.deviceRequestGate = new Promise<void>((resolve) => {
456
- releaseDevice = resolve;
457
- });
458
- const recovering = renderer.recover();
459
- await vi.waitFor(() => expect(recoverMock.requestDeviceCalls).toBeGreaterThan(1));
460
- unregister();
461
- releaseDevice();
462
-
463
- expect((await recovering).ok).toBe(true);
464
- const unregisterReplacement = renderer.postProcess.register('test::recover-race', {
465
- source: 'fn main() {}',
466
- });
467
- unregisterReplacement();
468
- renderer.dispose();
469
- },
470
- RECOVER_BOOT_TIMEOUT_MS,
471
- );
472
-
473
- it(
474
- 'recovery preserves adapter compression features when requesting the replacement device',
475
- async () => {
476
- const renderer = await makeRenderer();
477
- await renderer.ready;
478
- await driveDeviceLost(renderer);
479
-
480
- const result = await renderer.recover();
481
- expect(result.ok).toBe(true);
482
- expect(recoverMock.requestDeviceOptions.length).toBeGreaterThanOrEqual(2);
483
- for (const options of recoverMock.requestDeviceOptions) {
484
- expect(options).toEqual({ requiredFeatures: ['texture-compression-bc'] });
485
- }
486
- },
487
- RECOVER_BOOT_TIMEOUT_MS,
488
- );
489
-
490
- it(
491
- 'recovery prewarms shader modules for a feature installed after boot',
492
- async () => {
493
- const renderer = await makeRenderer();
494
- await renderer.ready;
495
- const feature: RenderFeature<unknown> = {
496
- identity: 'synthetic.late-recovery-shader',
497
- requiredMaterialShaders: ['forgeax::late-recovery-test'],
498
- extract: () => ok({}),
499
- prepare: () => ok(undefined),
500
- contribute: () => ok(undefined),
501
- };
502
-
503
- const installed = await renderer.installRenderFeature(feature);
504
- expect(installed.ok).toBe(true);
505
- const installCount = recoverMock.shaderModuleLabels.filter(
506
- (label) => label === 'module-forgeax::late-recovery-test',
507
- ).length;
508
- expect(installCount).toBe(1);
509
-
510
- await driveDeviceLost(renderer);
511
- expect((await renderer.recover()).ok).toBe(true);
512
- const recoveryCount = recoverMock.shaderModuleLabels.filter(
513
- (label) => label === 'module-forgeax::late-recovery-test',
514
- ).length;
515
- expect(recoveryCount).toBeGreaterThan(installCount);
516
- renderer.dispose();
517
- },
518
- RECOVER_BOOT_TIMEOUT_MS,
519
- );
520
-
521
- // ── w10b: reason 'destroyed' is intentional teardown, NOT device-lost ───────
522
- // Regression (PR #495 vitest-browser red): F4 made the device.lost Promise
523
- // actually resolve. On CI chromium the GPUDevice is destroyed between test
524
- // files (reason 'destroyed'); without this gate the health channel flipped to
525
- // 'device-lost', the M2 draw() guard refused the frame, and unrelated browser
526
- // tests failed with "renderer.draw frame N failed". Per the requirements A
527
- // constraint, reason 'destroyed' (host/driver device.destroy()) must leave
528
- // health alive. dawn/unit could not see this -- only the browser path does.
529
- it(
530
- 'w10b: device.lost reason=destroyed does NOT flip health to device-lost (intentional teardown)',
531
- async () => {
532
- const renderer = await makeRenderer();
533
- await renderer.ready;
534
- expect(renderer.health().reason).toBe('alive');
535
-
536
- if (!testDeviceLostResolve) throw new Error('device.lost resolver not set up');
537
- testDeviceLostResolve({ reason: 'destroyed', message: 'device was destroyed (teardown)' });
538
-
539
- // Give the lost Promise's microtask chain a tick to settle, then assert
540
- // health stayed alive (the fanout gated the destroyed reason). Because
541
- // health stays alive, the M2 draw() guard does NOT refuse frames -- which
542
- // is exactly what unblocks the browser tests that regressed in PR #495.
543
- await Promise.resolve();
544
- await Promise.resolve();
545
- expect(renderer.health().reason).toBe('alive');
546
- },
547
- RECOVER_BOOT_TIMEOUT_MS,
548
- );
549
-
550
- // ── w11: adapter-unavailable failure code (A-AC-07) ────────────────────────
551
-
552
- it(
553
- 'w11: recover() with null adapter -> recover-adapter-unavailable, health stays device-lost',
554
- async () => {
555
- const renderer = await makeRenderer();
556
- await renderer.ready;
557
- await driveDeviceLost(renderer);
558
-
559
- recoverMock.adapterReturnsNull = true;
560
- const result = await renderer.recover();
561
-
562
- expect(result.ok).toBe(false);
563
- if (!result.ok) {
564
- expect(result.error.code).toBe('recover-adapter-unavailable');
565
- expect(typeof result.error.expected).toBe('string');
566
- expect(result.error.expected.length).toBeGreaterThan(0);
567
- expect(typeof result.error.hint).toBe('string');
568
- expect(result.error.hint.length).toBeGreaterThan(0);
569
- }
570
- // Failure does not fake the renderer back to alive.
571
- expect(renderer.health().reason).toBe('device-lost');
572
- },
573
- RECOVER_BOOT_TIMEOUT_MS,
574
- );
575
-
576
- it(
577
- 'm39: one adapter refusal leaves the same renderer retryable and publishes a fresh device on retry',
578
- async () => {
579
- const renderer = await makeRenderer();
580
- await renderer.ready;
581
- const originalDevice = renderer.device;
582
- const cpuBefore = renderer.assets.inspect?.();
583
- await driveDeviceLost(renderer);
584
-
585
- const adapterCallsBeforeRefusal = recoverMock.requestAdapterCalls;
586
- const deviceCallsBeforeRefusal = recoverMock.requestDeviceCalls;
587
- recoverMock.adapterReturnsNull = true;
588
- const refusal = await renderer.recover();
589
-
590
- expect(refusal.ok).toBe(false);
591
- if (!refusal.ok) {
592
- expect(refusal.error.code).toBe('recover-adapter-unavailable');
593
- expect(refusal.error.expected).toBe(
594
- 'requestAdapter returned null; driver/GPU may have been reset',
595
- );
596
- expect(refusal.error.hint).toBe(
597
- 'retry recover() after a host-chosen delay; adapter availability is transient',
598
- );
599
- }
600
- expect(renderer.health().reason).toBe('device-lost');
601
- expect(renderer.device).toBe(originalDevice);
602
- expect(recoverMock.requestAdapterCalls).toBe(adapterCallsBeforeRefusal + 1);
603
- expect(recoverMock.requestDeviceCalls).toBe(deviceCallsBeforeRefusal);
604
- if (cpuBefore !== undefined && renderer.assets.inspect !== undefined) {
605
- expect(renderer.assets.inspect()).toEqual(cpuBefore);
606
- }
607
-
608
- recoverMock.adapterReturnsNull = false;
609
- const retry = await renderer.recover();
610
- expect(retry.ok).toBe(true);
611
- expect(renderer.health().reason).toBe('alive');
612
- expect(renderer.device).not.toBe(originalDevice);
613
- if (cpuBefore !== undefined && renderer.assets.inspect !== undefined) {
614
- expect(renderer.assets.inspect()).toEqual(cpuBefore);
615
- }
616
-
617
- const alive = await renderer.recover();
618
- expect(alive.ok).toBe(false);
619
- if (!alive.ok) expect(alive.error.code).toBe('recover-not-needed');
620
- renderer.dispose();
621
- renderer.dispose();
622
- },
623
- RECOVER_BOOT_TIMEOUT_MS,
624
- );
625
-
626
- // ── w12: device-unavailable failure code (A-AC-07) ─────────────────────────
627
-
628
- it(
629
- 'w12: recover() with requestDevice throw -> recover-device-unavailable, health stays device-lost',
630
- async () => {
631
- const renderer = await makeRenderer();
632
- await renderer.ready;
633
- await driveDeviceLost(renderer);
634
-
635
- recoverMock.deviceThrows = true;
636
- const result = await renderer.recover();
637
-
638
- expect(result.ok).toBe(false);
639
- if (!result.ok) {
640
- expect(result.error.code).toBe('recover-device-unavailable');
641
- expect(typeof result.error.expected).toBe('string');
642
- expect(result.error.expected.length).toBeGreaterThan(0);
643
- expect(typeof result.error.hint).toBe('string');
644
- expect(result.error.hint.length).toBeGreaterThan(0);
645
- }
646
- expect(renderer.health().reason).toBe('device-lost');
647
- },
648
- RECOVER_BOOT_TIMEOUT_MS,
649
- );
650
-
651
- it(
652
- 'm40: one requestDevice refusal preserves the same renderer for the next recover',
653
- async () => {
654
- const renderer = await makeRenderer();
655
- await renderer.ready;
656
- const originalDevice = renderer.device;
657
- const originalAssets = renderer.assets;
658
- const cpuBefore = renderer.assets.inspect?.();
659
- const healthTransitions: HealthSnapshot[] = [];
660
- const removeHealthListener = renderer.onHealthChange((snapshot) => {
661
- healthTransitions.push(snapshot);
662
- });
663
- await driveDeviceLost(renderer);
664
-
665
- const adapterCallsBeforeRefusal = recoverMock.requestAdapterCalls;
666
- const deviceCallsBeforeRefusal = recoverMock.requestDeviceCalls;
667
- recoverMock.deviceRefusalRemaining = 1;
668
- const refused = await renderer.recover();
669
-
670
- expect(refused.ok).toBe(false);
671
- if (!refused.ok) {
672
- expect(refused.error.code).toBe('recover-device-unavailable');
673
- expect(refused.error.expected).toBe('requestDevice failed or threw');
674
- expect(refused.error.hint).toBe(
675
- 'retry recover() after a host-chosen delay; device creation is driver-dependent',
676
- );
677
- }
678
- expect(renderer.health().reason).toBe('device-lost');
679
- expect(renderer.device).toBe(originalDevice);
680
- expect(renderer.assets).toBe(originalAssets);
681
- if (cpuBefore !== undefined && renderer.assets.inspect !== undefined) {
682
- expect(renderer.assets.inspect()).toEqual(cpuBefore);
683
- }
684
- expect(recoverMock.requestAdapterCalls).toBe(adapterCallsBeforeRefusal + 1);
685
- expect(recoverMock.requestDeviceCalls).toBe(deviceCallsBeforeRefusal + 1);
686
- const transitionCountAfterRefusal = healthTransitions.length;
687
- await Promise.resolve();
688
- await Promise.resolve();
689
- expect(healthTransitions).toHaveLength(transitionCountAfterRefusal);
690
-
691
- const recovered = await renderer.recover();
692
- expect(recovered.ok).toBe(true);
693
- expect(renderer.health().reason).toBe('alive');
694
- expect(renderer.device).not.toBe(originalDevice);
695
- expect(renderer.assets).toBe(originalAssets);
696
- expect(recoverMock.requestAdapterCalls).toBe(adapterCallsBeforeRefusal + 2);
697
- expect(recoverMock.requestDeviceCalls).toBe(deviceCallsBeforeRefusal + 2);
698
- const third = await renderer.recover();
699
- expect(third.ok).toBe(false);
700
- if (!third.ok) expect(third.error.code).toBe('recover-not-needed');
701
- removeHealthListener();
702
- renderer.dispose();
703
- renderer.dispose();
704
- },
705
- RECOVER_BOOT_TIMEOUT_MS,
706
- );
707
-
708
- // ── w13: idempotent + alive sentinel + no background state (A-AC-08) ────────
709
-
710
- it(
711
- 'w13(a): recover() on alive renderer returns recover-not-needed',
712
- async () => {
713
- const renderer = await makeRenderer();
714
- await renderer.ready;
715
- expect(renderer.health().reason).toBe('alive');
716
-
717
- const result = await renderer.recover();
718
- expect(result.ok).toBe(false);
719
- if (!result.ok) {
720
- expect(result.error.code).toBe('recover-not-needed');
721
- }
722
- },
723
- RECOVER_BOOT_TIMEOUT_MS,
724
- );
725
-
726
- it(
727
- 'w13(b): after a successful recover, second recover returns recover-not-needed',
728
- async () => {
729
- const renderer = await makeRenderer();
730
- await renderer.ready;
731
- await driveDeviceLost(renderer);
732
-
733
- const first = await renderer.recover();
734
- expect(first.ok).toBe(true);
735
- expect(renderer.health().reason).toBe('alive');
736
-
737
- // Idempotent: a second recover() in the now-alive state is a no-op signal.
738
- const second = await renderer.recover();
739
- expect(second.ok).toBe(false);
740
- if (!second.ok) {
741
- expect(second.error.code).toBe('recover-not-needed');
742
- }
743
- },
744
- RECOVER_BOOT_TIMEOUT_MS,
745
- );
746
-
747
- it(
748
- 'w13(c): concurrent recover calls share one device rebuild',
749
- async () => {
750
- const renderer = await makeRenderer();
751
- await renderer.ready;
752
- await driveDeviceLost(renderer);
753
- const adapterCallsBefore = recoverMock.requestAdapterCalls;
754
-
755
- const first = renderer.recover();
756
- const second = renderer.recover();
757
- expect(second).toBe(first);
758
- expect((await first).ok).toBe(true);
759
- expect(recoverMock.requestAdapterCalls).toBe(adapterCallsBefore + 1);
760
- },
761
- RECOVER_BOOT_TIMEOUT_MS,
762
- );
763
-
764
- it(
765
- 'w13(d): dispose wins while recovery is acquiring a replacement device',
766
- async () => {
767
- const renderer = await makeRenderer();
768
- await renderer.ready;
769
- await driveDeviceLost(renderer);
770
-
771
- let releaseDevice!: () => void;
772
- recoverMock.deviceRequestGate = new Promise<void>((resolve) => {
773
- releaseDevice = resolve;
774
- });
775
- const recovering = renderer.recover();
776
- await vi.waitFor(() => expect(recoverMock.requestDeviceCalls).toBeGreaterThan(1));
777
- renderer.dispose();
778
- releaseDevice();
779
-
780
- const result = await recovering;
781
- expect(result.ok).toBe(false);
782
- if (!result.ok) expect(result.error.code).toBe('recover-not-needed');
783
- expect(renderer.health().reason).not.toBe('alive');
784
- },
785
- RECOVER_BOOT_TIMEOUT_MS,
786
- );
787
-
788
- it('w13(e): recover() function body has no timer or retry loop', () => {
789
- // Hard acceptanceCheck (PD5): the compressed-discipline mandate forbids any
790
- // background state machine, retry counter, or timer in the recover path.
791
- // Source-text assertion over createRenderer.ts isolates the recover()
792
- // method body and greps for the forbidden constructs.
793
- const src = readFileSync(
794
- fileURLToPath(new URL('../../../render/src/renderer/renderer-factory.ts', import.meta.url)),
795
- 'utf8',
796
- );
797
- const startIdx = src.indexOf('recover(): Promise<Result<void, RecoverError>>');
798
- expect(startIdx).toBeGreaterThan(-1);
799
- // The onHealthChange method declaration immediately follows recover() in
800
- // the renderer facade object; bound the slice there.
801
- const endIdx = src.indexOf('onHealthChange(', startIdx);
802
- expect(endIdx).toBeGreaterThan(startIdx);
803
- const body = src.slice(startIdx, endIdx);
804
- expect(body).not.toMatch(/setTimeout/);
805
- expect(body).not.toMatch(/setInterval/);
806
- expect(body).not.toMatch(/retryCount/);
807
- expect(body).not.toMatch(/maxRetries/);
808
- expect(body).not.toMatch(/maxLetries/);
809
- });
810
-
811
- // ── w14: preserves CPU POD cache (A-AC-12) ─────────────────────────────────
812
-
813
- it(
814
- 'w14: recover() preserves AssetRegistry CPU POD entries; only GPU resources cleared',
815
- async () => {
816
- const renderer = await makeRenderer();
817
- await renderer.ready;
818
-
819
- // Inspect the AssetRegistry catalog entry count before recover. The
820
- // AssetRegistry survives recover (CPU POD cache); only the GpuResourceStore
821
- // is destroyed + the device rebuilt.
822
- const inspectBefore =
823
- typeof renderer.assets.inspect === 'function' ? renderer.assets.inspect() : null;
824
-
825
- const destroyAllSpy = vi.spyOn(renderer.store, 'destroyAll');
826
- await driveDeviceLost(renderer);
827
- const result = await renderer.recover();
828
- expect(result.ok).toBe(true);
829
-
830
- // GPU resources cleared via destroyAll.
831
- expect(destroyAllSpy).toHaveBeenCalled();
832
-
833
- // AssetRegistry instance identity is preserved (same CPU POD registry).
834
- const inspectAfter =
835
- typeof renderer.assets.inspect === 'function' ? renderer.assets.inspect() : null;
836
- // The catalog entry count is unchanged across recover (CPU POD preserved).
837
- if (inspectBefore !== null && inspectAfter !== null) {
838
- expect(JSON.stringify(inspectAfter)).toBe(JSON.stringify(inspectBefore));
839
- }
840
- },
841
- RECOVER_BOOT_TIMEOUT_MS,
842
- );
843
- });
844
-
845
- // ── w19: RecoverErrorCode exhaustiveness grep gate + switch (A-AC-09) ─────────
846
-
847
- describe('RecoverErrorCode closed union (A-AC-09)', () => {
848
- it('errors.ts RecoverErrorCode definition line declares exactly 4 literal members', () => {
849
- const src = readFileSync(
850
- fileURLToPath(new URL('../../../render/src/errors/recover.ts', import.meta.url)),
851
- 'utf8',
852
- );
853
- const defLine = src.split('\n').find((l) => l.includes('export type RecoverErrorCode'));
854
- expect(defLine).toBeDefined();
855
- const literals = (defLine as string).match(/recover-[a-z-]+/g) ?? [];
856
- expect(literals).toEqual([
857
- 'recover-not-needed',
858
- 'recover-not-implemented',
859
- 'recover-adapter-unavailable',
860
- 'recover-device-unavailable',
861
- ]);
862
- });
863
-
864
- it('exhaustive switch over RecoverErrorCode compiles without default / as cast', () => {
865
- function handle(code: RecoverErrorCode): string {
866
- switch (code) {
867
- case 'recover-not-needed':
868
- return 'not-needed';
869
- case 'recover-not-implemented':
870
- return 'not-implemented';
871
- case 'recover-adapter-unavailable':
872
- return 'adapter-unavailable';
873
- case 'recover-device-unavailable':
874
- return 'device-unavailable';
875
- }
876
- }
877
- expect(handle('recover-adapter-unavailable')).toBe('adapter-unavailable');
878
- expect(handle('recover-device-unavailable')).toBe('device-unavailable');
879
- });
880
-
881
- it('both new codes carry valid .expected + .hint', () => {
882
- for (const code of ['recover-adapter-unavailable', 'recover-device-unavailable'] as const) {
883
- const e = new RecoverError(code);
884
- expect(e.code).toBe(code);
885
- expect(e.expected.length).toBeGreaterThan(0);
886
- expect(e.hint.length).toBeGreaterThan(0);
11
+ const manifest = `data:application/json,${encodeURIComponent(JSON.stringify({ schemaVersion: '1.0.0', entries: [] }))}`;
12
+
13
+ describe('renderer recovery and receipt contract', () => {
14
+ it('returns a structured not-needed result while inspection is healthy', async () => {
15
+ const renderer = await requireRenderer(canvas(), { rhi }, { shaderManifestUrl: manifest });
16
+ expect(renderer.inspect().state).toBe('alive');
17
+ const result = await renderer.recover();
18
+ expect(result.ok).toBe(false);
19
+ if (!result.ok) {
20
+ expect(result.error.code).toBe('renderer-state-invalid');
21
+ expect(result.error.hint.length).toBeGreaterThan(0);
887
22
  }
888
- });
889
- });
890
-
891
- describe('GPU Scene visibility recovery contract (M4)', () => {
892
- const identity = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
893
- const key = {
894
- assetHandle: 17,
895
- drawKind: 'indexed',
896
- first: 0,
897
- count: 3,
898
- baseVertex: 0,
899
- materialSlot: 0,
900
- submeshIndex: 0,
901
- passIndex: 0,
902
- queue: RenderQueue.Geometry,
903
- topology: 'triangle-list',
904
- pipelineClass: 'forgeax::default-unlit',
905
- materialResourceClass: 'empty',
906
- materialClass: 'forgeax::default-unlit',
907
- resourceRevision: 4,
908
- materialRevision: 2,
909
- alphaTest: false,
910
- doubleSided: false,
911
- pbr: false,
912
- shadowCaster: true,
913
- } as unknown as SubmissionPlan['batches'][number]['key'];
914
-
915
- function slot(generation: number): RenderSceneProjectionSlot {
916
- return {
917
- slot: 3,
918
- generation,
919
- snapshot: {
920
- localAabb: new Float32Array([-1, -1, -1, 1, 1, 1]),
921
- transform: { world: identity },
922
- instances: { transforms: identity, instanceCount: 1 },
923
- },
924
- } as unknown as RenderSceneProjectionSlot;
925
- }
926
-
927
- function plan(): SubmissionPlan {
928
- return {
929
- revision: 9,
930
- batches: [
931
- {
932
- batchId: 0,
933
- generation: 1,
934
- key,
935
- candidates: [
936
- { primitiveIndex: 3, generation: 7, drawItemIndex: 0, instanceOrdinal: 0 },
937
- { primitiveIndex: 3, generation: 7, drawItemIndex: 0, instanceOrdinal: 0 },
938
- { primitiveIndex: 3, generation: 6, drawItemIndex: 0, instanceOrdinal: 0 },
939
- ],
940
- visibleBase: 0,
941
- visibleCapacity: 3,
942
- indirectOffset: 0,
943
- },
944
- ],
945
- candidateCount: 3,
946
- visibleCapacity: 3,
947
- };
948
- }
949
-
950
- it('surfaces capacity overflow and rejects stale generations in the indirect CPU oracle', () => {
951
- const result = classifyGpuDrivenView(plan(), [slot(7)], new Float32Array(), new Map([[0, 1]]));
952
-
953
- expect(result.candidateCount).toBe(3);
954
- expect(result.rejectedCount).toBe(1);
955
- expect(result.visibleCount).toBe(1);
956
- expect(result.overflowedBatches).toEqual([0]);
957
- expect(new DataView(result.indirectArgs).getUint32(4, true)).toBe(1);
958
- });
959
-
960
- it('uses the same candidates for CPU cascade fallback and reports caster overflow', () => {
961
- const result = executeCpuMultiViewVisibility({
962
- views: [
963
- {
964
- viewId: 'cascade-0',
965
- kind: 'cascade',
966
- cascadeIndex: 0,
967
- planes: new Float32Array(),
968
- casterCandidateIndices: new Set([10, 11]),
969
- volumeRevision: 6,
970
- spatialIndexRevision: 12,
971
- },
972
- ],
973
- candidates: [
974
- {
975
- stableId: 100,
976
- generation: 7,
977
- candidateIndex: 10,
978
- worldAabb: new Float32Array([-1, -1, -1, 1, 1, 1]),
979
- casterEligible: true,
980
- batchId: 0,
981
- instanceOrdinal: 0,
982
- },
983
- {
984
- stableId: 101,
985
- generation: 7,
986
- candidateIndex: 11,
987
- worldAabb: new Float32Array([-1, -1, -1, 1, 1, 1]),
988
- casterEligible: true,
989
- batchId: 0,
990
- instanceOrdinal: 1,
991
- },
992
- ],
993
- capacityByView: new Map([['cascade-0', 1]]),
994
- capacityByBatch: new Map([[0, 1]]),
995
- });
996
-
997
- expect(result.views[0]).toMatchObject({
998
- stableIds: [100],
999
- generations: [7],
1000
- overflow: true,
1001
- volumeRevision: 6,
1002
- spatialIndexRevision: 12,
23
+ await renderer.dispose();
24
+ });
25
+
26
+ it('observes only a receipt from the current frame generation', async () => {
27
+ const renderer = await requireRenderer(canvas(), { rhi }, { shaderManifestUrl: manifest });
28
+ const world = new World();
29
+ const attached = renderer.attach(world);
30
+ expect(attached.ok).toBe(true);
31
+ if (!attached.ok) return;
32
+ expect(world.update().ok).toBe(true);
33
+ const frame = renderer.draw({
34
+ leases: [attached.value],
35
+ camera: { lease: attached.value },
36
+ environment: { lease: attached.value },
1003
37
  });
1004
- expect(result.cascadeMasks).toEqual([
1005
- { stableId: 100, generation: 7, cascadeMask: 1 },
1006
- { stableId: 101, generation: 7, cascadeMask: 1 },
1007
- ]);
38
+ expect(frame.ok).toBe(true);
39
+ if (!frame.ok) return;
40
+ const receipt: FrameReceipt = frame.value;
41
+ const observed = await renderer.observe(receipt, { include: ['draws', 'bindings'] });
42
+ expect(observed.ok).toBe(true);
43
+ if (observed.ok) expect(observed.value.frameId).toBe(receipt.frameId);
44
+ await renderer.dispose();
45
+ });
46
+
47
+ it('does not recover a disposed renderer or reopen a second frame path', async () => {
48
+ const renderer = await requireRenderer(canvas(), { rhi }, { shaderManifestUrl: manifest });
49
+ renderer.dispose();
50
+ expect(renderer.inspect().state).toBe('disposed');
51
+ const result = await renderer.recover();
52
+ expect(result.ok).toBe(false);
53
+ if (!result.ok) expect(result.error.code).toBe('renderer-state-invalid');
1008
54
  });
1009
-
1010
- it('grows all GPU Scene tables together so recovery can rebuild the same resources', () => {
1011
- const device = {
1012
- caps: { storageBuffer: true },
1013
- createBuffer: ({ size }: { size: number }) => ok({ size }),
1014
- destroyBuffer: () => undefined,
1015
- queue: { writeBuffer: () => ok(undefined) },
1016
- } as unknown as Parameters<typeof GpuScene.create>[0];
1017
- const available = GpuScene.create(device, 1).unwrap();
1018
- expect(available.status).toBe('available');
1019
- if (available.status !== 'available') return;
1020
-
1021
- const projection = new RenderSceneProjection();
1022
- projection.reset([gpuSceneSlot(0).snapshot]);
1023
- const first = available.scene.rebuild(projection).unwrap();
1024
- expect(first.grew).toBe(true);
1025
- expect(available.scene.inspect()).toMatchObject({ capacity: 2, capacityGrows: 1 });
1026
-
1027
- projection.reset([gpuSceneSlot(0).snapshot, gpuSceneSlot(1).snapshot]);
1028
- const second = available.scene.rebuild(projection).unwrap();
1029
- expect(second.grew).toBe(true);
1030
- const inspection = available.scene.inspect();
1031
- expect(inspection.capacity).toBeGreaterThanOrEqual(4);
1032
- expect(inspection.capacityGrows).toBe(2);
1033
- for (const table of Object.values(inspection.tables)) {
1034
- expect(table.capacity).toBe(inspection.capacity);
1035
- }
1036
- available.scene.dispose();
1037
- });
1038
-
1039
- function gpuSceneSlot(slotIndex: number): RenderSceneProjectionSlot {
1040
- const bounds = new Float32Array([-1, -1, -1, 1, 1, 1]);
1041
- const material = { baseColor: [1, 1, 1] };
1042
- return {
1043
- slot: slotIndex,
1044
- generation: 1,
1045
- snapshot: {
1046
- assetHandle: slotIndex + 1,
1047
- transform: { world: identity },
1048
- localAabb: bounds,
1049
- material,
1050
- materials: [material],
1051
- materialBindingSources: [],
1052
- worldId: 0,
1053
- entityKey: slotIndex + 1,
1054
- },
1055
- fact: {
1056
- slot: slotIndex,
1057
- generation: 1,
1058
- worldId: 0,
1059
- entityKey: slotIndex + 1,
1060
- bounds: { status: 'present', localAabb: bounds, worldAabb: bounds },
1061
- mobility: 'dynamic',
1062
- casterEligibility: 'unknown',
1063
- revisions: {
1064
- transform: 1,
1065
- geometry: 1,
1066
- material: 1,
1067
- eligibility: 1,
1068
- space: 1,
1069
- batch: 1,
1070
- resource: 1,
1071
- },
1072
- },
1073
- } as unknown as RenderSceneProjectionSlot;
1074
- }
1075
55
  });