@forgeax/engine-assets-runtime 0.1.3 → 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 (313) hide show
  1. package/README.md +442 -175
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/aabb.unit.test.d.ts +2 -0
  4. package/dist/__tests__/aabb.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/artifact-integrity.unit.test.d.ts +2 -0
  6. package/dist/__tests__/artifact-integrity.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/artifact-io.unit.test.d.ts +2 -0
  8. package/dist/__tests__/artifact-io.unit.test.d.ts.map +1 -0
  9. package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts +2 -0
  10. package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/asset-evidence.unit.test.d.ts +2 -0
  12. package/dist/__tests__/asset-evidence.unit.test.d.ts.map +1 -0
  13. package/dist/__tests__/asset-kind.unit.test.d.ts +2 -0
  14. package/dist/__tests__/asset-kind.unit.test.d.ts.map +1 -0
  15. package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts +2 -0
  16. package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts.map +1 -0
  17. package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts +2 -0
  18. package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts.map +1 -0
  19. package/dist/__tests__/asset-registry-catalog.unit.test.d.ts +2 -0
  20. package/dist/__tests__/asset-registry-catalog.unit.test.d.ts.map +1 -0
  21. package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
  22. package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
  23. package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
  24. package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
  25. package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts +2 -0
  26. package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts.map +1 -0
  27. package/dist/__tests__/catalog-replica.unit.test.d.ts +2 -0
  28. package/dist/__tests__/catalog-replica.unit.test.d.ts.map +1 -0
  29. package/dist/__tests__/catalog-source-v2.unit.test.d.ts +2 -0
  30. package/dist/__tests__/catalog-source-v2.unit.test.d.ts.map +1 -0
  31. package/dist/__tests__/catalog-source.unit.test.d.ts +2 -0
  32. package/dist/__tests__/catalog-source.unit.test.d.ts.map +1 -0
  33. package/dist/__tests__/catalog-subscription-order.unit.test.d.ts +2 -0
  34. package/dist/__tests__/catalog-subscription-order.unit.test.d.ts.map +1 -0
  35. package/dist/__tests__/catalog-subscription.unit.test.d.ts +2 -0
  36. package/dist/__tests__/catalog-subscription.unit.test.d.ts.map +1 -0
  37. package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts +2 -0
  38. package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts.map +1 -0
  39. package/dist/__tests__/decode-image-bytes.browser.test.d.ts +2 -0
  40. package/dist/__tests__/decode-image-bytes.browser.test.d.ts.map +1 -0
  41. package/dist/__tests__/decode-image-bytes.test-d.d.ts +2 -0
  42. package/dist/__tests__/decode-image-bytes.test-d.d.ts.map +1 -0
  43. package/dist/__tests__/decode-image-bytes.test.d.ts +2 -0
  44. package/dist/__tests__/decode-image-bytes.test.d.ts.map +1 -0
  45. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
  46. package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
  47. package/dist/__tests__/errors-asset.unit.test.d.ts +2 -0
  48. package/dist/__tests__/errors-asset.unit.test.d.ts.map +1 -0
  49. package/dist/__tests__/inline-pack-loaders.unit.test.d.ts +2 -0
  50. package/dist/__tests__/inline-pack-loaders.unit.test.d.ts.map +1 -0
  51. package/dist/__tests__/instantiate-context.unit.test.d.ts +2 -0
  52. package/dist/__tests__/instantiate-context.unit.test.d.ts.map +1 -0
  53. package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts +2 -0
  54. package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts.map +1 -0
  55. package/dist/__tests__/instantiate.unit.test.d.ts +2 -0
  56. package/dist/__tests__/instantiate.unit.test.d.ts.map +1 -0
  57. package/dist/__tests__/loader-input-contract.unit.test.d.ts +2 -0
  58. package/dist/__tests__/loader-input-contract.unit.test.d.ts.map +1 -0
  59. package/dist/__tests__/loader-registry.unit.test.d.ts +2 -0
  60. package/dist/__tests__/loader-registry.unit.test.d.ts.map +1 -0
  61. package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts +2 -0
  62. package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts.map +1 -0
  63. package/dist/__tests__/material-generation-cache.unit.test.d.ts +2 -0
  64. package/dist/__tests__/material-generation-cache.unit.test.d.ts.map +1 -0
  65. package/dist/__tests__/material-load-consumer.test-d.d.ts +2 -0
  66. package/dist/__tests__/material-load-consumer.test-d.d.ts.map +1 -0
  67. package/dist/__tests__/material-ready.integration.test.d.ts +2 -0
  68. package/dist/__tests__/material-ready.integration.test.d.ts.map +1 -0
  69. package/dist/__tests__/material-stale-generation.unit.test.d.ts +2 -0
  70. package/dist/__tests__/material-stale-generation.unit.test.d.ts.map +1 -0
  71. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
  72. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
  73. package/dist/__tests__/mesh-bin.unit.test.d.ts +2 -0
  74. package/dist/__tests__/mesh-bin.unit.test.d.ts.map +1 -0
  75. package/dist/__tests__/mipmap-generator.unit.test.d.ts +2 -0
  76. package/dist/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
  77. package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts +2 -0
  78. package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts.map +1 -0
  79. package/dist/__tests__/pack-basis-load.integration.test.d.ts +2 -0
  80. package/dist/__tests__/pack-basis-load.integration.test.d.ts.map +1 -0
  81. package/dist/__tests__/payload-validate.unit.test.d.ts +2 -0
  82. package/dist/__tests__/payload-validate.unit.test.d.ts.map +1 -0
  83. package/dist/__tests__/plugin.test.d.ts +2 -0
  84. package/dist/__tests__/plugin.test.d.ts.map +1 -0
  85. package/dist/__tests__/registry-concurrency.bench.test.d.ts +2 -0
  86. package/dist/__tests__/registry-concurrency.bench.test.d.ts.map +1 -0
  87. package/dist/__tests__/registry-failure-retry.integration.test.d.ts +2 -0
  88. package/dist/__tests__/registry-failure-retry.integration.test.d.ts.map +1 -0
  89. package/dist/__tests__/registry-load-graph.integration.test.d.ts +2 -0
  90. package/dist/__tests__/registry-load-graph.integration.test.d.ts.map +1 -0
  91. package/dist/__tests__/registry-load-state.unit.test.d.ts +2 -0
  92. package/dist/__tests__/registry-load-state.unit.test.d.ts.map +1 -0
  93. package/dist/__tests__/registry-package-cache.unit.test.d.ts +2 -0
  94. package/dist/__tests__/registry-package-cache.unit.test.d.ts.map +1 -0
  95. package/dist/__tests__/registry-public-ready.unit.test.d.ts +2 -0
  96. package/dist/__tests__/registry-public-ready.unit.test.d.ts.map +1 -0
  97. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
  98. package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
  99. package/dist/__tests__/scene-handle-fields.unit.test.d.ts +2 -0
  100. package/dist/__tests__/scene-handle-fields.unit.test.d.ts.map +1 -0
  101. package/dist/__tests__/scene-payload.unit.test.d.ts +2 -0
  102. package/dist/__tests__/scene-payload.unit.test.d.ts.map +1 -0
  103. package/dist/__tests__/scene-publication-fence.test.d.ts +2 -0
  104. package/dist/__tests__/scene-publication-fence.test.d.ts.map +1 -0
  105. package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts +2 -0
  106. package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts.map +1 -0
  107. package/dist/__tests__/static-rig.integration.test.d.ts +2 -0
  108. package/dist/__tests__/static-rig.integration.test.d.ts.map +1 -0
  109. package/dist/__tests__/validate-material.unit.test.d.ts +2 -0
  110. package/dist/__tests__/validate-material.unit.test.d.ts.map +1 -0
  111. package/dist/__tests__/video-pack-loader.integration.test.d.ts +2 -0
  112. package/dist/__tests__/video-pack-loader.integration.test.d.ts.map +1 -0
  113. package/dist/aabb.d.ts +3 -0
  114. package/dist/aabb.d.ts.map +1 -0
  115. package/dist/asset-registry.d.ts +552 -0
  116. package/dist/asset-registry.d.ts.map +1 -0
  117. package/dist/builtin-asset-registry.d.ts +22 -0
  118. package/dist/builtin-asset-registry.d.ts.map +1 -0
  119. package/dist/catalog-source.d.ts +2 -1
  120. package/dist/catalog-source.d.ts.map +1 -1
  121. package/dist/decode-image-bytes.d.ts +57 -0
  122. package/dist/decode-image-bytes.d.ts.map +1 -0
  123. package/dist/dynamic-texture-store.d.ts +89 -0
  124. package/dist/dynamic-texture-store.d.ts.map +1 -0
  125. package/dist/errors/asset.d.ts +201 -0
  126. package/dist/errors/asset.d.ts.map +1 -0
  127. package/dist/handles.d.ts +141 -0
  128. package/dist/handles.d.ts.map +1 -0
  129. package/dist/image-error.d.ts +6 -0
  130. package/dist/image-error.d.ts.map +1 -0
  131. package/dist/index.d.ts +32 -3
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.mjs +7218 -114
  134. package/dist/index.mjs.map +1 -1
  135. package/dist/internal.d.ts +2 -1
  136. package/dist/internal.d.ts.map +1 -1
  137. package/dist/internal.mjs +317 -3
  138. package/dist/internal.mjs.map +1 -1
  139. package/dist/loader-registry.d.ts +63 -0
  140. package/dist/loader-registry.d.ts.map +1 -0
  141. package/dist/loaders/inline-pack.d.ts +72 -0
  142. package/dist/loaders/inline-pack.d.ts.map +1 -0
  143. package/dist/loaders/mesh-bin.d.ts +21 -0
  144. package/dist/loaders/mesh-bin.d.ts.map +1 -0
  145. package/dist/loaders/pack-artifact.d.ts +20 -0
  146. package/dist/loaders/pack-artifact.d.ts.map +1 -0
  147. package/dist/material/dependency-graph.d.ts +9 -0
  148. package/dist/material/dependency-graph.d.ts.map +1 -0
  149. package/dist/material/generation-cache.d.ts +24 -0
  150. package/dist/material/generation-cache.d.ts.map +1 -0
  151. package/dist/material/inspection.d.ts +24 -0
  152. package/dist/material/inspection.d.ts.map +1 -0
  153. package/dist/material/loader.d.ts +59 -0
  154. package/dist/material/loader.d.ts.map +1 -0
  155. package/dist/material/runtime-shader.d.ts +14 -0
  156. package/dist/material/runtime-shader.d.ts.map +1 -0
  157. package/dist/mesh-bin.d.ts +3 -0
  158. package/dist/mesh-bin.d.ts.map +1 -0
  159. package/dist/mipmap-generator.d.ts +132 -0
  160. package/dist/mipmap-generator.d.ts.map +1 -0
  161. package/dist/payload-validate.d.ts +60 -0
  162. package/dist/payload-validate.d.ts.map +1 -0
  163. package/dist/plugin.d.ts +16 -0
  164. package/dist/plugin.d.ts.map +1 -0
  165. package/dist/registry/artifact-io.d.ts +16 -0
  166. package/dist/registry/artifact-io.d.ts.map +1 -0
  167. package/dist/registry/asset-evidence.d.ts +13 -0
  168. package/dist/registry/asset-evidence.d.ts.map +1 -0
  169. package/dist/registry/catalog-state.d.ts +47 -0
  170. package/dist/registry/catalog-state.d.ts.map +1 -0
  171. package/dist/registry/catalog.d.ts +48 -0
  172. package/dist/registry/catalog.d.ts.map +1 -0
  173. package/dist/registry/instantiate.d.ts +153 -0
  174. package/dist/registry/instantiate.d.ts.map +1 -0
  175. package/dist/registry/load-by-guid.d.ts +247 -0
  176. package/dist/registry/load-by-guid.d.ts.map +1 -0
  177. package/dist/registry/load-state.d.ts +26 -0
  178. package/dist/registry/load-state.d.ts.map +1 -0
  179. package/dist/registry/runtime-projection.d.ts +16 -0
  180. package/dist/registry/runtime-projection.d.ts.map +1 -0
  181. package/dist/registry/scene-publication-fence.d.ts +38 -0
  182. package/dist/registry/scene-publication-fence.d.ts.map +1 -0
  183. package/dist/registry/validate-material.d.ts +77 -0
  184. package/dist/registry/validate-material.d.ts.map +1 -0
  185. package/dist/resolve-asset-handle.d.ts +43 -0
  186. package/dist/resolve-asset-handle.d.ts.map +1 -0
  187. package/dist/resolve-tileset-runtime.d.ts +23 -0
  188. package/dist/resolve-tileset-runtime.d.ts.map +1 -0
  189. package/dist/scene-handle-fields.d.ts +88 -0
  190. package/dist/scene-handle-fields.d.ts.map +1 -0
  191. package/dist/scene-payload.d.ts +15 -0
  192. package/dist/scene-payload.d.ts.map +1 -0
  193. package/dist/wire-default-loaders.d.ts +29 -0
  194. package/dist/wire-default-loaders.d.ts.map +1 -0
  195. package/package.json +22 -9
  196. package/src/__tests__/aabb.unit.test.ts +97 -0
  197. package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
  198. package/src/__tests__/artifact-io.unit.test.ts +127 -0
  199. package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
  200. package/src/__tests__/asset-evidence.unit.test.ts +32 -0
  201. package/src/__tests__/{asset-kind.test.ts → asset-kind.unit.test.ts} +1 -1
  202. package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
  203. package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
  204. package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
  205. package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
  206. package/src/__tests__/asset-registry.unit.test.ts +511 -0
  207. package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
  208. package/src/__tests__/catalog-replica.unit.test.ts +196 -0
  209. package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
  210. package/src/__tests__/catalog-source.unit.test.ts +126 -0
  211. package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
  212. package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
  213. package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
  214. package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
  215. package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
  216. package/src/__tests__/decode-image-bytes.test.ts +90 -0
  217. package/src/__tests__/decode-image-mime-owner.test.ts +2 -5
  218. package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
  219. package/src/__tests__/errors-asset.unit.test.ts +92 -0
  220. package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
  221. package/src/__tests__/instantiate-context.unit.test.ts +65 -0
  222. package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
  223. package/src/__tests__/instantiate.unit.test.ts +684 -0
  224. package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
  225. package/src/__tests__/loader-registry.unit.test.ts +101 -0
  226. package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
  227. package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
  228. package/src/__tests__/material-load-consumer.test-d.ts +42 -0
  229. package/src/__tests__/material-ready.integration.test.ts +492 -0
  230. package/src/__tests__/material-stale-generation.unit.test.ts +152 -0
  231. package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
  232. package/src/__tests__/mesh-bin.unit.test.ts +152 -0
  233. package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
  234. package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
  235. package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
  236. package/src/__tests__/payload-validate.unit.test.ts +285 -0
  237. package/src/__tests__/plugin.test.ts +55 -0
  238. package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
  239. package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
  240. package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
  241. package/src/__tests__/registry-load-state.unit.test.ts +35 -0
  242. package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
  243. package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
  244. package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
  245. package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
  246. package/src/__tests__/scene-payload.unit.test.ts +151 -0
  247. package/src/__tests__/scene-publication-fence.test.ts +129 -0
  248. package/src/__tests__/scriptable-pack-public-surface.test-d.ts +16 -0
  249. package/src/__tests__/static-rig.integration.test.ts +8 -0
  250. package/src/__tests__/validate-material.unit.test.ts +359 -0
  251. package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
  252. package/src/aabb.ts +107 -0
  253. package/src/asset-registry.ts +2040 -0
  254. package/src/builtin-asset-registry.ts +164 -0
  255. package/src/catalog-source.ts +14 -70
  256. package/src/decode-image-bytes.ts +126 -0
  257. package/src/dynamic-texture-store.ts +234 -0
  258. package/src/errors/asset.ts +347 -0
  259. package/src/handles.ts +180 -0
  260. package/src/image-error.ts +45 -0
  261. package/src/index.ts +177 -19
  262. package/src/internal.ts +2 -0
  263. package/src/loader-registry.ts +157 -0
  264. package/src/loaders/inline-pack.ts +837 -0
  265. package/src/loaders/mesh-bin.ts +304 -0
  266. package/src/loaders/pack-artifact.ts +665 -0
  267. package/src/material/dependency-graph.ts +46 -0
  268. package/src/material/generation-cache.ts +177 -0
  269. package/src/material/inspection.ts +46 -0
  270. package/src/material/loader.ts +326 -0
  271. package/src/material/runtime-shader.ts +133 -0
  272. package/src/mesh-bin.ts +5 -0
  273. package/src/mipmap-generator.ts +450 -0
  274. package/src/payload-validate.ts +573 -0
  275. package/src/plugin.ts +48 -0
  276. package/src/registry/artifact-io.ts +259 -0
  277. package/src/registry/asset-evidence.ts +51 -0
  278. package/src/registry/catalog-state.ts +409 -0
  279. package/src/registry/catalog.ts +369 -0
  280. package/src/registry/instantiate.ts +1056 -0
  281. package/src/registry/load-by-guid.ts +1657 -0
  282. package/src/registry/load-state.ts +115 -0
  283. package/src/registry/runtime-projection.ts +63 -0
  284. package/src/registry/scene-publication-fence.ts +256 -0
  285. package/src/registry/validate-material.ts +386 -0
  286. package/src/resolve-asset-handle.ts +147 -0
  287. package/src/resolve-tileset-runtime.ts +72 -0
  288. package/src/scene-handle-fields.ts +221 -0
  289. package/src/scene-payload.ts +207 -0
  290. package/src/wire-default-loaders.ts +79 -0
  291. package/dist/__tests__/asset-graph-red.integration.test.d.ts +0 -2
  292. package/dist/__tests__/asset-graph-red.integration.test.d.ts.map +0 -1
  293. package/dist/__tests__/asset-kind.test.d.ts +0 -2
  294. package/dist/__tests__/asset-kind.test.d.ts.map +0 -1
  295. package/dist/__tests__/asset-registry-core.integration.test.d.ts +0 -2
  296. package/dist/__tests__/asset-registry-core.integration.test.d.ts.map +0 -1
  297. package/dist/__tests__/asset-registry-public-api.test-d.d.ts +0 -2
  298. package/dist/__tests__/asset-registry-public-api.test-d.d.ts.map +0 -1
  299. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts +0 -2
  300. package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts.map +0 -1
  301. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts +0 -2
  302. package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts.map +0 -1
  303. package/dist/__tests__/catalog-session-red.unit.test.d.ts +0 -2
  304. package/dist/__tests__/catalog-session-red.unit.test.d.ts.map +0 -1
  305. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts +0 -2
  306. package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts.map +0 -1
  307. package/src/__tests__/asset-graph-red.integration.test.ts +0 -113
  308. package/src/__tests__/asset-registry-core.integration.test.ts +0 -161
  309. package/src/__tests__/asset-registry-public-api.test-d.ts +0 -31
  310. package/src/__tests__/asset-runtime-core-lifecycle.integration.test.ts +0 -79
  311. package/src/__tests__/asset-runtime-snapshot.unit.test.ts +0 -23
  312. package/src/__tests__/catalog-session-red.unit.test.ts +0 -276
  313. package/src/__tests__/registry-lifecycle-red.integration.test.ts +0 -80
@@ -0,0 +1,450 @@
1
+ // @forgeax/engine-assets-runtime - mipmap-generator.ts (T-M3-04).
2
+ //
3
+ // Independent-file home of the runtime mipmap auto-generation utility
4
+ // (plan-strategy section 2.6 D Open Q-5 selection (a)). Purposefully lives
5
+ // outside `asset-registry.ts` so the asset-registry stays single-purpose
6
+ // (charter proposition 5 producer / consumer split; architecture-principles
7
+ // pipeline isolation).
8
+ //
9
+ // Shader-module DI boundary (bug-20260518 D-1): the per-device pipeline
10
+ // cache builds its single ShaderModule via an injected
11
+ // `MipmapShaderModuleFactory`, NOT via `device.createShaderModule` -- the
12
+ // post-fix-f3 `RhiDevice` surface has no synchronous `createShaderModule`
13
+ // member, and the runtime mipmap utility must NOT reverse-import the
14
+ // rhi-webgpu shim package (charter F1 layering). The factory is wired
15
+ // once at `createRenderer.ts` from `pack.createShaderModule` and threaded
16
+ // through `AssetRegistry.configureGpuDevice` -> `generateMipmaps` ->
17
+ // `getOrCreateMipmapPipeline` -> `ensureDeviceCache`.
18
+ //
19
+ // SSOT three-source convergence (research F-1 + F-2):
20
+ // - webgpufundamentals importing-textures-mipmaps (formula + main loop)
21
+ // - toji web-texture-tool webgpu-mipmap-generator (oversized triangle WGSL)
22
+ // - greggman webgpu-utils generate-mipmap (WeakMap<GPUDevice> cache)
23
+ //
24
+ // Public surface (charter F2 minimal):
25
+ // - numMipLevels({ width, height }) -> number (formula SSOT)
26
+ // - getOrCreateMipmapPipeline(device, format) -> Result (cache lookup; per-format)
27
+ // - mipmapCacheSize(device) -> number (introspection)
28
+ // - generateMipmaps(device, texture, descriptor) -> Result<void>(per-mip render-pass blit-loop)
29
+ //
30
+ // Per-device cache structure (greggman SSOT):
31
+ // WeakMap<MipmapDevice, MipmapPipelineCache>
32
+ // where MipmapPipelineCache = {
33
+ // sampler: Sampler, // shared linear / linear / linear sampler (F-5)
34
+ // module: ShaderModule, // shared oversized-triangle vertex + sampling fragment WGSL
35
+ // layout: BindGroupLayout, // shared sampler @binding(0) + texture_2d<f32> @binding(1)
36
+ // pipelines: Map<GPUTextureFormat, RenderPipeline> // per-format pipeline (F-2 cache key)
37
+ // }
38
+ // Per-device isolation prevents pipeline leak across `createRenderer` calls
39
+ // (charter P5 consistent abstraction; greggman 36-44 anchor).
40
+ //
41
+ // Why this layout (not closure / not class):
42
+ // - `getOrCreateMipmapPipeline` is module-level for test surface (T-M3-01
43
+ // directly imports it; cache reference equality assertions need stable
44
+ // identity across calls).
45
+ // - `numMipLevels` is pure (no device argument); the WeakMap lookup only
46
+ // happens at pipeline-create time.
47
+
48
+ import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
49
+
50
+ /**
51
+ * Subset of `RhiDevice` consumed by the mipmap utility (charter F2: only
52
+ * the methods the algorithm actually invokes; the dawn integration tests
53
+ * pass a real `RhiDevice` and the unit cache tests pass a hand-rolled stub).
54
+ *
55
+ * Four entry-points (all synchronous Result):
56
+ * - createSampler (default linear / linear / linear)
57
+ * - createBindGroupLayout
58
+ * - createPipelineLayout
59
+ * - createRenderPipeline (per-format pipeline cache miss)
60
+ *
61
+ * Shader module is NOT an entry-point here; it is supplied by an injected
62
+ * `MipmapShaderModuleFactory` (bug-20260518 D-1) so the utility does not
63
+ * reach into the rhi-webgpu shim package from the runtime layer.
64
+ *
65
+ * Plus `createCommandEncoder` + `queue.submit` are added once the upload
66
+ * call-site (T-M3-05) wires generateMipmaps in; the cache-only public hooks
67
+ * exercised by T-M3-01 do not need them, so they live behind an extended
68
+ * MipmapDeviceForBlit alias.
69
+ */
70
+ export type MipmapDevice = {
71
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
72
+ createSampler(desc?: any): Result<any, any>;
73
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
74
+ createBindGroupLayout(desc: any): Result<any, any>;
75
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
76
+ createPipelineLayout(desc: any): Result<any, any>;
77
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
78
+ createRenderPipeline(desc: any): Result<any, any>;
79
+ };
80
+
81
+ /**
82
+ * Async shader-module factory injected through the call-chain
83
+ * `createRenderer.ts` -> `AssetRegistry.configureGpuDevice` ->
84
+ * `generateMipmaps` -> `getOrCreateMipmapPipeline` -> `ensureDeviceCache`
85
+ * (bug-20260518 D-1 / D-4).
86
+ *
87
+ * Signature mirrors the top-level async `createShaderModule(device, desc)`
88
+ * exposed by the rhi-webgpu shim (and the rhi-wgpu shim) so the
89
+ * call-site in createRenderer can pass `pack.createShaderModule` directly
90
+ * without a structural cast. The opaque module value is typed `unknown`
91
+ * here -- callers do not introspect it; they hand the value back to
92
+ * `createRenderPipeline` descriptors via the cached `MipmapPipelineCache`.
93
+ */
94
+ export type MipmapShaderModuleFactory = (
95
+ device: MipmapDevice,
96
+ desc: { code: string; label?: string },
97
+ ) => Promise<Result<unknown, RhiError>>;
98
+
99
+ /**
100
+ * Per-device pipeline cache structure (research F-2 SSOT).
101
+ * Held under a module-private `WeakMap<MipmapDevice, MipmapPipelineCache>`.
102
+ */
103
+ interface MipmapPipelineCache {
104
+ // biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU sampler
105
+ readonly sampler: any;
106
+ // biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU shader module
107
+ readonly module: any;
108
+ // biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU bind-group layout
109
+ readonly layout: any;
110
+ // biome-ignore lint/suspicious/noExplicitAny: per-format render pipeline cache
111
+ readonly pipelines: Map<GPUTextureFormat, any>;
112
+ }
113
+
114
+ // Module-private cache (greggman 36-44 SSOT). WeakMap so that destroying a
115
+ // device naturally clears its cache (no manual cleanup hook needed).
116
+ const deviceCache: WeakMap<MipmapDevice, MipmapPipelineCache> = new WeakMap();
117
+
118
+ /**
119
+ * Oversized-triangle WGSL (toji + greggman convergent template).
120
+ *
121
+ * Vertex stage emits a single triangle covering the entire NDC, and texCoord
122
+ * is computed from clip-space position via `pos * vec2(0.5, -0.5) +
123
+ * vec2(0.5)` (NDC -> UV with Y-flip in one fused MAD; saves a 6-vertex quad).
124
+ *
125
+ * Fragment stage samples the bound `texture_2d<f32>` with the linear /
126
+ * linear / linear sampler -- bilinear when the source mip is read by the
127
+ * destination mip, automatic sRGB decode / encode happens in the spec when
128
+ * source AND destination views are the SAME `*-srgb` format (research F-3).
129
+ */
130
+ const MIPMAP_WGSL = `
131
+ struct VsOut {
132
+ @builtin(position) clip : vec4<f32>,
133
+ @location(0) uv : vec2<f32>,
134
+ };
135
+
136
+ @vertex
137
+ fn vs_main(@builtin(vertex_index) vid : u32) -> VsOut {
138
+ // 3 vertices forming an oversized triangle: (-1,-1), (-1,3), (3,-1)
139
+ var positions = array<vec2<f32>, 3>(
140
+ vec2<f32>(-1.0, -1.0),
141
+ vec2<f32>(-1.0, 3.0),
142
+ vec2<f32>( 3.0, -1.0),
143
+ );
144
+ let pos = positions[vid];
145
+ var out : VsOut;
146
+ out.clip = vec4<f32>(pos, 0.0, 1.0);
147
+ // NDC -> UV with Y-flip fused into a single MAD.
148
+ out.uv = pos * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
149
+ return out;
150
+ }
151
+
152
+ @group(0) @binding(0) var src_sampler : sampler;
153
+ @group(0) @binding(1) var src_texture : texture_2d<f32>;
154
+
155
+ @fragment
156
+ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
157
+ return textureSample(src_texture, src_sampler, in.uv);
158
+ }
159
+ `;
160
+
161
+ /**
162
+ * Compute mip-level count for a 2D texture (research F-1 SSOT formula).
163
+ *
164
+ * Spec-anchor: WebGPU §3.6 texture-creation says `mipLevelCount` defaults
165
+ * to 1; the runtime utility opts AI users into a full chain via
166
+ * `floor(log2(max(width, height))) + 1` -- the same formula greggman /
167
+ * toji / webgpufundamentals all converge on.
168
+ *
169
+ * @example
170
+ * numMipLevels({ width: 256, height: 256 }) // 9
171
+ * numMipLevels({ width: 1, height: 1 }) // 1
172
+ */
173
+ export function numMipLevels(size: { width: number; height: number }): number {
174
+ const max = Math.max(size.width, size.height);
175
+ if (max <= 1) return 1;
176
+ return Math.floor(Math.log2(max)) + 1;
177
+ }
178
+
179
+ /**
180
+ * Number of cached pipelines for the device (introspection hook used by
181
+ * T-M3-01 (c) cache-hit / (d) per-device-isolation tests; charter P3
182
+ * explicit failure: a cache size of 0 on a fresh device is a witnessed
183
+ * pre-condition rather than a swept-under hint).
184
+ */
185
+ export function mipmapCacheSize(device: MipmapDevice): number {
186
+ const cache = deviceCache.get(device);
187
+ return cache?.pipelines.size ?? 0;
188
+ }
189
+
190
+ /**
191
+ * Get the existing per-format render pipeline or build a fresh one.
192
+ *
193
+ * Per-device WeakMap + per-format `Map<GPUTextureFormat, RenderPipeline>`
194
+ * (greggman 36-44 + 119-122). The shared sampler / shader-module /
195
+ * bind-group-layout are constructed lazily on the first cache miss for a
196
+ * given device and reused across all formats on that device.
197
+ */
198
+ export async function getOrCreateMipmapPipeline(
199
+ device: MipmapDevice,
200
+ format: GPUTextureFormat,
201
+ asyncCreateShaderModule: MipmapShaderModuleFactory,
202
+ // biome-ignore lint/suspicious/noExplicitAny: returns opaque GPU pipeline
203
+ ): Promise<Result<any, unknown>> {
204
+ const cache = await ensureDeviceCache(device, asyncCreateShaderModule);
205
+ if (!cache.ok) return cache;
206
+ const existing = cache.value.pipelines.get(format);
207
+ if (existing !== undefined) {
208
+ return ok(existing);
209
+ }
210
+ const layoutRes = device.createPipelineLayout({
211
+ label: 'mipmap-pl',
212
+ bindGroupLayouts: [cache.value.layout],
213
+ });
214
+ if (!layoutRes.ok) return layoutRes;
215
+ const pipelineRes = device.createRenderPipeline({
216
+ label: `mipmap-pipeline-${format}`,
217
+ layout: layoutRes.value,
218
+ vertex: { module: cache.value.module, entryPoint: 'vs_main' },
219
+ fragment: {
220
+ module: cache.value.module,
221
+ entryPoint: 'fs_main',
222
+ targets: [{ format }],
223
+ },
224
+ primitive: { topology: 'triangle-list' },
225
+ });
226
+ if (!pipelineRes.ok) return pipelineRes;
227
+ cache.value.pipelines.set(format, pipelineRes.value);
228
+ return pipelineRes;
229
+ }
230
+
231
+ async function ensureDeviceCache(
232
+ device: MipmapDevice,
233
+ asyncCreateShaderModule: MipmapShaderModuleFactory,
234
+ ): Promise<Result<MipmapPipelineCache, RhiError>> {
235
+ const existing = deviceCache.get(device);
236
+ if (existing !== undefined) return ok(existing);
237
+
238
+ const moduleRes = await asyncCreateShaderModule(device, {
239
+ code: MIPMAP_WGSL,
240
+ label: 'mipmap-wgsl',
241
+ });
242
+ if (!moduleRes.ok) return moduleRes;
243
+
244
+ const samplerRes = device.createSampler({
245
+ magFilter: 'linear',
246
+ minFilter: 'linear',
247
+ mipmapFilter: 'linear',
248
+ });
249
+ if (!samplerRes.ok) return samplerRes;
250
+
251
+ // GPUShaderStage.FRAGMENT = 0x2 spec constant; embedded literally to keep
252
+ // the utility math-free (charter F2 minimal surface; spec value is stable).
253
+ const FRAGMENT_STAGE = 0x2;
254
+ const layoutRes = device.createBindGroupLayout({
255
+ label: 'mipmap-bgl',
256
+ entries: [
257
+ { binding: 0, visibility: FRAGMENT_STAGE, sampler: { type: 'filtering' } },
258
+ {
259
+ binding: 1,
260
+ visibility: FRAGMENT_STAGE,
261
+ texture: { sampleType: 'float', viewDimension: '2d' },
262
+ },
263
+ ],
264
+ });
265
+ if (!layoutRes.ok) return layoutRes;
266
+
267
+ const cache: MipmapPipelineCache = {
268
+ sampler: samplerRes.value,
269
+ module: moduleRes.value,
270
+ layout: layoutRes.value,
271
+ pipelines: new Map(),
272
+ };
273
+ deviceCache.set(device, cache);
274
+ return ok(cache);
275
+ }
276
+
277
+ /**
278
+ * Extended device shape used by `generateMipmaps` (call-site T-M3-05).
279
+ * Adds command-encoder + bind-group + texture-view + queue surfaces beyond
280
+ * the cache-only `MipmapDevice` so the pipeline-cache unit tests still
281
+ * compile against the smaller surface.
282
+ */
283
+ export type MipmapBlitDevice = MipmapDevice & {
284
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
285
+ createTexture(desc: any): Result<any, any>;
286
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
287
+ createBindGroup(desc: any): Result<any, any>;
288
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
289
+ createTextureView(texture: any, desc: any): Result<any, any>;
290
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
291
+ createCommandEncoder(desc?: any): Result<any, any>;
292
+ readonly queue: {
293
+ // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
294
+ submit(buffers: ReadonlyArray<any>): Result<void, any>;
295
+ };
296
+ };
297
+
298
+ function recordMipmapBlit<E>(
299
+ device: MipmapBlitDevice,
300
+ // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
301
+ texture: any,
302
+ levels: number,
303
+ // biome-ignore lint/suspicious/noExplicitAny: opaque GPU pipeline handle
304
+ pipeline: any,
305
+ cache: MipmapPipelineCache,
306
+ ): Result<void, E> {
307
+ const encoderRes = device.createCommandEncoder({ label: 'mipmap-encoder' });
308
+ if (!encoderRes.ok) return encoderRes;
309
+ const encoder = encoderRes.value;
310
+
311
+ for (let i = 1; i < levels; i++) {
312
+ const srcViewRes = device.createTextureView(texture, {
313
+ baseMipLevel: i - 1,
314
+ mipLevelCount: 1,
315
+ dimension: '2d',
316
+ });
317
+ if (!srcViewRes.ok) return srcViewRes;
318
+
319
+ const dstViewRes = device.createTextureView(texture, {
320
+ baseMipLevel: i,
321
+ mipLevelCount: 1,
322
+ dimension: '2d',
323
+ });
324
+ if (!dstViewRes.ok) return dstViewRes;
325
+
326
+ const bindGroupRes = device.createBindGroup({
327
+ label: `mipmap-bg-${i}`,
328
+ layout: cache.layout,
329
+ entries: [
330
+ { binding: 0, resource: { kind: 'sampler', value: cache.sampler } },
331
+ { binding: 1, resource: { kind: 'textureView', value: srcViewRes.value } },
332
+ ],
333
+ });
334
+ if (!bindGroupRes.ok) return bindGroupRes;
335
+
336
+ const pass = encoder.beginRenderPass({
337
+ label: `mipmap-pass-${i}`,
338
+ colorAttachments: [
339
+ {
340
+ view: dstViewRes.value,
341
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
342
+ loadOp: 'clear',
343
+ storeOp: 'store',
344
+ },
345
+ ],
346
+ });
347
+ pass.setPipeline(pipeline);
348
+ pass.setBindGroup(0, bindGroupRes.value);
349
+ pass.draw(3, 1, 0, 0);
350
+ pass.end();
351
+ }
352
+
353
+ const finishRes = encoder.finish();
354
+ if (!finishRes.ok) return finishRes;
355
+ return device.queue.submit([finishRes.value]);
356
+ }
357
+
358
+ /**
359
+ * Per-mip render-pass blit-loop (research F-1 SSOT main template).
360
+ *
361
+ * For mip levels 1..levels-1: source view = previous mip {baseMipLevel:i-1,
362
+ * mipLevelCount:1}; destination view = current mip {baseMipLevel:i,
363
+ * mipLevelCount:1, usage: RENDER_ATTACHMENT}; oversized-triangle pipeline
364
+ * draws 3 vertices, fragment shader samples source via linear sampler.
365
+ *
366
+ * Spec sRGB correctness (F-3): when format is `*-srgb`, both views inherit
367
+ * the sRGB encoding so hardware decode -> bilinear in linear -> encode back
368
+ * happens automatically; no special-casing required.
369
+ *
370
+ * `levels` defaults to `numMipLevels({ width, height })`. AI users that
371
+ * already created the texture with explicit `mipLevelCount` should pass the
372
+ * same value here.
373
+ */
374
+ export async function generateMipmaps(
375
+ device: MipmapBlitDevice,
376
+ // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
377
+ texture: any,
378
+ descriptor: {
379
+ readonly format: GPUTextureFormat;
380
+ readonly width: number;
381
+ readonly height: number;
382
+ readonly levels?: number;
383
+ },
384
+ asyncCreateShaderModule: MipmapShaderModuleFactory,
385
+ ): Promise<Result<void, unknown>> {
386
+ const levels = descriptor.levels ?? numMipLevels(descriptor);
387
+ if (levels <= 1) return ok(undefined);
388
+
389
+ const pipelineRes = await getOrCreateMipmapPipeline(
390
+ device,
391
+ descriptor.format,
392
+ asyncCreateShaderModule,
393
+ );
394
+ if (!pipelineRes.ok) return pipelineRes;
395
+ const cache = deviceCache.get(device);
396
+ if (cache === undefined) {
397
+ return err({ code: 'webgpu-runtime-error', message: 'mipmap cache missing after create' });
398
+ }
399
+ return recordMipmapBlit<unknown>(device, texture, levels, pipelineRes.value, cache);
400
+ }
401
+
402
+ /**
403
+ * Synchronous sibling of `generateMipmaps` (feat-20260601-gpu-resource-store
404
+ * -extraction D-9). Identical per-mip blit loop, but reads the per-format
405
+ * pipeline + shared sampler / layout from the prewarmed device cache instead
406
+ * of building them. The pipeline MUST already be present (via a prior
407
+ * `getOrCreateMipmapPipeline` / `prewarmMipmapPipeline` async call) -- an
408
+ * un-prewarmed device or format returns a structured `RhiError` rather than
409
+ * awaiting a build, so the synchronous `draw(world)` frame contract is never
410
+ * broken. The async build itself does not affect the written bytes (same
411
+ * shader, same encoder sequence), so prewarm + sync blit reproduces the
412
+ * pre-extraction async `generateMipmaps` byte-for-byte (D-9 sub-contract 5).
413
+ */
414
+ export function blitMipmapsSync(
415
+ device: MipmapBlitDevice,
416
+ // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
417
+ texture: any,
418
+ descriptor: {
419
+ readonly format: GPUTextureFormat;
420
+ readonly width: number;
421
+ readonly height: number;
422
+ readonly levels?: number;
423
+ },
424
+ ): Result<void, RhiError> {
425
+ const levels = descriptor.levels ?? numMipLevels(descriptor);
426
+ if (levels <= 1) return ok(undefined);
427
+
428
+ const cache = deviceCache.get(device);
429
+ if (cache === undefined) {
430
+ return err(
431
+ new RhiError({
432
+ code: 'rhi-not-available',
433
+ expected: 'mipmap pipeline cache prewarmed for this device before blitMipmapsSync',
434
+ hint: 'call prewarmMipmapPipeline(device, formats) at renderer.ready before the synchronous record-stage mipmap blit',
435
+ }),
436
+ );
437
+ }
438
+ const pipeline = cache.pipelines.get(descriptor.format);
439
+ if (pipeline === undefined) {
440
+ return err(
441
+ new RhiError({
442
+ code: 'rhi-not-available',
443
+ expected: `mipmap pipeline for format ${descriptor.format} prewarmed`,
444
+ hint: `format ${descriptor.format} was not prewarmed; add it to the prewarmMipmapPipeline format list at renderer.ready`,
445
+ }),
446
+ );
447
+ }
448
+
449
+ return recordMipmapBlit<RhiError>(device, texture, levels, pipeline, cache);
450
+ }