@forgeax/engine-runtime 0.1.4 → 0.1.7

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 +65 -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 +7 -4
  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 +20 -82
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.mjs +107 -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 +608 -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 +24 -9
  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 +1227 -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 +31 -511
  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 +21 -25
  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 +117 -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 +65 -559
  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 +61 -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 +56 -260
  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 +15 -14
  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 +68 -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 +35 -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 +51 -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
@@ -5,29 +5,66 @@
5
5
  // Each original file body sits in its own block-scope so helper names
6
6
  // cannot collide; describe() inside still registers globally with vitest.
7
7
 
8
+ import { HANDLE_CUBE } from '@forgeax/engine-assets-runtime';
8
9
  import type { World as WorldType } from '@forgeax/engine-ecs';
10
+ import { World } from '@forgeax/engine-ecs';
9
11
  import type { Renderer as RendererType } from '@forgeax/engine-render';
10
12
  import {
11
- DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
12
- DEFAULT_UNLIT_PARAM_SCHEMA,
13
- } from '@forgeax/engine-shader';
13
+ Camera,
14
+ DirectionalLight,
15
+ Instances,
16
+ MeshFilter,
17
+ MeshRenderer,
18
+ } from '@forgeax/engine-render';
19
+ import { Transform } from '@forgeax/engine-scene';
14
20
  import type { Handle } from '@forgeax/engine-types';
15
21
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
16
- import { canonicalDefaultMaterialVariants } from './helpers/mock-shader-registry';
17
- import {
18
- TEST_CUBE_HANDLE,
19
- TEST_TRIANGLE_HANDLE,
20
- withPrimitiveMeshes,
21
- } from './helpers/primitive-world';
22
-
23
- function canonicalMaterialParamSchema(identifier: string): string {
24
- if (identifier === 'forgeax::default-standard-pbr') {
25
- return JSON.stringify(DEFAULT_STANDARD_PBR_PARAM_SCHEMA);
26
- }
27
- if (identifier === 'forgeax::default-unlit') {
28
- return JSON.stringify(DEFAULT_UNLIT_PARAM_SCHEMA);
22
+ import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
23
+ import { drawWithOwners } from './renderer-test-utils';
24
+
25
+ type RendererErrorObservation = {
26
+ readonly code: string;
27
+ readonly detail?: unknown;
28
+ readonly hint?: string;
29
+ };
30
+
31
+ type RendererLike = RendererType;
32
+
33
+ function unwrapRendererError(value: unknown): RendererErrorObservation {
34
+ let current = value as RendererErrorObservation;
35
+ while (
36
+ current.detail !== undefined &&
37
+ typeof current.detail === 'object' &&
38
+ current.detail !== null
39
+ ) {
40
+ const cause = (current.detail as { cause?: unknown }).cause;
41
+ if (
42
+ cause === undefined ||
43
+ typeof cause !== 'object' ||
44
+ cause === null ||
45
+ typeof (cause as { code?: unknown }).code !== 'string'
46
+ ) {
47
+ break;
48
+ }
49
+ current = cause as RendererErrorObservation;
29
50
  }
30
- return '[]';
51
+ return current;
52
+ }
53
+
54
+ function subscribeRendererErrors(
55
+ renderer: RendererType,
56
+ listener: (error: RendererErrorObservation) => void,
57
+ ): () => void {
58
+ return renderer.subscribe((event) => {
59
+ if (event.kind === 'error') listener(unwrapRendererError(event.error));
60
+ });
61
+ }
62
+
63
+ function drawPublished(renderer: RendererType, world: WorldType) {
64
+ const attached = renderer.attach(world);
65
+ if (!attached.ok) throw attached.error;
66
+ world.update().unwrap();
67
+ return drawWithOwners(renderer, world);
31
68
  }
32
69
 
33
70
  // ─── from render-system.test.ts ───
@@ -43,11 +80,11 @@ function canonicalMaterialParamSchema(identifier: string): string {
43
80
  //
44
81
  // Charter proposition 4 mapping:
45
82
  // case A (entity missing Transform / MeshRenderer): default
46
- // values used; no onError fired (D-Q7 softening point).
83
+ // values used; no error event fired (D-Q7 softening point).
47
84
  // case B (world has 0 Camera entity): fires
48
85
  // 'render-system-no-camera' + frame skipped.
49
86
  // case C (world has 0 DirectionalLight): unlit fallback
50
- // (intensity = 0); no onError fired (D-Q7 softening point).
87
+ // (intensity = 0); no error event fired (D-Q7 softening point).
51
88
  // case D (world has N>1 Camera / Light): fires
52
89
  // 'render-system-multi-camera' / 'render-system-multi-light' +
53
90
  // uses first archetype hit.
@@ -154,6 +191,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
154
191
  log.writeBufferCount++;
155
192
  },
156
193
  writeTexture: () => undefined,
194
+ onSubmittedWorkDone: () => Promise.resolve(undefined),
157
195
  },
158
196
  createShaderModule: () => ({ getCompilationInfo: async () => ({ messages: [] }) }),
159
197
  createBindGroupLayout: () => ({}),
@@ -172,7 +210,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
172
210
  setPipeline: () => {
173
211
  log.setPipelineCount++;
174
212
  },
175
- setViewport: () => undefined,
176
213
  setVertexBuffer: () => undefined,
177
214
  setIndexBuffer: () => undefined,
178
215
  setBindGroup: (groupIndex: number, _bg: unknown, dynamicOffsets?: number[]) => {
@@ -235,17 +272,13 @@ function canonicalMaterialParamSchema(identifier: string): string {
235
272
  // w10: materialShaders[] entries are required so the record/extract path
236
273
  // can validate material passes that reference forgeax::default-standard-pbr
237
274
  // and forgeax::default-unlit. Each entry carries a minimal composedWgsl
238
- // stub + schema + the canonical capability variants.
275
+ // stub + empty paramSchema + empty variants.
239
276
  const materialShaderStub = (identifier: string) => ({
240
277
  identifier,
241
278
  sourcePath: `${identifier}.wgsl`,
242
279
  composedWgsl: '/* stub */',
243
- paramSchema: canonicalMaterialParamSchema(identifier),
244
- variants: canonicalDefaultMaterialVariants(identifier).filter(
245
- (variant) =>
246
- variant.defines.STORAGE_BUFFER_AVAILABLE === false &&
247
- variant.defines.VERTEX_COLOR_AVAILABLE === false,
248
- ),
280
+ paramSchema: '[]',
281
+ variants: [],
249
282
  });
250
283
  const manifest = {
251
284
  schemaVersion: '1.0.0',
@@ -292,19 +325,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
292
325
  // Type-only import imports the public surface so we can assert RenderSystem
293
326
  // is re-exported by `@forgeax/engine-runtime` (F-1 single-import contract part 3/3).
294
327
  async function importEngine(): Promise<{
295
- createRenderer: (
296
- canvas: unknown,
297
- opts?: unknown,
298
- bundler?: unknown,
299
- ) => Promise<{
300
- backend: string;
301
- ready: Promise<void>;
302
- draw: (worlds: unknown, opts: { cameraOwner: number; resourceOwner: number }) => void;
303
- onError: (cb: (err: { code: string; detail?: unknown; hint?: string }) => void) => () => void;
304
- assets: {
305
- register: (asset: unknown) => { ok: boolean; value: unknown };
306
- };
307
- }>;
328
+ createRenderer: (...args: unknown[]) => Promise<{ unwrap(): RendererLike }>;
308
329
  }> {
309
330
  return (await import(ENGINE)) as never;
310
331
  }
@@ -317,8 +338,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
317
338
  allocSharedRef: (target: string, payload: unknown) => Handle<string, 'shared'>;
318
339
  };
319
340
  }> {
320
- const { World: BaseWorld } = await import('@forgeax/engine-ecs');
321
- return { World: withPrimitiveMeshes(BaseWorld) } as never;
341
+ return (await import('@forgeax/engine-ecs')) as never;
322
342
  }
323
343
 
324
344
  async function importComponents(): Promise<{
@@ -336,27 +356,14 @@ function canonicalMaterialParamSchema(identifier: string): string {
336
356
  HANDLE_TRIANGLE: Handle<'MeshAsset', 'shared'>;
337
357
  }> {
338
358
  return {
339
- ...(await import('@forgeax/engine-render/internal')),
359
+ ...(await import('@forgeax/engine-render')),
340
360
  ...(await import('@forgeax/engine-scene')),
341
- HANDLE_CUBE: TEST_CUBE_HANDLE,
342
- HANDLE_TRIANGLE: TEST_TRIANGLE_HANDLE,
361
+ ...(await import('@forgeax/engine-assets-runtime')),
343
362
  } as never;
344
363
  }
345
364
 
346
365
  interface TestSetup {
347
- createRenderer: (
348
- canvas: unknown,
349
- opts?: unknown,
350
- bundler?: unknown,
351
- ) => Promise<{
352
- backend: string;
353
- ready: Promise<void>;
354
- draw: (worlds: unknown, opts: { cameraOwner: number; resourceOwner: number }) => void;
355
- onError: (cb: (err: { code: string; detail?: unknown; hint?: string }) => void) => () => void;
356
- assets: {
357
- register: (asset: unknown) => { ok: boolean; value: unknown };
358
- };
359
- }>;
366
+ createRenderer: (...args: unknown[]) => Promise<RendererType>;
360
367
  log: DeviceCallLog;
361
368
  }
362
369
 
@@ -365,7 +372,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
365
372
  const { device } = makeMockGPUDevice(log);
366
373
  vi.stubGlobal('navigator', { ...baseNavigator, gpu: makeMockGPU(device) });
367
374
  const engine = await importEngine();
368
- return { createRenderer: engine.createRenderer, log };
375
+ return {
376
+ createRenderer: async (...args: unknown[]) => (await engine.createRenderer(...args)).unwrap(),
377
+ log,
378
+ };
369
379
  }
370
380
 
371
381
  // ─── Tests ──────────────────────────────────────────────────────────────────
@@ -379,7 +389,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
379
389
  {},
380
390
  { shaderManifestUrl: buildManifestDataUrl() },
381
391
  );
382
- await renderer.ready;
383
392
  const { World } = await importEcs();
384
393
  const C = await importComponents();
385
394
  const world = new World();
@@ -417,28 +426,24 @@ function canonicalMaterialParamSchema(identifier: string): string {
417
426
  );
418
427
 
419
428
  const errors: { code: string }[] = [];
420
- renderer.onError((e) => errors.push(e));
421
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
422
- throw new Error('World attachment failed');
423
- }
424
- (world as WorldType).update().unwrap();
425
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
429
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
430
+ drawPublished(renderer, world as WorldType);
431
+ await new Promise((resolve) => setTimeout(resolve, 0));
432
+ Object.assign(log, makeLog());
433
+ drawPublished(renderer, world as WorldType);
426
434
 
427
435
  // DirectionalLight with merged shadow fields.
428
436
  expect(errors).toHaveLength(0);
429
- // Seven boot fallback clears remain outside the frame graph. The typed
430
- // graph adds directional, spot, point-shadow, and main attachment passes.
431
- expect(log.beginRenderPassCount).toBe(14);
432
- // CSM shadow passes bind their own pipeline before the main draw.
433
- expect(log.setPipelineCount).toBeGreaterThanOrEqual(1);
434
- // The typed shadow graph may record a caster draw in addition to the
435
- // main unlit draw; the contract is that the renderable is not dropped.
436
- expect(log.drawIndexedCount).toBeGreaterThanOrEqual(1);
437
- expect(log.encoderFinishCount).toBe(8); // shadow fallback + 6 cube fallback faces + frame
438
- expect(log.queueSubmitCount).toBe(8); // shadow fallback + 6 cube fallback faces + frame
437
+ // The current typed graph owns one shadow fallback, six cube fallback
438
+ // faces, and the frame attachment pass.
439
+ expect(log.beginRenderPassCount).toBe(8);
440
+ expect(log.setPipelineCount).toBe(1);
441
+ expect(log.drawIndexedCount).toBe(1);
442
+ expect(log.encoderFinishCount).toBe(1); // one typed graph command encoder
443
+ expect(log.queueSubmitCount).toBe(1); // one submission for the frame graph
439
444
  // Three BindGroups recorded per entity (view / material / mesh-array).
440
445
  expect(log.setBindGroupCount).toBeGreaterThanOrEqual(3);
441
- }, 30_000);
446
+ });
442
447
 
443
448
  it('AC-09: RenderSystem is NOT registered as an ECS schedule system', async () => {
444
449
  const { createRenderer } = await setupWebGPU();
@@ -448,18 +453,18 @@ function canonicalMaterialParamSchema(identifier: string): string {
448
453
  {},
449
454
  { shaderManifestUrl: buildManifestDataUrl() },
450
455
  );
451
- await renderer.ready;
452
456
  const { World } = await importEcs();
453
457
  const world = new World();
454
458
  // RenderSystem must NOT be auto-attached: world.update(1 / 60).unwrap() does not run
455
459
  // it (engine internal phase, plan-strategy D-S2).
456
460
  expect(world.inspect().systemCount).toBe(0);
457
461
  expect(world.inspect().systemCount).toBe(0);
458
- }, 30_000);
462
+ await renderer.dispose();
463
+ });
459
464
  });
460
465
 
461
466
  describe('RenderSystem error tier table (D-S4..D-S8)', () => {
462
- it('case A: entity missing Transform / MeshRenderer uses defaults; does NOT fire onError', async () => {
467
+ it('case A: entity missing Transform / MeshRenderer uses defaults; does NOT fire an error event', async () => {
463
468
  const { createRenderer } = await setupWebGPU();
464
469
  const canvas = makeMockCanvas({ webgl2: 'context', webgpu: 'context' });
465
470
  const renderer = await createRenderer(
@@ -467,7 +472,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
467
472
  {},
468
473
  { shaderManifestUrl: buildManifestDataUrl() },
469
474
  );
470
- await renderer.ready;
471
475
  const { World } = await importEcs();
472
476
  const C = await importComponents();
473
477
  const world = new World();
@@ -494,12 +498,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
494
498
  world.spawn({ component: C.MeshFilter, data: { assetHandle: C.HANDLE_CUBE } });
495
499
 
496
500
  const errors: { code: string }[] = [];
497
- renderer.onError((e) => errors.push(e));
498
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
499
- throw new Error('World attachment failed');
500
- }
501
- (world as WorldType).update().unwrap();
502
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
501
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
502
+ drawPublished(renderer, world as WorldType);
503
503
  expect(errors).toHaveLength(0);
504
504
  });
505
505
 
@@ -518,7 +518,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
518
518
  {},
519
519
  { shaderManifestUrl: buildManifestDataUrl() },
520
520
  );
521
- await renderer.ready;
522
521
  const { World } = await importEcs();
523
522
  const C = await importComponents();
524
523
  const world = new World();
@@ -530,12 +529,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
530
529
  );
531
530
 
532
531
  const errors: { code: string }[] = [];
533
- renderer.onError((e) => errors.push(e));
534
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
535
- throw new Error('World attachment failed');
536
- }
537
- (world as WorldType).update().unwrap();
538
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
532
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
533
+ drawPublished(renderer, world as WorldType);
539
534
 
540
535
  expect(errors.some((e) => e.code === 'render-system-no-camera')).toBe(true);
541
536
  // Clear pass executed under the synthetic camera path; the frame is
@@ -544,13 +539,13 @@ function canonicalMaterialParamSchema(identifier: string): string {
544
539
  // through the identity projection — that's an acceptable AI-user
545
540
  // signal (something paints + diagnostic fires) over the previous
546
541
  // "blank canvas + console error" silent skip. Assert at least one
547
- // additional submit landed beyond the renderer.ready baseline
542
+ // additional submit landed beyond the renderer construction baseline
548
543
  // (shadowFallback).
549
544
  expect(log.queueSubmitCount).toBeGreaterThanOrEqual(2);
550
545
  expect(log.beginRenderPassCount).toBeGreaterThanOrEqual(2);
551
546
  });
552
547
 
553
- it('case E: world has Camera + 0 renderables emits clear-pass-only frame; does NOT fire onError', async () => {
548
+ it('case E: world has Camera + 0 renderables emits clear-pass-only frame; does NOT fire an error event', async () => {
554
549
  // LO §1.1 hello-window minimum semantic: `Engine.create({ clearColor })`
555
550
  // must paint the swap-chain even when no entity carries MeshFilter +
556
551
  // MeshRenderer. The engine softens the empty-renderables case (Case E,
@@ -565,7 +560,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
565
560
  {},
566
561
  { shaderManifestUrl: buildManifestDataUrl() },
567
562
  );
568
- await renderer.ready;
569
563
  const { World } = await importEcs();
570
564
  const C = await importComponents();
571
565
  const world = new World();
@@ -591,23 +585,19 @@ function canonicalMaterialParamSchema(identifier: string): string {
591
585
  );
592
586
 
593
587
  const errors: { code: string }[] = [];
594
- renderer.onError((e) => errors.push(e));
595
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
596
- throw new Error('World attachment failed');
597
- }
598
- (world as WorldType).update().unwrap();
599
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
588
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
589
+ drawPublished(renderer, world as WorldType);
600
590
 
601
591
  // Soft path: no error fired (D-Q7 mirroring; LO §1.1 minimum semantic).
602
592
  expect(errors).toHaveLength(0);
603
593
  // Empty geometry does not remove graph-owned shadow and output passes.
604
- expect(log.beginRenderPassCount).toBe(11);
594
+ expect(log.beginRenderPassCount).toBe(12);
605
595
  expect(log.queueSubmitCount).toBe(8); // shadow fallback + 6 cube faces + frame
606
596
  // No geometry submitted.
607
597
  expect(log.drawIndexedCount).toBe(0);
608
598
  });
609
599
 
610
- it('case C: world has 0 DirectionalLight renders unlit; does NOT fire onError', async () => {
600
+ it('case C: world has 0 DirectionalLight renders unlit; does NOT fire an error event', async () => {
611
601
  const { createRenderer, log } = await setupWebGPU();
612
602
  const canvas = makeMockCanvas({ webgl2: 'context', webgpu: 'context' });
613
603
  const renderer = await createRenderer(
@@ -615,7 +605,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
615
605
  {},
616
606
  { shaderManifestUrl: buildManifestDataUrl() },
617
607
  );
618
- await renderer.ready;
619
608
  const { World } = await importEcs();
620
609
  const C = await importComponents();
621
610
  const world = new World();
@@ -644,16 +633,14 @@ function canonicalMaterialParamSchema(identifier: string): string {
644
633
  // No DirectionalLight.
645
634
 
646
635
  const errors: { code: string }[] = [];
647
- renderer.onError((e) => errors.push(e));
648
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
649
- throw new Error('World attachment failed');
650
- }
651
- (world as WorldType).update().unwrap();
652
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
636
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
637
+ drawPublished(renderer, world as WorldType);
638
+ await new Promise((resolve) => setTimeout(resolve, 0));
639
+ Object.assign(log, makeLog());
640
+ drawPublished(renderer, world as WorldType);
653
641
 
654
642
  expect(errors).toHaveLength(0);
655
- // Shadow and main passes are both allowed to visit the renderable.
656
- expect(log.drawIndexedCount).toBeGreaterThanOrEqual(1);
643
+ expect(log.drawIndexedCount).toBe(1);
657
644
  });
658
645
 
659
646
  it('case D: world has N>1 Camera fires render-system-multi-camera + uses first hit', async () => {
@@ -664,7 +651,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
664
651
  {},
665
652
  { shaderManifestUrl: buildManifestDataUrl() },
666
653
  );
667
- await renderer.ready;
668
654
  const { World } = await importEcs();
669
655
  const C = await importComponents();
670
656
  const world = new World();
@@ -709,12 +695,11 @@ function canonicalMaterialParamSchema(identifier: string): string {
709
695
  );
710
696
 
711
697
  const errors: { code: string; hint?: string }[] = [];
712
- renderer.onError((e) => errors.push(e));
713
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
714
- throw new Error('World attachment failed');
715
- }
716
- (world as WorldType).update().unwrap();
717
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
698
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
699
+ drawPublished(renderer, world as WorldType);
700
+ await new Promise((resolve) => setTimeout(resolve, 0));
701
+ Object.assign(log, makeLog());
702
+ drawPublished(renderer, world as WorldType);
718
703
 
719
704
  expect(errors.some((e) => e.code === 'render-system-multi-camera')).toBe(true);
720
705
  // First archetype hit still rendered.
@@ -735,7 +720,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
735
720
  {},
736
721
  { shaderManifestUrl: buildManifestDataUrl() },
737
722
  );
738
- await renderer.ready;
739
723
  const { World } = await importEcs();
740
724
  const C = await importComponents();
741
725
  const world = new World();
@@ -768,11 +752,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
768
752
  );
769
753
 
770
754
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
771
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
772
- throw new Error('World attachment failed');
773
- }
774
- (world as WorldType).update().unwrap();
775
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
755
+ drawPublished(renderer, world as WorldType);
756
+ await new Promise((resolve) => setTimeout(resolve, 0));
757
+ Object.assign(log, makeLog());
758
+ drawPublished(renderer, world as WorldType);
776
759
 
777
760
  const multiLightCalls = warnSpy.mock.calls.filter(
778
761
  (c) =>
@@ -786,7 +769,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
786
769
  });
787
770
 
788
771
  describe('RenderSystem asset-not-registered + internal exception (.detail F-3 contract)', () => {
789
- it('asset-not-registered fires onError with .detail = { assetHandle } and skips entity', async () => {
772
+ it('asset-not-registered fires an error event with .detail = { assetHandle } and skips entity', async () => {
790
773
  const { createRenderer, log } = await setupWebGPU();
791
774
  const canvas = makeMockCanvas({ webgl2: 'context', webgpu: 'context' });
792
775
  const renderer = await createRenderer(
@@ -794,7 +777,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
794
777
  {},
795
778
  { shaderManifestUrl: buildManifestDataUrl() },
796
779
  );
797
- await renderer.ready;
798
780
  const { World } = await importEcs();
799
781
  const C = await importComponents();
800
782
  const world = new World();
@@ -822,18 +804,16 @@ function canonicalMaterialParamSchema(identifier: string): string {
822
804
  { component: C.Transform, data: identityTransform() },
823
805
  );
824
806
 
825
- const errors: { code: string; detail?: { assetHandle?: number } }[] = [];
826
- renderer.onError((e) => errors.push(e as never));
827
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
828
- throw new Error('World attachment failed');
829
- }
830
- (world as WorldType).update().unwrap();
831
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
807
+ const errors: RendererErrorObservation[] = [];
808
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
809
+ drawPublished(renderer, world as WorldType);
832
810
 
833
811
  const assetErr = errors.find((e) => e.code === 'asset-not-registered');
834
812
  expect(assetErr).toBeDefined();
835
813
  expect(assetErr?.detail).toBeDefined();
836
- expect(assetErr?.detail?.assetHandle).toBe(BAD_HANDLE);
814
+ expect((assetErr?.detail as { assetHandle?: number } | undefined)?.assetHandle).toBe(
815
+ BAD_HANDLE,
816
+ );
837
817
  // The bad entity is skipped; no draw recorded for it (only entity).
838
818
  expect(log.drawIndexedCount).toBe(0);
839
819
  // Case E secondary-fix: even when every renderable fails asset
@@ -841,7 +821,7 @@ function canonicalMaterialParamSchema(identifier: string): string {
841
821
  // the cleared canvas rather than a stale frame).
842
822
  // The invalid entity is skipped, while graph-owned attachment passes are
843
823
  // still recorded after the seven boot fallback clears.
844
- expect(log.beginRenderPassCount).toBe(11);
824
+ expect(log.beginRenderPassCount).toBe(12);
845
825
  expect(log.queueSubmitCount).toBe(8); // shadow fallback + 6 cube faces + frame
846
826
  });
847
827
 
@@ -883,12 +863,9 @@ function canonicalMaterialParamSchema(identifier: string): string {
883
863
  vi.stubGlobal('navigator', { ...baseNavigator, gpu: makeMockGPU(device) });
884
864
  const engine = await importEngine();
885
865
  const canvas = makeMockCanvas({ webgl2: 'context', webgpu: 'context' });
886
- const renderer = await engine.createRenderer(
887
- canvas,
888
- {},
889
- { shaderManifestUrl: buildManifestDataUrl() },
890
- );
891
- await renderer.ready;
866
+ const renderer = (
867
+ await engine.createRenderer(canvas, {}, { shaderManifestUrl: buildManifestDataUrl() })
868
+ ).unwrap();
892
869
 
893
870
  const { World } = await importEcs();
894
871
  const C = await importComponents();
@@ -916,23 +893,19 @@ function canonicalMaterialParamSchema(identifier: string): string {
916
893
  { component: C.Transform, data: identityTransform() },
917
894
  );
918
895
 
919
- const errors: {
920
- code: string;
921
- detail?: { error?: { code: string; message: string } };
922
- }[] = [];
923
- renderer.onError((e) => errors.push(e as never));
924
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
925
- throw new Error('World attachment failed');
926
- }
927
- (world as WorldType).update().unwrap();
928
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
896
+ const errors: RendererErrorObservation[] = [];
897
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
898
+ drawPublished(renderer, world as WorldType);
929
899
 
930
900
  const runtimeErr = errors.find((e) => e.code === 'webgpu-runtime-error');
931
901
  expect(runtimeErr).toBeDefined();
932
902
  expect(runtimeErr?.detail).toBeDefined();
933
- expect(runtimeErr?.detail?.error).toBeDefined();
934
- expect(typeof runtimeErr?.detail?.error?.message).toBe('string');
935
- expect(runtimeErr?.detail?.error?.message).toContain('writeBuffer');
903
+ const runtimeDetail = runtimeErr?.detail as
904
+ | { error?: { code: string; message: string } }
905
+ | undefined;
906
+ expect(runtimeDetail?.error).toBeDefined();
907
+ expect(typeof runtimeDetail?.error?.message).toBe('string');
908
+ expect(runtimeDetail?.error?.message).toContain('writeBuffer');
936
909
  });
937
910
  });
938
911
 
@@ -1019,11 +992,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
1019
992
  {},
1020
993
  { shaderManifestUrl: buildManifestDataUrl() },
1021
994
  );
1022
- await renderer.ready;
1023
995
  const { World } = await importEcs();
1024
996
  const C = await importComponents();
1025
997
  const { setTransparentSortConfig, TRANSPARENT_SORT_MODE_DISTANCE } = await import(
1026
- '@forgeax/engine-render/internal'
998
+ '../../../render/src/systems/transparent-sort-config'
1027
999
  );
1028
1000
  const { RenderQueue } = await import('@forgeax/engine-types');
1029
1001
 
@@ -1115,12 +1087,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
1115
1087
  expect(cfgRes.ok).toBe(true);
1116
1088
 
1117
1089
  const errors: { code: string }[] = [];
1118
- renderer.onError((e) => errors.push(e));
1119
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
1120
- throw new Error('World attachment failed');
1121
- }
1122
- (world as WorldType).update().unwrap();
1123
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
1090
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
1091
+ drawPublished(renderer, world as WorldType);
1124
1092
 
1125
1093
  expect(errors).toHaveLength(0);
1126
1094
  // All 3 transparent entities are drawn.
@@ -1169,11 +1137,10 @@ function canonicalMaterialParamSchema(identifier: string): string {
1169
1137
  {},
1170
1138
  { shaderManifestUrl: buildManifestDataUrl() },
1171
1139
  );
1172
- await renderer.ready;
1173
1140
  const { World } = await importEcs();
1174
1141
  const C = await importComponents();
1175
1142
  const { setTransparentSortConfig, TRANSPARENT_SORT_MODE_DISTANCE } = await import(
1176
- '@forgeax/engine-render/internal'
1143
+ '../../../render/src/systems/transparent-sort-config'
1177
1144
  );
1178
1145
 
1179
1146
  // Register a MaterialAsset with Geometry-queue pass.
@@ -1241,12 +1208,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
1241
1208
  expect(cfgRes.ok).toBe(true);
1242
1209
 
1243
1210
  const errors: { code: string }[] = [];
1244
- renderer.onError((e) => errors.push(e));
1245
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
1246
- throw new Error('World attachment failed');
1247
- }
1248
- (world as WorldType).update().unwrap();
1249
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
1211
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
1212
+ drawPublished(renderer, world as WorldType);
1250
1213
 
1251
1214
  expect(errors).toHaveLength(0);
1252
1215
  // Both opaque entities are drawn (structural).
@@ -1266,7 +1229,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
1266
1229
  {},
1267
1230
  { shaderManifestUrl: buildManifestDataUrl() },
1268
1231
  );
1269
- await renderer.ready;
1270
1232
  const { World } = await importEcs();
1271
1233
  const C = await importComponents();
1272
1234
  const { RenderQueue } = await import('@forgeax/engine-types');
@@ -1330,12 +1292,8 @@ function canonicalMaterialParamSchema(identifier: string): string {
1330
1292
  );
1331
1293
 
1332
1294
  const errors: { code: string }[] = [];
1333
- renderer.onError((e) => errors.push(e));
1334
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
1335
- throw new Error('World attachment failed');
1336
- }
1337
- (world as WorldType).update().unwrap();
1338
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
1295
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
1296
+ drawPublished(renderer, world as WorldType);
1339
1297
 
1340
1298
  expect(errors).toHaveLength(0);
1341
1299
  expect(log.drawIndexedCount).toBe(1);
@@ -1356,7 +1314,6 @@ function canonicalMaterialParamSchema(identifier: string): string {
1356
1314
  {},
1357
1315
  { shaderManifestUrl: buildManifestDataUrl() },
1358
1316
  );
1359
- await renderer.ready;
1360
1317
  const { World } = await importEcs();
1361
1318
  const C = await importComponents();
1362
1319
 
@@ -1396,16 +1353,14 @@ function canonicalMaterialParamSchema(identifier: string): string {
1396
1353
  );
1397
1354
 
1398
1355
  const errors: { code: string }[] = [];
1399
- renderer.onError((e) => errors.push(e));
1400
- if (!(renderer as unknown as RendererType).attachWorld(world as WorldType).ok) {
1401
- throw new Error('World attachment failed');
1402
- }
1403
- (world as WorldType).update().unwrap();
1404
- renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 });
1356
+ subscribeRendererErrors(renderer, (e) => errors.push(e));
1357
+ drawPublished(renderer, world as WorldType);
1358
+ await new Promise((resolve) => setTimeout(resolve, 0));
1359
+ Object.assign(log, makeLog());
1360
+ drawPublished(renderer, world as WorldType);
1405
1361
 
1406
1362
  expect(errors).toHaveLength(0);
1407
- // Shadow and main passes are both allowed to visit the renderable.
1408
- expect(log.drawIndexedCount).toBeGreaterThanOrEqual(1);
1363
+ expect(log.drawIndexedCount).toBe(1);
1409
1364
 
1410
1365
  // Without stencilReference, the draw loop should call
1411
1366
  // setStencilReference(0) (harmless WebGPU default) or not call it at
@@ -1550,7 +1505,7 @@ describe('RenderSystem Skylight record phase (AC-11)', () => {
1550
1505
  // brdfLutBakeCount === 1 after the first frame that has Skylight.
1551
1506
  //
1552
1507
  // When t26 lands:
1553
- // const cache = getOrCreateIblCache(device);
1508
+ // const cache = getOrCreateIblCache(deviceScope);
1554
1509
  // expect(cache.irradianceBakeCount).toBe(1);
1555
1510
  // expect(cache.prefilterBakeCount).toBe(1);
1556
1511
  // expect(cache.brdfLutBakeCount).toBe(1);
@@ -1576,3 +1531,150 @@ describe('RenderSystem Skylight extract + record phase contract (plan-strategy D
1576
1531
  expect(noSeparateSystem).toBe(true);
1577
1532
  });
1578
1533
  });
1534
+
1535
+ // ─── from render-system-stride.test.ts ───
1536
+ {
1537
+ // w14 - RenderSystem extract entry stride defensive test (M3, AC-06).
1538
+ //
1539
+ // Locks the AC-06 invariant: when an entity carries an `Instances` component
1540
+ // whose `transforms` array<f32> snapshot has a length that is NOT a multiple
1541
+ // of 16, the RenderSystem extract stage MUST emit a structured `EcsError`
1542
+ // with `code: 'instance-transforms-stride-mismatch'` BEFORE the GPU upload
1543
+ // path is reached. The error carries a discriminated `.detail` per
1544
+ // `EcsErrorDetail`:
1545
+ //
1546
+ // detail = { actualLength: number, expectedStride: 16 }
1547
+ //
1548
+ // Coverage matrix:
1549
+ // (a) length === 16 -> pass; no error fires; renderable lands.
1550
+ // (b) length === 32 -> pass; no error fires; renderable lands.
1551
+ // (c) length === 15 -> fail; structured error fires with detail; no
1552
+ // renderable lands.
1553
+ // (d) length === 17 -> fail; structured error fires with detail; no
1554
+ // renderable lands.
1555
+ //
1556
+ // The defensive lives in `packages/runtime/src/render-system-extract.ts`
1557
+ // inside the `Instances` archetype loop, immediately after the
1558
+ // `world.get(entity, Instances)` snapshot is extracted but BEFORE any
1559
+ // per-element copy / GPU upload. Violation routes through Layer-3
1560
+ // `World._errorHandler` (set via `world.setErrorHandler`); the renderable
1561
+ // MUST NOT be pushed (fail-fast halts the per-entity branch).
1562
+
1563
+ interface CollectedError {
1564
+ readonly code: string;
1565
+ readonly detail: unknown;
1566
+ }
1567
+
1568
+ function makeIdentityTransform(): {
1569
+ pos: number[];
1570
+ quat: number[];
1571
+ scale: number[];
1572
+ } {
1573
+ return { pos: [0, 0, 0], quat: [0, 0, 0, 1], scale: [1, 1, 1] };
1574
+ }
1575
+
1576
+ function makeWorld(): { world: World; collected: CollectedError[] } {
1577
+ const collected: CollectedError[] = [];
1578
+ const world = new World();
1579
+ vi.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
1580
+ const e = args[args.length - 1] as { code?: string; detail?: unknown };
1581
+ if (e.code !== undefined) collected.push({ code: e.code, detail: e.detail });
1582
+ });
1583
+ // Camera + light so extractFrame's pre-loop short-circuit does not skip
1584
+ // the Instances archetype walk.
1585
+ world
1586
+ .spawn({
1587
+ component: Camera,
1588
+ data: {
1589
+ fov: 1.0,
1590
+ aspect: 1.0,
1591
+ near: 0.1,
1592
+ far: 100.0,
1593
+ projection: 0,
1594
+ left: -1,
1595
+ right: 1,
1596
+ bottom: -1,
1597
+ top: 1,
1598
+ },
1599
+ })
1600
+ .unwrap();
1601
+ world
1602
+ .spawn({
1603
+ component: DirectionalLight,
1604
+ data: {
1605
+ direction: [0, -1, 0],
1606
+ color: [1, 1, 1],
1607
+ intensity: 1,
1608
+ },
1609
+ })
1610
+ .unwrap();
1611
+ return { world, collected };
1612
+ }
1613
+
1614
+ function spawnInstancedEntity(world: World, transforms: Float32Array): void {
1615
+ world
1616
+ .spawn(
1617
+ { component: Transform, data: makeIdentityTransform() },
1618
+ {
1619
+ component: MeshFilter,
1620
+ data: { assetHandle: HANDLE_CUBE },
1621
+ },
1622
+ {
1623
+ component: MeshRenderer,
1624
+ data: {} as never,
1625
+ },
1626
+ { component: Instances, data: { transforms } },
1627
+ )
1628
+ .unwrap();
1629
+ }
1630
+
1631
+ describe('render-system-extract stride defensive (w14, AC-06)', () => {
1632
+ it('(a) length === 16: no error; renderable lands', () => {
1633
+ const { world, collected } = makeWorld();
1634
+ const transforms = new Float32Array(16);
1635
+ spawnInstancedEntity(world, transforms);
1636
+ const frame = extractFrame(world, prepareExtractContext(world));
1637
+ expect(collected.filter((c) => c.code === 'instance-transforms-stride-mismatch').length).toBe(
1638
+ 0,
1639
+ );
1640
+ expect(frame.renderables.length).toBe(1);
1641
+ });
1642
+
1643
+ it('(b) length === 32: no error; renderable lands', () => {
1644
+ const { world, collected } = makeWorld();
1645
+ const transforms = new Float32Array(32);
1646
+ spawnInstancedEntity(world, transforms);
1647
+ const frame = extractFrame(world, prepareExtractContext(world));
1648
+ expect(collected.filter((c) => c.code === 'instance-transforms-stride-mismatch').length).toBe(
1649
+ 0,
1650
+ );
1651
+ expect(frame.renderables.length).toBe(1);
1652
+ });
1653
+
1654
+ it('(c) length === 15: structured error fires; detail.actualLength === 15; expectedStride === 16; no renderable', () => {
1655
+ const { world, collected } = makeWorld();
1656
+ const transforms = new Float32Array(15);
1657
+ spawnInstancedEntity(world, transforms);
1658
+ const frame = extractFrame(world, prepareExtractContext(world));
1659
+ const stride = collected.filter((c) => c.code === 'instance-transforms-stride-mismatch');
1660
+ expect(stride.length).toBe(1);
1661
+ const detail = stride[0]?.detail as { actualLength: number; expectedStride: 16 };
1662
+ expect(detail.actualLength).toBe(15);
1663
+ expect(detail.expectedStride).toBe(16);
1664
+ expect(frame.renderables.length).toBe(0);
1665
+ });
1666
+
1667
+ it('(d) length === 17: structured error fires; detail.actualLength === 17; expectedStride === 16; no renderable', () => {
1668
+ const { world, collected } = makeWorld();
1669
+ const transforms = new Float32Array(17);
1670
+ spawnInstancedEntity(world, transforms);
1671
+ const frame = extractFrame(world, prepareExtractContext(world));
1672
+ const stride = collected.filter((c) => c.code === 'instance-transforms-stride-mismatch');
1673
+ expect(stride.length).toBe(1);
1674
+ const detail = stride[0]?.detail as { actualLength: number; expectedStride: 16 };
1675
+ expect(detail.actualLength).toBe(17);
1676
+ expect(detail.expectedStride).toBe(16);
1677
+ expect(frame.renderables.length).toBe(0);
1678
+ });
1679
+ });
1680
+ }