@forgeax/engine-render 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (918) hide show
  1. package/README.md +130 -641
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +2 -0
  4. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +2 -0
  6. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +2 -0
  8. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +1 -0
  9. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -1
  10. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +6 -0
  11. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -1
  12. package/dist/{gpu-driven/view-reference.d.ts → __tests__/gpu-driven-view-reference.d.ts} +4 -4
  13. package/dist/__tests__/gpu-driven-view-reference.d.ts.map +1 -0
  14. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -1
  15. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +2 -0
  16. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +1 -0
  17. package/dist/__tests__/morph-feature-plan.unit.test.d.ts +2 -0
  18. package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +1 -0
  19. package/dist/__tests__/render-feature-plan.test-d.d.ts +2 -0
  20. package/dist/__tests__/render-feature-plan.test-d.d.ts.map +1 -0
  21. package/dist/__tests__/render-feature-plan.unit.test.d.ts +2 -0
  22. package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +1 -0
  23. package/dist/__tests__/ssao-config.unit.test.d.ts +2 -0
  24. package/dist/__tests__/ssao-config.unit.test.d.ts.map +1 -0
  25. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +2 -0
  26. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +1 -0
  27. package/dist/__tests__/standard-descriptors.test-d.d.ts +2 -0
  28. package/dist/__tests__/standard-descriptors.test-d.d.ts.map +1 -0
  29. package/dist/__tests__/standard-pipeline.dawn.test.d.ts +2 -0
  30. package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +1 -0
  31. package/dist/__tests__/standard-pipeline.integration.test.d.ts +2 -0
  32. package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +1 -0
  33. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +2 -0
  34. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +1 -0
  35. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +2 -0
  36. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +1 -0
  37. package/dist/{renderer/backend-selection.d.ts → assembly/backend-contract.d.ts} +15 -4
  38. package/dist/assembly/backend-contract.d.ts.map +1 -0
  39. package/dist/assembly/factory.d.ts +207 -0
  40. package/dist/assembly/factory.d.ts.map +1 -0
  41. package/dist/assembly/host-contract.d.ts +84 -0
  42. package/dist/assembly/host-contract.d.ts.map +1 -0
  43. package/dist/{renderer → assembly}/material/assembly.d.ts +0 -1
  44. package/dist/{renderer → assembly}/material/assembly.d.ts.map +1 -1
  45. package/dist/assembly/material/pipeline-projection.d.ts +30 -0
  46. package/dist/assembly/material/pipeline-projection.d.ts.map +1 -0
  47. package/dist/authoring.d.ts +10 -11
  48. package/dist/authoring.d.ts.map +1 -1
  49. package/dist/authoring.mjs +25 -3
  50. package/dist/authoring.mjs.map +1 -1
  51. package/dist/chunk-CQXZC5XI.mjs +182 -0
  52. package/dist/chunk-CQXZC5XI.mjs.map +1 -0
  53. package/dist/chunk-QC4VW3XJ.mjs +79 -0
  54. package/dist/chunk-QC4VW3XJ.mjs.map +1 -0
  55. package/dist/chunk-SCRSAK5B.mjs +480 -0
  56. package/dist/chunk-SCRSAK5B.mjs.map +1 -0
  57. package/dist/components/__tests__/schema-contract.unit.test.d.ts +2 -0
  58. package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +1 -0
  59. package/dist/components/glyph-text.d.ts +1 -1
  60. package/dist/components/glyph-text.d.ts.map +1 -1
  61. package/dist/components/index.d.ts +2 -2
  62. package/dist/components/index.d.ts.map +1 -1
  63. package/dist/components/instances.d.ts +1 -1
  64. package/dist/components/instances.d.ts.map +1 -1
  65. package/dist/components/light-helpers.d.ts +1 -1
  66. package/dist/components/light-helpers.d.ts.map +1 -1
  67. package/dist/components/mesh-filter.d.ts +7 -7
  68. package/dist/components/mesh-filter.d.ts.map +1 -1
  69. package/dist/components/scene-instance.d.ts +11 -11
  70. package/dist/components/skybox-background.d.ts +56 -0
  71. package/dist/components/skybox-background.d.ts.map +1 -0
  72. package/dist/components/skylight.d.ts +55 -0
  73. package/dist/components/skylight.d.ts.map +1 -0
  74. package/dist/components/sprite-animation.d.ts +2 -3
  75. package/dist/components/sprite-animation.d.ts.map +1 -1
  76. package/dist/components/sprite-instances.d.ts +1 -1
  77. package/dist/components/sprite-playback-mode.d.ts +6 -1
  78. package/dist/components/sprite-playback-mode.d.ts.map +1 -1
  79. package/dist/components/sprite-region-override.d.ts +2 -3
  80. package/dist/components/sprite-region-override.d.ts.map +1 -1
  81. package/dist/components/tile-layer.d.ts +5 -0
  82. package/dist/components/tile-layer.d.ts.map +1 -1
  83. package/dist/construct-renderer.d.ts +18 -1
  84. package/dist/construct-renderer.d.ts.map +1 -1
  85. package/dist/construct-renderer.mjs +27008 -4
  86. package/dist/construct-renderer.mjs.map +1 -1
  87. package/dist/debug-draw-glue.d.ts +2 -53
  88. package/dist/debug-draw-glue.d.ts.map +1 -1
  89. package/dist/device/device-scope.d.ts +83 -0
  90. package/dist/device/device-scope.d.ts.map +1 -0
  91. package/dist/{gpu-resource-store.d.ts → device/gpu-residency.d.ts} +91 -48
  92. package/dist/device/gpu-residency.d.ts.map +1 -0
  93. package/dist/device/resource-types.d.ts +8 -0
  94. package/dist/device/resource-types.d.ts.map +1 -0
  95. package/dist/engine-metrics.d.ts +2 -2
  96. package/dist/engine-metrics.d.ts.map +1 -1
  97. package/dist/errors/recover.d.ts +28 -1
  98. package/dist/errors/recover.d.ts.map +1 -1
  99. package/dist/errors/render.d.ts +249 -258
  100. package/dist/errors/render.d.ts.map +1 -1
  101. package/dist/features/fullscreen.d.ts +19 -0
  102. package/dist/features/fullscreen.d.ts.map +1 -0
  103. package/dist/features/host.d.ts +32 -26
  104. package/dist/features/host.d.ts.map +1 -1
  105. package/dist/features/morph/morph-feature.d.ts +39 -5
  106. package/dist/features/morph/morph-feature.d.ts.map +1 -1
  107. package/dist/features/plan.d.ts +182 -0
  108. package/dist/features/plan.d.ts.map +1 -0
  109. package/dist/features/prepared-gpu-work.d.ts +21 -17
  110. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  111. package/dist/features/prepared-graphics-store.d.ts +2 -1
  112. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  113. package/dist/features/prepared-graphics.d.ts +11 -21
  114. package/dist/features/prepared-graphics.d.ts.map +1 -1
  115. package/dist/features/render-graph-compute.d.ts +2 -2
  116. package/dist/features/render-graph-compute.d.ts.map +1 -1
  117. package/dist/features/render-graph-contribution.d.ts +4 -7
  118. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  119. package/dist/features/render-graph-raster.d.ts +2 -2
  120. package/dist/features/render-graph-raster.d.ts.map +1 -1
  121. package/dist/features/render-graph-resources.d.ts +6 -6
  122. package/dist/features/render-graph-resources.d.ts.map +1 -1
  123. package/dist/features/targets.d.ts +5 -5
  124. package/dist/features/targets.d.ts.map +1 -1
  125. package/dist/features/types.d.ts +11 -190
  126. package/dist/features/types.d.ts.map +1 -1
  127. package/dist/features/vocabulary.d.ts +14 -0
  128. package/dist/features/vocabulary.d.ts.map +1 -0
  129. package/dist/fullscreen-post-process-pass.d.ts +4 -25
  130. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  131. package/dist/glyph-text-layout-system.d.ts +3 -2
  132. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  133. package/dist/gpu-driven/batch-topology.d.ts +6 -75
  134. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  135. package/dist/gpu-driven/production-raster.d.ts +13 -112
  136. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  137. package/dist/gpu-driven/view-gpu.d.ts +10 -66
  138. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  139. package/dist/gpu-resource.d.ts +3 -3
  140. package/dist/gpu-scene-schema.d.ts +1 -94
  141. package/dist/gpu-scene-schema.d.ts.map +1 -1
  142. package/dist/gpu-scene.d.ts +7 -33
  143. package/dist/gpu-scene.d.ts.map +1 -1
  144. package/dist/hdrp-buffers.d.ts +3 -53
  145. package/dist/hdrp-buffers.d.ts.map +1 -1
  146. package/dist/ibl/IblPipelineCache.d.ts +74 -180
  147. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  148. package/dist/ibl/skylight-bind-group.d.ts +1 -1
  149. package/dist/index.d.ts +13 -43
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.mjs +165 -3
  152. package/dist/index.mjs.map +1 -1
  153. package/dist/inspection-types.d.ts +69 -0
  154. package/dist/inspection-types.d.ts.map +1 -0
  155. package/dist/instance-buffer-cache.d.ts +1 -4
  156. package/dist/instance-buffer-cache.d.ts.map +1 -1
  157. package/dist/internal.d.ts +1 -73
  158. package/dist/internal.d.ts.map +1 -1
  159. package/dist/internal.mjs +1 -43
  160. package/dist/internal.mjs.map +1 -1
  161. package/dist/lifecycle.d.ts +36 -2
  162. package/dist/lifecycle.d.ts.map +1 -1
  163. package/dist/materials.d.ts +0 -3
  164. package/dist/materials.d.ts.map +1 -1
  165. package/dist/mesh-material-bindings.d.ts +0 -1
  166. package/dist/mesh-material-bindings.d.ts.map +1 -1
  167. package/dist/pbr-pipeline.d.ts +75 -29
  168. package/dist/pbr-pipeline.d.ts.map +1 -1
  169. package/dist/pipeline/standard-forward-lane.d.ts +5 -0
  170. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -0
  171. package/dist/pipeline/standard-pipeline.d.ts +31 -0
  172. package/dist/pipeline/standard-pipeline.d.ts.map +1 -0
  173. package/dist/pipeline/standard-profile.d.ts +40 -0
  174. package/dist/pipeline/standard-profile.d.ts.map +1 -0
  175. package/dist/pipeline-builder.d.ts +3 -3
  176. package/dist/pipeline-builder.d.ts.map +1 -1
  177. package/dist/pipeline-spec-types.d.ts +24 -0
  178. package/dist/pipeline-spec-types.d.ts.map +1 -0
  179. package/dist/pipeline-spec.d.ts +16 -155
  180. package/dist/pipeline-spec.d.ts.map +1 -1
  181. package/dist/plugin.d.ts +1 -14
  182. package/dist/plugin.d.ts.map +1 -1
  183. package/dist/post-process-errors.d.ts +18 -18
  184. package/dist/post-process-errors.d.ts.map +1 -1
  185. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -5
  186. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  187. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +2 -0
  188. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +1 -0
  189. package/dist/record/frame-lighting.d.ts +7 -73
  190. package/dist/record/frame-lighting.d.ts.map +1 -1
  191. package/dist/record/frame-snapshot.d.ts +44 -44
  192. package/dist/record/frame-snapshot.d.ts.map +1 -1
  193. package/dist/record/frame-targets.d.ts +5 -38
  194. package/dist/record/frame-targets.d.ts.map +1 -1
  195. package/dist/record/frame.d.ts +64 -52
  196. package/dist/record/frame.d.ts.map +1 -1
  197. package/dist/record/helpers.d.ts +3 -2
  198. package/dist/record/helpers.d.ts.map +1 -1
  199. package/dist/record/main-pass-geometry.d.ts +5 -4
  200. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  201. package/dist/record/main-pass-material.d.ts +90 -59
  202. package/dist/record/main-pass-material.d.ts.map +1 -1
  203. package/dist/record/main-pass-sprite-draws.d.ts +28 -3
  204. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  205. package/dist/record/main-pass.d.ts +1 -1
  206. package/dist/record/main-pass.d.ts.map +1 -1
  207. package/dist/record/mesh-ssbo.d.ts +276 -3
  208. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  209. package/dist/record/render-context.d.ts +235 -0
  210. package/dist/record/render-context.d.ts.map +1 -0
  211. package/dist/record/shadow-pass.d.ts +15 -88
  212. package/dist/record/shadow-pass.d.ts.map +1 -1
  213. package/dist/record/skybox-post-pass.d.ts +1 -1
  214. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  215. package/dist/record/typed-frame-graph.d.ts +29 -7
  216. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  217. package/dist/record/view-ubo.d.ts +3 -3
  218. package/dist/record/view-ubo.d.ts.map +1 -1
  219. package/dist/render-contract.d.ts +416 -0
  220. package/dist/render-contract.d.ts.map +1 -0
  221. package/dist/render-data.d.ts.map +1 -1
  222. package/dist/render-graph-primitives.d.ts +14 -362
  223. package/dist/render-graph-primitives.d.ts.map +1 -1
  224. package/dist/render-pipeline.d.ts +25 -64
  225. package/dist/render-pipeline.d.ts.map +1 -1
  226. package/dist/render-system-extract.d.ts +107 -338
  227. package/dist/render-system-extract.d.ts.map +1 -1
  228. package/dist/render-system.d.ts +21 -713
  229. package/dist/render-system.d.ts.map +1 -1
  230. package/dist/scene/render-scene-types.d.ts +58 -0
  231. package/dist/scene/render-scene-types.d.ts.map +1 -0
  232. package/dist/scene/render-scene.d.ts +105 -0
  233. package/dist/scene/render-scene.d.ts.map +1 -0
  234. package/dist/scene-instances/post-spawn-resolve-joints.d.ts +2 -5
  235. package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -1
  236. package/dist/ssao-buffers.d.ts +22 -3
  237. package/dist/ssao-buffers.d.ts.map +1 -1
  238. package/dist/systems/skin-palette-allocator.d.ts +2 -4
  239. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  240. package/dist/systems/skin-palette-types.d.ts +14 -0
  241. package/dist/systems/skin-palette-types.d.ts.map +1 -0
  242. package/dist/systems/transparent-sort-config.d.ts +23 -2
  243. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  244. package/dist/tilemap-chunk-extract-system.d.ts +4 -3
  245. package/dist/tilemap-chunk-extract-system.d.ts.map +1 -1
  246. package/dist/typed-render-graph-primitives.d.ts +2 -4
  247. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  248. package/dist/typed-shadow-passes.d.ts +3 -3
  249. package/dist/typed-shadow-passes.d.ts.map +1 -1
  250. package/package.json +23 -23
  251. package/src/__tests__/backend-selection-rhi-capture.test-d.ts +1 -1
  252. package/src/__tests__/callback-inference.test-d.ts +6 -3
  253. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -4
  254. package/src/__tests__/construct-renderer-identity.unit.test.ts +15 -2
  255. package/src/__tests__/device-scope-lifecycle.unit.test.ts +151 -0
  256. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +42 -0
  257. package/src/__tests__/direct-extract-consumer-contract.test-d.ts +8 -19
  258. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -13
  259. package/src/__tests__/factory-contract.integration.test.ts +158 -209
  260. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +1 -4
  261. package/src/__tests__/frame-observation.contract.test.ts +17 -8
  262. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +32 -0
  263. package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +6 -11
  264. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +9 -16
  265. package/src/__tests__/gpu-driven-production.integration.test.ts +32 -205
  266. package/src/__tests__/gpu-driven-scaling.unit.test.ts +4 -7
  267. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +87 -8
  268. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +170 -8
  269. package/src/{gpu-driven/view-reference.ts → __tests__/gpu-driven-view-reference.ts} +4 -3
  270. package/src/__tests__/gpu-driven-view.browser.test.ts +12 -1
  271. package/src/__tests__/gpu-driven-view.dawn.test.ts +12 -1
  272. package/src/__tests__/gpu-driven-view.unit.test.ts +11 -267
  273. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +3 -3
  274. package/src/__tests__/gpu-scene-render-graph-gpu.ts +2 -3
  275. package/src/__tests__/gpu-scene.dawn.test.ts +2 -6
  276. package/src/__tests__/gpu-scene.unit.test.ts +10 -24
  277. package/src/__tests__/ibl/face-uniforms.unit.test.ts +46 -0
  278. package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +34 -66
  279. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -5
  280. package/src/__tests__/light-buffer-layout.unit.test.ts +4 -49
  281. package/src/__tests__/material-cooked-projection.unit.test.ts +2 -51
  282. package/src/__tests__/material-snapshot-mutation.integration.test.ts +10 -15
  283. package/src/__tests__/material-static-dynamic-route.unit.test.ts +4 -23
  284. package/src/__tests__/material-stride-surface.unit.test.ts +2 -2
  285. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +7 -8
  286. package/src/__tests__/morph-culling-reentry.dawn.test.ts +29 -38
  287. package/src/__tests__/morph-feature-gpu-work.integration.test.ts +45 -43
  288. package/src/__tests__/morph-feature-plan.unit.test.ts +53 -0
  289. package/src/__tests__/morph-visual-evidence.integration.test.ts +47 -33
  290. package/src/__tests__/nineslice-handle-surface.unit.test.ts +11 -6
  291. package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -13
  292. package/src/__tests__/prepared-graphics-capability.unit.test.ts +6 -81
  293. package/src/__tests__/prepared-graphics-generation.unit.test.ts +3 -17
  294. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +6 -74
  295. package/src/__tests__/prepared-graphics-resolver.unit.test.ts +1 -3
  296. package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +2 -2
  297. package/src/__tests__/profiler-default-off.test.ts +1 -1
  298. package/src/__tests__/profiler-phase-catalog.test.ts +2 -2
  299. package/src/__tests__/profiler-phase-integration.test.ts +5 -13
  300. package/src/__tests__/profiler-phase-token.test.ts +3 -8
  301. package/src/__tests__/public-surface.test-d.ts +65 -3
  302. package/src/__tests__/recover-error-policy-owner.unit.test.ts +1 -21
  303. package/src/__tests__/render-error-code-owner.test-d.ts +18 -23
  304. package/src/__tests__/render-error-detail-ssot.test-d.ts +0 -95
  305. package/src/__tests__/render-error-exhaustive.test-d.ts +8 -1
  306. package/src/__tests__/render-feature-assembly.test-d.ts +8 -16
  307. package/src/__tests__/render-feature-context.test-d.ts +16 -40
  308. package/src/__tests__/render-feature-contract.test-d.ts +9 -18
  309. package/src/__tests__/render-feature-diagnostics.unit.test.ts +6 -8
  310. package/src/__tests__/render-feature-docs.unit.test.ts +8 -7
  311. package/src/__tests__/render-feature-gpu-work.integration.test.ts +14 -75
  312. package/src/__tests__/render-feature-plan.test-d.ts +161 -0
  313. package/src/__tests__/render-feature-plan.unit.test.ts +170 -0
  314. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +8 -19
  315. package/src/__tests__/render-feature-prepared-errors.test-d.ts +3 -6
  316. package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +10 -71
  317. package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +11 -61
  318. package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +20 -43
  319. package/src/__tests__/render-feature-prepared-graphics.test-d.ts +56 -78
  320. package/src/__tests__/render-feature-public-surface.test-d.ts +6 -24
  321. package/src/__tests__/render-feature-retry.unit.test.ts +7 -48
  322. package/src/__tests__/render-feature-scheduling.unit.test.ts +15 -27
  323. package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +0 -9
  324. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +19 -109
  325. package/src/__tests__/render-feature-visibility-context.test-d.ts +1 -6
  326. package/src/__tests__/render-feature-visibility-context.unit.test.ts +1 -2
  327. package/src/__tests__/render-feature-zero-work.unit.test.ts +14 -20
  328. package/src/__tests__/render-scene-projection.unit.test.ts +73 -111
  329. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +44 -95
  330. package/src/__tests__/renderer-lifecycle.integration.test.ts +49 -1
  331. package/src/__tests__/shader-stage-surface.unit.test.ts +14 -9
  332. package/src/__tests__/ssao-config.unit.test.ts +36 -0
  333. package/src/__tests__/ssao-kernel-surface.unit.test.ts +4 -5
  334. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +28 -0
  335. package/src/__tests__/standard-descriptors.test-d.ts +22 -0
  336. package/src/__tests__/standard-pipeline.dawn.test.ts +61 -0
  337. package/src/__tests__/standard-pipeline.integration.test.ts +124 -0
  338. package/src/__tests__/standard-profile-oracle.unit.test.ts +62 -0
  339. package/src/__tests__/surface-configuration.unit.test.ts +1 -2
  340. package/src/__tests__/texture-usage-surface.unit.test.ts +3 -6
  341. package/src/__tests__/typed-pipeline-topology.unit.test.ts +40 -206
  342. package/src/__tests__/urp-pipeline-targets.unit.test.ts +1 -11
  343. package/src/__tests__/urp-pipeline.unit.test.ts +2 -2
  344. package/src/__tests__/vertex-color-pipeline.unit.test.ts +10 -2
  345. package/src/__tests__/vertex-layout-consumer.unit.test.ts +1 -1
  346. package/src/__tests__/visibility-diagnostics.integration.test.ts +7 -11
  347. package/src/__tests__/visibility-extract.integration.test.ts +20 -11
  348. package/src/__tests__/visibility-extract.perf.test.ts +66 -32
  349. package/src/__tests__/visibility-feature-dedup.integration.test.ts +2 -4
  350. package/src/__tests__/visibility-hidden-report.unit.test.ts +1 -2
  351. package/src/__tests__/visibility-instances.integration.test.ts +7 -11
  352. package/src/__tests__/visibility-multi-world.integration.test.ts +38 -6
  353. package/src/__tests__/visibility-producer-matrix.integration.test.ts +18 -41
  354. package/src/__tests__/visibility-public-surface.test-d.ts +10 -9
  355. package/src/__tests__/visibility-resolver.unit.test.ts +2 -1
  356. package/src/__tests__/visibility-resource-short-circuit.unit.test.ts +80 -0
  357. package/src/__tests__/visibility-snapshot-contract.unit.test.ts +2 -1
  358. package/src/__tests__/vocabulary.unit.test.ts +1 -1
  359. package/src/assembly/backend-contract.ts +57 -0
  360. package/src/{renderer/renderer-factory.ts → assembly/factory.ts} +1663 -2690
  361. package/src/assembly/host-contract.ts +101 -0
  362. package/src/{renderer → assembly}/material/assembly.ts +0 -2
  363. package/src/assembly/material/pipeline-projection.ts +63 -0
  364. package/src/authoring.ts +14 -22
  365. package/src/components/__tests__/schema-contract.unit.test.ts +10 -0
  366. package/src/components/glyph-text.ts +3 -2
  367. package/src/components/index.ts +7 -9
  368. package/src/components/instances.ts +4 -5
  369. package/src/components/light-helpers.ts +1 -1
  370. package/src/components/mesh-filter.ts +11 -10
  371. package/src/components/post-process-params.ts +3 -3
  372. package/src/components/scene-instance.ts +12 -12
  373. package/src/components/skybox-background.ts +103 -0
  374. package/src/components/{environment.ts → skylight.ts} +2 -110
  375. package/src/components/sprite-animation.ts +3 -4
  376. package/src/components/sprite-instances.ts +2 -2
  377. package/src/components/sprite-playback-mode.ts +7 -1
  378. package/src/components/sprite-region-override.ts +2 -3
  379. package/src/components/tile-layer.ts +8 -2
  380. package/src/construct-renderer.ts +67 -4
  381. package/src/debug-draw-glue.ts +8 -129
  382. package/src/device/device-scope.ts +306 -0
  383. package/src/{gpu-resource-store.ts → device/gpu-residency.ts} +540 -639
  384. package/src/device/resource-types.ts +20 -0
  385. package/src/engine-metrics.ts +12 -19
  386. package/src/errors/recover.ts +45 -3
  387. package/src/errors/render.ts +357 -460
  388. package/src/features/fullscreen.ts +46 -0
  389. package/src/features/host.ts +575 -309
  390. package/src/features/morph/morph-feature.ts +195 -105
  391. package/src/features/plan.ts +447 -0
  392. package/src/features/prepared-gpu-work.ts +72 -15
  393. package/src/features/prepared-graphics-store.ts +20 -6
  394. package/src/features/prepared-graphics.ts +12 -71
  395. package/src/features/render-graph-compute.ts +14 -5
  396. package/src/features/render-graph-contribution.ts +43 -63
  397. package/src/features/render-graph-raster.ts +11 -6
  398. package/src/features/render-graph-resources.ts +25 -23
  399. package/src/features/targets.ts +8 -21
  400. package/src/features/types.ts +39 -227
  401. package/src/features/vocabulary.ts +24 -0
  402. package/src/fullscreen-post-process-pass.ts +14 -57
  403. package/src/glyph-text-layout-system.ts +70 -96
  404. package/src/gpu-driven/batch-topology.ts +24 -202
  405. package/src/gpu-driven/production-raster.ts +93 -1108
  406. package/src/gpu-driven/view-gpu.ts +46 -192
  407. package/src/gpu-resource.ts +4 -4
  408. package/src/gpu-scene-schema.ts +11 -99
  409. package/src/gpu-scene.ts +231 -364
  410. package/src/hdrp-buffers.ts +12 -221
  411. package/src/ibl/IblPipelineCache.ts +471 -711
  412. package/src/ibl/skylight-bind-group.ts +4 -4
  413. package/src/index.ts +58 -217
  414. package/src/inspection-types.ts +76 -0
  415. package/src/instance-buffer-cache.ts +1 -29
  416. package/src/internal.ts +6 -85
  417. package/src/lifecycle.ts +64 -19
  418. package/src/materials.ts +7 -29
  419. package/src/mesh-material-bindings.ts +0 -1
  420. package/src/pbr-pipeline.ts +434 -53
  421. package/src/{urp-pipeline.ts → pipeline/standard-forward-lane.ts} +38 -149
  422. package/src/{hdrp-pipeline.ts → pipeline/standard-pipeline.ts} +221 -191
  423. package/src/pipeline/standard-profile.ts +65 -0
  424. package/src/pipeline-builder.ts +60 -64
  425. package/src/pipeline-spec-types.ts +28 -0
  426. package/src/pipeline-spec.ts +72 -609
  427. package/src/plugin.ts +3 -75
  428. package/src/post-process-errors.ts +31 -33
  429. package/src/prepare/prepared-graphics-resolver.ts +5 -4
  430. package/src/record/__tests__/material-slot-plan.unit.test.ts +1 -33
  431. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +41 -0
  432. package/src/record/__tests__/video-texture-view.unit.test.ts +1 -1
  433. package/src/record/frame-lighting.ts +74 -195
  434. package/src/record/frame-snapshot.ts +45 -60
  435. package/src/record/frame-targets.ts +8 -243
  436. package/src/record/frame.ts +509 -796
  437. package/src/record/helpers.ts +6 -11
  438. package/src/record/main-pass-geometry.ts +106 -147
  439. package/src/record/main-pass-material.ts +517 -190
  440. package/src/record/main-pass-sprite-draws.ts +198 -117
  441. package/src/record/main-pass.ts +162 -143
  442. package/src/record/mesh-ssbo.ts +517 -38
  443. package/src/record/render-context.ts +364 -0
  444. package/src/record/shadow-pass.ts +138 -1838
  445. package/src/record/skybox-post-pass.ts +12 -14
  446. package/src/record/typed-frame-graph.ts +215 -254
  447. package/src/record/view-ubo.ts +6 -37
  448. package/src/render-contract.ts +571 -0
  449. package/src/render-data.ts +3 -3
  450. package/src/render-graph-primitives.ts +76 -851
  451. package/src/render-pipeline.ts +104 -188
  452. package/src/render-system-extract.ts +1048 -1729
  453. package/src/render-system.ts +330 -1957
  454. package/src/scene/render-scene-types.ts +64 -0
  455. package/src/scene/render-scene.ts +1032 -0
  456. package/src/scene-instances/post-spawn-resolve-joints.ts +4 -7
  457. package/src/ssao-buffers.ts +167 -8
  458. package/src/systems/skin-palette-allocator.ts +21 -64
  459. package/src/systems/skin-palette-types.ts +14 -0
  460. package/src/systems/transparent-sort-config.ts +25 -3
  461. package/src/tilemap-chunk-extract-system.ts +53 -70
  462. package/src/typed-render-graph-primitives.ts +35 -24
  463. package/src/typed-shadow-passes.ts +26 -56
  464. package/dist/__tests__/analytic-sky-contract.test.d.ts +0 -2
  465. package/dist/__tests__/analytic-sky-contract.test.d.ts.map +0 -1
  466. package/dist/__tests__/analytic-sky-falsify.test.d.ts +0 -2
  467. package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +0 -1
  468. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +0 -2
  469. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +0 -1
  470. package/dist/__tests__/backend-selection.integration.test.d.ts +0 -2
  471. package/dist/__tests__/backend-selection.integration.test.d.ts.map +0 -1
  472. package/dist/__tests__/camera-temporal.test-d.d.ts +0 -2
  473. package/dist/__tests__/camera-temporal.test-d.d.ts.map +0 -1
  474. package/dist/__tests__/camera-temporal.test.d.ts +0 -2
  475. package/dist/__tests__/camera-temporal.test.d.ts.map +0 -1
  476. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +0 -2
  477. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +0 -1
  478. package/dist/__tests__/environment-inspection.test.d.ts +0 -2
  479. package/dist/__tests__/environment-inspection.test.d.ts.map +0 -1
  480. package/dist/__tests__/fog-ai-user.fixture.d.ts +0 -2
  481. package/dist/__tests__/fog-ai-user.fixture.d.ts.map +0 -1
  482. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +0 -16
  483. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +0 -1
  484. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +0 -2
  485. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +0 -1
  486. package/dist/__tests__/fog-inspection.integration.test.d.ts +0 -2
  487. package/dist/__tests__/fog-inspection.integration.test.d.ts.map +0 -1
  488. package/dist/__tests__/fog-owner-state.integration.test.d.ts +0 -2
  489. package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +0 -1
  490. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +0 -2
  491. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +0 -1
  492. package/dist/__tests__/fog-public-surface.integration.test.d.ts +0 -2
  493. package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +0 -1
  494. package/dist/__tests__/fog-ray-contract.integration.test.d.ts +0 -2
  495. package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +0 -1
  496. package/dist/__tests__/fog-source-absence.integration.test.d.ts +0 -2
  497. package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +0 -1
  498. package/dist/__tests__/fog-temporal.integration.test.d.ts +0 -2
  499. package/dist/__tests__/fog-temporal.integration.test.d.ts.map +0 -1
  500. package/dist/__tests__/frame-chain.unit.test.d.ts +0 -2
  501. package/dist/__tests__/frame-chain.unit.test.d.ts.map +0 -1
  502. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +0 -2
  503. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +0 -1
  504. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +0 -2
  505. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +0 -1
  506. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +0 -2
  507. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +0 -1
  508. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +0 -2
  509. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +0 -1
  510. package/dist/__tests__/material-derived-payload.integration.test.d.ts +0 -2
  511. package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +0 -1
  512. package/dist/__tests__/materials.unit.test.d.ts +0 -2
  513. package/dist/__tests__/materials.unit.test.d.ts.map +0 -1
  514. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +0 -2
  515. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +0 -1
  516. package/dist/__tests__/persistent-render-scene.unit.test.d.ts +0 -2
  517. package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +0 -1
  518. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +0 -2
  519. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +0 -1
  520. package/dist/__tests__/pipeline-pass-trace.test.d.ts +0 -2
  521. package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +0 -1
  522. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +0 -2
  523. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +0 -1
  524. package/dist/__tests__/plugin.test.d.ts +0 -2
  525. package/dist/__tests__/plugin.test.d.ts.map +0 -1
  526. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +0 -2
  527. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +0 -1
  528. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +0 -2
  529. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +0 -1
  530. package/dist/__tests__/public-pipeline-id.test-d.d.ts +0 -2
  531. package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +0 -1
  532. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +0 -2
  533. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +0 -1
  534. package/dist/__tests__/render-feature-construction.unit.test.d.ts +0 -2
  535. package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +0 -1
  536. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +0 -2
  537. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +0 -1
  538. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +0 -2
  539. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +0 -1
  540. package/dist/__tests__/render-feature-graph.integration.test.d.ts +0 -2
  541. package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +0 -1
  542. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +0 -2
  543. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +0 -1
  544. package/dist/__tests__/render-feature-order.unit.test.d.ts +0 -2
  545. package/dist/__tests__/render-feature-order.unit.test.d.ts.map +0 -1
  546. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +0 -2
  547. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +0 -1
  548. package/dist/__tests__/render-feature-staging.unit.test.d.ts +0 -2
  549. package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +0 -1
  550. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +0 -2
  551. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +0 -1
  552. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +0 -2
  553. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +0 -1
  554. package/dist/__tests__/sun-snapshot-consumers.test.d.ts +0 -2
  555. package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +0 -1
  556. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +0 -2
  557. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +0 -1
  558. package/dist/__tests__/taa-history-store.unit.test.d.ts +0 -2
  559. package/dist/__tests__/taa-history-store.unit.test.d.ts.map +0 -1
  560. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +0 -2
  561. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +0 -1
  562. package/dist/__tests__/temporal-inspection.unit.test.d.ts +0 -2
  563. package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +0 -1
  564. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +0 -2
  565. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +0 -1
  566. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +0 -2
  567. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +0 -1
  568. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +0 -2
  569. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +0 -1
  570. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +0 -2
  571. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +0 -1
  572. package/dist/chunk-DJIMFYX6.mjs +0 -1439
  573. package/dist/chunk-DJIMFYX6.mjs.map +0 -1
  574. package/dist/chunk-F2SIOCRM.mjs +0 -7423
  575. package/dist/chunk-F2SIOCRM.mjs.map +0 -1
  576. package/dist/chunk-JMSB7AR4.mjs +0 -3
  577. package/dist/chunk-JMSB7AR4.mjs.map +0 -1
  578. package/dist/chunk-KHI6K4C6.mjs +0 -659
  579. package/dist/chunk-KHI6K4C6.mjs.map +0 -1
  580. package/dist/chunk-PORZOL2V.mjs +0 -569
  581. package/dist/chunk-PORZOL2V.mjs.map +0 -1
  582. package/dist/chunk-UOJGMDGP.mjs +0 -29913
  583. package/dist/chunk-UOJGMDGP.mjs.map +0 -1
  584. package/dist/components/__tests__/atmosphere-red.test.d.ts +0 -2
  585. package/dist/components/__tests__/atmosphere-red.test.d.ts.map +0 -1
  586. package/dist/components/__tests__/atmosphere.test.d.ts +0 -2
  587. package/dist/components/__tests__/atmosphere.test.d.ts.map +0 -1
  588. package/dist/components/__tests__/fog.test-d.d.ts +0 -2
  589. package/dist/components/__tests__/fog.test-d.d.ts.map +0 -1
  590. package/dist/components/__tests__/fog.test.d.ts +0 -2
  591. package/dist/components/__tests__/fog.test.d.ts.map +0 -1
  592. package/dist/components/atmosphere.d.ts +0 -18
  593. package/dist/components/atmosphere.d.ts.map +0 -1
  594. package/dist/components/environment.d.ts +0 -115
  595. package/dist/components/environment.d.ts.map +0 -1
  596. package/dist/deferred-lighting.d.ts +0 -13
  597. package/dist/deferred-lighting.d.ts.map +0 -1
  598. package/dist/dynamic-texture-store.d.ts +0 -42
  599. package/dist/dynamic-texture-store.d.ts.map +0 -1
  600. package/dist/environment/__tests__/failure-injection.test.d.ts +0 -2
  601. package/dist/environment/__tests__/failure-injection.test.d.ts.map +0 -1
  602. package/dist/environment/__tests__/generation-red.test.d.ts +0 -2
  603. package/dist/environment/__tests__/generation-red.test.d.ts.map +0 -1
  604. package/dist/environment/__tests__/generation.test.d.ts +0 -2
  605. package/dist/environment/__tests__/generation.test.d.ts.map +0 -1
  606. package/dist/environment/__tests__/graph-transaction.test.d.ts +0 -2
  607. package/dist/environment/__tests__/graph-transaction.test.d.ts.map +0 -1
  608. package/dist/environment/__tests__/inspection-red.test.d.ts +0 -2
  609. package/dist/environment/__tests__/inspection-red.test.d.ts.map +0 -1
  610. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +0 -2
  611. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +0 -1
  612. package/dist/environment/__tests__/pipeline-topology.test.d.ts +0 -2
  613. package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +0 -1
  614. package/dist/environment/__tests__/readiness.test.d.ts +0 -2
  615. package/dist/environment/__tests__/readiness.test.d.ts.map +0 -1
  616. package/dist/environment/__tests__/recovery.test.d.ts +0 -2
  617. package/dist/environment/__tests__/recovery.test.d.ts.map +0 -1
  618. package/dist/environment/__tests__/resize-dispose.test.d.ts +0 -2
  619. package/dist/environment/__tests__/resize-dispose.test.d.ts.map +0 -1
  620. package/dist/environment/__tests__/signature-red.test.d.ts +0 -2
  621. package/dist/environment/__tests__/signature-red.test.d.ts.map +0 -1
  622. package/dist/environment/__tests__/signature.test.d.ts +0 -2
  623. package/dist/environment/__tests__/signature.test.d.ts.map +0 -1
  624. package/dist/environment/descriptors.d.ts +0 -2
  625. package/dist/environment/descriptors.d.ts.map +0 -1
  626. package/dist/environment/extracted-environment.d.ts +0 -53
  627. package/dist/environment/extracted-environment.d.ts.map +0 -1
  628. package/dist/environment/generation.d.ts +0 -62
  629. package/dist/environment/generation.d.ts.map +0 -1
  630. package/dist/environment/gpu-bundle.d.ts +0 -23
  631. package/dist/environment/gpu-bundle.d.ts.map +0 -1
  632. package/dist/environment/graph.d.ts +0 -108
  633. package/dist/environment/graph.d.ts.map +0 -1
  634. package/dist/environment/inspection.d.ts +0 -48
  635. package/dist/environment/inspection.d.ts.map +0 -1
  636. package/dist/environment/lifecycle.d.ts +0 -22
  637. package/dist/environment/lifecycle.d.ts.map +0 -1
  638. package/dist/environment/readiness.d.ts +0 -29
  639. package/dist/environment/readiness.d.ts.map +0 -1
  640. package/dist/environment/signature.d.ts +0 -70
  641. package/dist/environment/signature.d.ts.map +0 -1
  642. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +0 -2
  643. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +0 -1
  644. package/dist/extract/__tests__/environment-source.test.d.ts +0 -2
  645. package/dist/extract/__tests__/environment-source.test.d.ts.map +0 -1
  646. package/dist/extract/__tests__/schema-readers.unit.test.d.ts +0 -2
  647. package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +0 -1
  648. package/dist/extract/directional-csm.d.ts +0 -129
  649. package/dist/extract/directional-csm.d.ts.map +0 -1
  650. package/dist/extract/index.d.ts +0 -5
  651. package/dist/extract/index.d.ts.map +0 -1
  652. package/dist/extract/material-interface.d.ts +0 -56
  653. package/dist/extract/material-interface.d.ts.map +0 -1
  654. package/dist/extract/schema-readers.d.ts +0 -20
  655. package/dist/extract/schema-readers.d.ts.map +0 -1
  656. package/dist/features/graph-contribution.d.ts +0 -64
  657. package/dist/features/graph-contribution.d.ts.map +0 -1
  658. package/dist/features/index.d.ts +0 -21
  659. package/dist/features/index.d.ts.map +0 -1
  660. package/dist/features/morph/index.d.ts +0 -3
  661. package/dist/features/morph/index.d.ts.map +0 -1
  662. package/dist/features/morph/morph-bounds.d.ts +0 -42
  663. package/dist/features/morph/morph-bounds.d.ts.map +0 -1
  664. package/dist/features/scene-temporal.d.ts +0 -45
  665. package/dist/features/scene-temporal.d.ts.map +0 -1
  666. package/dist/features/taa-resolve.d.ts +0 -21
  667. package/dist/features/taa-resolve.d.ts.map +0 -1
  668. package/dist/frame-chain.d.ts +0 -14
  669. package/dist/frame-chain.d.ts.map +0 -1
  670. package/dist/gpu-driven/view-reference.d.ts.map +0 -1
  671. package/dist/gpu-resource-store.d.ts.map +0 -1
  672. package/dist/hdrp-cluster-membership.d.ts +0 -2
  673. package/dist/hdrp-cluster-membership.d.ts.map +0 -1
  674. package/dist/hdrp-pipeline.d.ts +0 -36
  675. package/dist/hdrp-pipeline.d.ts.map +0 -1
  676. package/dist/ibl/cubemap-projection.d.ts +0 -23
  677. package/dist/ibl/cubemap-projection.d.ts.map +0 -1
  678. package/dist/ibl/face-uniforms.d.ts +0 -65
  679. package/dist/ibl/face-uniforms.d.ts.map +0 -1
  680. package/dist/mesh-update-errors.d.ts +0 -71
  681. package/dist/mesh-update-errors.d.ts.map +0 -1
  682. package/dist/mipmap-generator.d.ts +0 -39
  683. package/dist/mipmap-generator.d.ts.map +0 -1
  684. package/dist/param-schema-projection-census.d.ts +0 -18
  685. package/dist/param-schema-projection-census.d.ts.map +0 -1
  686. package/dist/persistent-render-scene.d.ts +0 -158
  687. package/dist/persistent-render-scene.d.ts.map +0 -1
  688. package/dist/pipeline/index.d.ts +0 -11
  689. package/dist/pipeline/index.d.ts.map +0 -1
  690. package/dist/pipeline-errors.d.ts +0 -110
  691. package/dist/pipeline-errors.d.ts.map +0 -1
  692. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +0 -2
  693. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +0 -1
  694. package/dist/prepare/index.d.ts +0 -4
  695. package/dist/prepare/index.d.ts.map +0 -1
  696. package/dist/prepare/schema-readers.d.ts +0 -11
  697. package/dist/prepare/schema-readers.d.ts.map +0 -1
  698. package/dist/record/__tests__/environment-record.test.d.ts +0 -2
  699. package/dist/record/__tests__/environment-record.test.d.ts.map +0 -1
  700. package/dist/record/__tests__/frame-targets.unit.test.d.ts +0 -2
  701. package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +0 -1
  702. package/dist/record/__tests__/membership-timing.unit.test.d.ts +0 -2
  703. package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +0 -1
  704. package/dist/record/__tests__/render-submission.integration.test.d.ts +0 -2
  705. package/dist/record/__tests__/render-submission.integration.test.d.ts.map +0 -1
  706. package/dist/record/__tests__/render-submission.perf.test.d.ts +0 -2
  707. package/dist/record/__tests__/render-submission.perf.test.d.ts.map +0 -1
  708. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +0 -2
  709. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +0 -1
  710. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +0 -2
  711. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +0 -1
  712. package/dist/record/frame-observation.d.ts +0 -38
  713. package/dist/record/frame-observation.d.ts.map +0 -1
  714. package/dist/record/index.d.ts +0 -15
  715. package/dist/record/index.d.ts.map +0 -1
  716. package/dist/record/main-pass-skin.d.ts +0 -46
  717. package/dist/record/main-pass-skin.d.ts.map +0 -1
  718. package/dist/record/main-pass-sprite.d.ts +0 -69
  719. package/dist/record/main-pass-sprite.d.ts.map +0 -1
  720. package/dist/record/material-payload.d.ts +0 -43
  721. package/dist/record/material-payload.d.ts.map +0 -1
  722. package/dist/record/material-slot-plan.d.ts +0 -26
  723. package/dist/record/material-slot-plan.d.ts.map +0 -1
  724. package/dist/record/membership-timing.d.ts +0 -232
  725. package/dist/record/membership-timing.d.ts.map +0 -1
  726. package/dist/record/shadow-caster-uniform.d.ts +0 -8
  727. package/dist/record/shadow-caster-uniform.d.ts.map +0 -1
  728. package/dist/record/shadow-submit-mode.d.ts +0 -66
  729. package/dist/record/shadow-submit-mode.d.ts.map +0 -1
  730. package/dist/render-dispatch-membership.d.ts +0 -47
  731. package/dist/render-dispatch-membership.d.ts.map +0 -1
  732. package/dist/render-pipeline-context.d.ts +0 -289
  733. package/dist/render-pipeline-context.d.ts.map +0 -1
  734. package/dist/render-pipeline-target.d.ts +0 -16
  735. package/dist/render-pipeline-target.d.ts.map +0 -1
  736. package/dist/render-scene-projection.d.ts +0 -174
  737. package/dist/render-scene-projection.d.ts.map +0 -1
  738. package/dist/render-system-fold.d.ts +0 -266
  739. package/dist/render-system-fold.d.ts.map +0 -1
  740. package/dist/renderer/asset-service.d.ts +0 -10
  741. package/dist/renderer/asset-service.d.ts.map +0 -1
  742. package/dist/renderer/backend-selection.d.ts.map +0 -1
  743. package/dist/renderer/environment-classify.d.ts +0 -6
  744. package/dist/renderer/environment-classify.d.ts.map +0 -1
  745. package/dist/renderer/environment-error.d.ts +0 -13
  746. package/dist/renderer/environment-error.d.ts.map +0 -1
  747. package/dist/renderer/material/pipeline-projection.d.ts +0 -91
  748. package/dist/renderer/material/pipeline-projection.d.ts.map +0 -1
  749. package/dist/renderer/renderer-factory.d.ts +0 -289
  750. package/dist/renderer/renderer-factory.d.ts.map +0 -1
  751. package/dist/renderer.d.ts +0 -771
  752. package/dist/renderer.d.ts.map +0 -1
  753. package/dist/scene-utils/collect-subtree.d.ts +0 -11
  754. package/dist/scene-utils/collect-subtree.d.ts.map +0 -1
  755. package/dist/ssao-data.d.ts +0 -34
  756. package/dist/ssao-data.d.ts.map +0 -1
  757. package/dist/surface-format.d.ts +0 -10
  758. package/dist/surface-format.d.ts.map +0 -1
  759. package/dist/taa-history-store.d.ts +0 -88
  760. package/dist/taa-history-store.d.ts.map +0 -1
  761. package/dist/temporal-coverage.d.ts +0 -35
  762. package/dist/temporal-coverage.d.ts.map +0 -1
  763. package/dist/temporal-frame-transaction.d.ts +0 -92
  764. package/dist/temporal-frame-transaction.d.ts.map +0 -1
  765. package/dist/temporal-inspection.d.ts +0 -57
  766. package/dist/temporal-inspection.d.ts.map +0 -1
  767. package/dist/temporal-view.d.ts +0 -43
  768. package/dist/temporal-view.d.ts.map +0 -1
  769. package/dist/tilemap/resolve-tileset-runtime.d.ts +0 -23
  770. package/dist/tilemap/resolve-tileset-runtime.d.ts.map +0 -1
  771. package/dist/urp-pipeline.d.ts +0 -4
  772. package/dist/urp-pipeline.d.ts.map +0 -1
  773. package/src/__tests__/analytic-sky-contract.test.ts +0 -27
  774. package/src/__tests__/analytic-sky-falsify-carriers.mjs +0 -43
  775. package/src/__tests__/analytic-sky-falsify.test.ts +0 -94
  776. package/src/__tests__/assets-runtime-handoff.integration.test.ts +0 -36
  777. package/src/__tests__/backend-selection.integration.test.ts +0 -11
  778. package/src/__tests__/camera-temporal.test-d.ts +0 -8
  779. package/src/__tests__/camera-temporal.test.ts +0 -188
  780. package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +0 -18
  781. package/src/__tests__/environment-inspection.test.ts +0 -45
  782. package/src/__tests__/fog-ai-user.fixture.ts +0 -350
  783. package/src/__tests__/fog-custom-render-feature.fixture.ts +0 -33
  784. package/src/__tests__/fog-custom-render-feature.integration.test.ts +0 -76
  785. package/src/__tests__/fog-inspection.integration.test.ts +0 -127
  786. package/src/__tests__/fog-owner-state.integration.test.ts +0 -112
  787. package/src/__tests__/fog-pipeline-topology.integration.test.ts +0 -93
  788. package/src/__tests__/fog-public-surface.integration.test.ts +0 -60
  789. package/src/__tests__/fog-ray-contract.integration.test.ts +0 -73
  790. package/src/__tests__/fog-source-absence.integration.test.ts +0 -51
  791. package/src/__tests__/fog-temporal.integration.test.ts +0 -115
  792. package/src/__tests__/frame-chain.unit.test.ts +0 -26
  793. package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +0 -27
  794. package/src/__tests__/hdrp-pipeline.unit.test.ts +0 -100
  795. package/src/__tests__/ibl/ibl-fallback.unit.test.ts +0 -23
  796. package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +0 -69
  797. package/src/__tests__/material-derived-payload.integration.test.ts +0 -164
  798. package/src/__tests__/materials.unit.test.ts +0 -25
  799. package/src/__tests__/param-schema-projection-census.unit.test.ts +0 -40
  800. package/src/__tests__/persistent-render-scene.unit.test.ts +0 -1311
  801. package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +0 -167
  802. package/src/__tests__/pipeline-pass-trace.test.ts +0 -92
  803. package/src/__tests__/pipeline-stage-owner.test-d.ts +0 -13
  804. package/src/__tests__/plugin.test.ts +0 -25
  805. package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +0 -129
  806. package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +0 -180
  807. package/src/__tests__/public-pipeline-id.test-d.ts +0 -6
  808. package/src/__tests__/render-dispatch-membership.unit.test.ts +0 -117
  809. package/src/__tests__/render-feature-construction.unit.test.ts +0 -124
  810. package/src/__tests__/render-feature-error-code-owner.test-d.ts +0 -27
  811. package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +0 -46
  812. package/src/__tests__/render-feature-graph.integration.test.ts +0 -205
  813. package/src/__tests__/render-feature-graphics-staging.unit.test.ts +0 -200
  814. package/src/__tests__/render-feature-order.unit.test.ts +0 -66
  815. package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +0 -107
  816. package/src/__tests__/render-feature-staging.unit.test.ts +0 -94
  817. package/src/__tests__/renderer-render-scene-public.test-d.ts +0 -27
  818. package/src/__tests__/scene-temporal-coverage.unit.test.ts +0 -259
  819. package/src/__tests__/sun-snapshot-consumers.test.ts +0 -35
  820. package/src/__tests__/surface-backend-kind-owner.test-d.ts +0 -42
  821. package/src/__tests__/taa-history-store.unit.test.ts +0 -196
  822. package/src/__tests__/temporal-frame-transaction.integration.test.ts +0 -537
  823. package/src/__tests__/temporal-inspection.unit.test.ts +0 -138
  824. package/src/assets/__tests__/asset-decoders.unit.test.ts +0 -92
  825. package/src/assets/__tests__/mipmap-generator.unit.test.ts +0 -52
  826. package/src/assets/__tests__/render-asset-port.unit.test.ts +0 -54
  827. package/src/assets/__tests__/resolve-render-asset.unit.test.ts +0 -47
  828. package/src/components/__tests__/atmosphere-red.test.ts +0 -51
  829. package/src/components/__tests__/atmosphere.test.ts +0 -79
  830. package/src/components/__tests__/fog.test-d.ts +0 -49
  831. package/src/components/__tests__/fog.test.ts +0 -60
  832. package/src/components/atmosphere.ts +0 -64
  833. package/src/deferred-lighting.ts +0 -92
  834. package/src/dynamic-texture-store.ts +0 -111
  835. package/src/environment/README.md +0 -76
  836. package/src/environment/__tests__/failure-injection.test.ts +0 -50
  837. package/src/environment/__tests__/generation-red.test.ts +0 -64
  838. package/src/environment/__tests__/generation.test.ts +0 -37
  839. package/src/environment/__tests__/graph-transaction.test.ts +0 -85
  840. package/src/environment/__tests__/inspection-red.test.ts +0 -53
  841. package/src/environment/__tests__/lifecycle-evidence.test.ts +0 -40
  842. package/src/environment/__tests__/pipeline-topology.test.ts +0 -33
  843. package/src/environment/__tests__/readiness.test.ts +0 -42
  844. package/src/environment/__tests__/recovery.test.ts +0 -62
  845. package/src/environment/__tests__/resize-dispose.test.ts +0 -30
  846. package/src/environment/__tests__/signature-red.test.ts +0 -45
  847. package/src/environment/__tests__/signature.test.ts +0 -60
  848. package/src/environment/descriptors.ts +0 -7
  849. package/src/environment/extracted-environment.ts +0 -239
  850. package/src/environment/generation.ts +0 -158
  851. package/src/environment/gpu-bundle.ts +0 -144
  852. package/src/environment/graph.ts +0 -588
  853. package/src/environment/inspection.ts +0 -107
  854. package/src/environment/lifecycle.ts +0 -124
  855. package/src/environment/readiness.ts +0 -76
  856. package/src/environment/signature.ts +0 -185
  857. package/src/extract/__tests__/directional-csm-domain.unit.test.ts +0 -391
  858. package/src/extract/__tests__/environment-source.test.ts +0 -105
  859. package/src/extract/__tests__/schema-readers.unit.test.ts +0 -16
  860. package/src/extract/directional-csm.ts +0 -959
  861. package/src/extract/index.ts +0 -10
  862. package/src/extract/material-interface.ts +0 -604
  863. package/src/extract/schema-readers.ts +0 -27
  864. package/src/features/graph-contribution.ts +0 -577
  865. package/src/features/index.ts +0 -100
  866. package/src/features/morph/index.ts +0 -24
  867. package/src/features/morph/morph-bounds.ts +0 -123
  868. package/src/features/scene-temporal.ts +0 -274
  869. package/src/features/taa-resolve.ts +0 -244
  870. package/src/frame-chain.ts +0 -21
  871. package/src/hdrp-cluster-membership.ts +0 -70
  872. package/src/ibl/cubemap-projection.ts +0 -60
  873. package/src/ibl/face-uniforms.ts +0 -150
  874. package/src/mesh-update-errors.ts +0 -113
  875. package/src/mipmap-generator.ts +0 -244
  876. package/src/param-schema-projection-census.ts +0 -73
  877. package/src/persistent-render-scene.ts +0 -1752
  878. package/src/pipeline/index.ts +0 -40
  879. package/src/pipeline-errors.ts +0 -172
  880. package/src/prepare/__tests__/schema-readers.unit.test.ts +0 -16
  881. package/src/prepare/index.ts +0 -8
  882. package/src/prepare/schema-readers.ts +0 -20
  883. package/src/record/__tests__/environment-record.test.ts +0 -35
  884. package/src/record/__tests__/frame-targets.unit.test.ts +0 -54
  885. package/src/record/__tests__/membership-timing.unit.test.ts +0 -524
  886. package/src/record/__tests__/render-submission.integration.test.ts +0 -76
  887. package/src/record/__tests__/render-submission.perf.test.ts +0 -141
  888. package/src/record/__tests__/shadow-submit-mode.unit.test.ts +0 -88
  889. package/src/record/__tests__/view-ubo-layout.unit.test.ts +0 -101
  890. package/src/record/frame-observation.ts +0 -122
  891. package/src/record/index.ts +0 -15
  892. package/src/record/main-pass-skin.ts +0 -51
  893. package/src/record/main-pass-sprite.ts +0 -166
  894. package/src/record/material-payload.ts +0 -292
  895. package/src/record/material-slot-plan.ts +0 -68
  896. package/src/record/membership-timing.ts +0 -1012
  897. package/src/record/shadow-caster-uniform.ts +0 -37
  898. package/src/record/shadow-submit-mode.ts +0 -277
  899. package/src/render-dispatch-membership.ts +0 -155
  900. package/src/render-pipeline-context.ts +0 -332
  901. package/src/render-pipeline-target.ts +0 -96
  902. package/src/render-scene-projection.ts +0 -1738
  903. package/src/render-system-fold.ts +0 -531
  904. package/src/renderer/asset-service.ts +0 -32
  905. package/src/renderer/backend-selection.ts +0 -121
  906. package/src/renderer/environment-classify.ts +0 -30
  907. package/src/renderer/environment-error.ts +0 -19
  908. package/src/renderer/material/pipeline-projection.ts +0 -140
  909. package/src/renderer.ts +0 -940
  910. package/src/scene-utils/collect-subtree.ts +0 -40
  911. package/src/ssao-data.ts +0 -106
  912. package/src/surface-format.ts +0 -37
  913. package/src/taa-history-store.ts +0 -429
  914. package/src/temporal-coverage.ts +0 -106
  915. package/src/temporal-frame-transaction.ts +0 -301
  916. package/src/temporal-inspection.ts +0 -194
  917. package/src/temporal-view.ts +0 -174
  918. package/src/tilemap/resolve-tileset-runtime.ts +0 -72
@@ -1,5 +1,5 @@
1
- // @forgeax/engine-runtime - GpuResourceStore
2
- // (feat-20260601-gpu-resource-store-extraction M1; M2 added the deriveRenderData
1
+ // @forgeax/engine-runtime - GpuResidencyCache
2
+ // (feat-20260601-device/gpu-residency-extraction M1; M2 added the deriveRenderData
3
3
  // projection seam — see render-data.ts).
4
4
  //
5
5
  // Owns the GPU residency layer extracted from AssetRegistry: per-handle GPU
@@ -23,30 +23,42 @@
23
23
  //
24
24
  // Texture residency is SYNCHRONOUS (D-9): `uploadTexture`'s only async source
25
25
  // was the one-time per-device mipmap shader-module build, which is hoisted to
26
- // `prewarmMipmapPipeline` (called from `createRenderer.ready`, already async).
26
+ // `prewarmMipmapPipeline` (called from `createRenderer.initialization`, already async).
27
27
  // After prewarm the per-texture mipmap blit is pure synchronous encoder work,
28
28
  // so the sync `draw(world)` frame contract is preserved. A format that was
29
29
  // not prewarmed surfaces a structured RhiError on the sync path -- it is never
30
30
  // lazily awaited (that would break the sync draw contract).
31
31
 
32
+ import {
33
+ blitMipmapsSync,
34
+ getOrCreateMipmapPipeline,
35
+ type MipmapShaderModuleFactory,
36
+ } from '@forgeax/engine-assets-runtime';
32
37
  import type { World } from '@forgeax/engine-ecs';
33
- import type { VertexLayoutProjection } from '@forgeax/engine-geometry';
38
+ import {
39
+ PROCEDURAL_FLOATS_PER_VERTEX,
40
+ type VertexLayoutProjection,
41
+ } from '@forgeax/engine-geometry';
34
42
  import { halfFloat } from '@forgeax/engine-math';
35
43
  import {
36
44
  type Buffer,
45
+ type BufferDescriptor,
37
46
  err,
38
47
  ok,
39
48
  type Result,
40
49
  type RhiCaps,
41
50
  type RhiDevice,
42
51
  RhiError,
52
+ type RhiQueue,
43
53
  type Sampler,
44
54
  type Texture,
55
+ type TextureFormat,
45
56
  type TextureView,
46
57
  } from '@forgeax/engine-rhi';
47
58
  import {
48
59
  ASSET_ERROR_HINTS,
49
60
  type AssetError,
61
+ countExtraUvSets,
50
62
  type DecodedImage,
51
63
  type EquirectAsset,
52
64
  type Handle,
@@ -61,37 +73,29 @@ import {
61
73
  type Submesh,
62
74
  type TextureAsset,
63
75
  type MeshAsset as TypesMeshAsset,
76
+ toShared,
64
77
  } from '@forgeax/engine-types';
65
- import { GpuBuffer, GpuTexture } from './gpu-resource';
66
- import { GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING } from './gpu-texture-usage';
78
+ import { GpuBuffer, GpuTexture } from '../gpu-resource';
79
+ import {
80
+ GPU_TEXTURE_USAGE_COPY_DST,
81
+ GPU_TEXTURE_USAGE_TEXTURE_BINDING,
82
+ } from '../gpu-texture-usage';
67
83
  import {
68
84
  GPU_BUFFER_USAGE_COPY_DST,
69
85
  GPU_BUFFER_USAGE_INDEX,
86
+ GPU_BUFFER_USAGE_UNIFORM,
70
87
  GPU_BUFFER_USAGE_VERTEX,
71
- } from './gpu-usage';
72
- import {
73
- createFaceUniformsBuffer,
74
- createPrefilterUniformsBuffer,
75
- writeAllFaceUniforms,
76
- writeAllPrefilterUniforms,
77
- } from './ibl/face-uniforms';
88
+ } from '../gpu-usage';
78
89
  import {
90
+ CAPTURE_VIEW_PROJS,
79
91
  CUBEMAP_FACE_VERTICES,
80
92
  createIblPipelines,
93
+ type IblShaderModuleFactory,
94
+ PREFILTER_MIP_LEVELS,
95
+ PREFILTER_SIZE,
81
96
  runIblPrecompute,
82
- } from './ibl/IblPipelineCache';
83
- import type { RhiErrorListenerRegistry } from './lifecycle';
84
- import {
85
- type MeshRangeInvalidReason,
86
- MeshUpdateError,
87
- type MeshUpdateError as MeshUpdateErrorType,
88
- } from './mesh-update-errors';
89
- import {
90
- blitMipmapsSync,
91
- getOrCreateMipmapPipeline,
92
- type MipmapBlitDevice,
93
- type MipmapShaderModuleFactory,
94
- } from './mipmap-generator';
97
+ } from '../ibl/IblPipelineCache';
98
+ import type { RhiErrorListenerRegistry } from '../lifecycle';
95
99
  import {
96
100
  type CubeRenderData,
97
101
  deriveMipUploadLayout,
@@ -100,7 +104,114 @@ import {
100
104
  deriveRenderDataTexture,
101
105
  type MeshRenderData,
102
106
  type TextureRenderData,
103
- } from './render-data';
107
+ } from '../render-data';
108
+ import type { DeviceScope } from './device-scope';
109
+
110
+ const DYNAMIC_OFFSET_STRIDE = 256;
111
+ const FACE_COUNT = 6;
112
+ const PREFILTER_SUBPASS_COUNT = PREFILTER_MIP_LEVELS * FACE_COUNT;
113
+
114
+ export type FaceUniformsDevice = Pick<RhiDevice, 'createBuffer'> & {
115
+ readonly queue: Pick<RhiQueue, 'writeBuffer'>;
116
+ };
117
+
118
+ function invalidUniformInput(expected: string, hint: string): Result<never, RhiError> {
119
+ return err(new RhiError({ code: 'webgpu-runtime-error', expected, hint }));
120
+ }
121
+
122
+ export function createFaceUniformsBuffer(device: FaceUniformsDevice): Result<Buffer, RhiError> {
123
+ const descriptor: BufferDescriptor = {
124
+ label: 'ibl-face-uniforms',
125
+ size: FACE_COUNT * DYNAMIC_OFFSET_STRIDE,
126
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
127
+ mappedAtCreation: false,
128
+ };
129
+ return device.createBuffer(descriptor);
130
+ }
131
+
132
+ export function createPrefilterUniformsBuffer(
133
+ device: FaceUniformsDevice,
134
+ ): Result<Buffer, RhiError> {
135
+ const descriptor: BufferDescriptor = {
136
+ label: 'ibl-prefilter-uniforms',
137
+ size: PREFILTER_SUBPASS_COUNT * DYNAMIC_OFFSET_STRIDE,
138
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
139
+ mappedAtCreation: false,
140
+ };
141
+ return device.createBuffer(descriptor);
142
+ }
143
+
144
+ export function writeFaceUniforms(
145
+ device: FaceUniformsDevice,
146
+ buffer: Buffer,
147
+ faceIdx: number,
148
+ viewProj: Float32Array,
149
+ ): Result<void, RhiError> {
150
+ if (faceIdx < 0 || faceIdx >= FACE_COUNT) {
151
+ return invalidUniformInput(
152
+ 'faceIdx is an integer in the inclusive range [0, 5]',
153
+ `writeFaceUniforms received faceIdx=${faceIdx}`,
154
+ );
155
+ }
156
+ if (viewProj.length !== 16) {
157
+ return invalidUniformInput(
158
+ 'viewProj contains exactly 16 floats',
159
+ `writeFaceUniforms received ${viewProj.length} floats`,
160
+ );
161
+ }
162
+ return device.queue.writeBuffer(buffer, faceIdx * DYNAMIC_OFFSET_STRIDE, viewProj);
163
+ }
164
+
165
+ export function writeAllFaceUniforms(
166
+ device: FaceUniformsDevice,
167
+ buffer: Buffer,
168
+ ): Result<void, RhiError> {
169
+ for (let face = 0; face < FACE_COUNT; face++) {
170
+ const viewProj = CAPTURE_VIEW_PROJS[face];
171
+ if (viewProj === undefined) continue;
172
+ const result = writeFaceUniforms(device, buffer, face, viewProj);
173
+ if (!result.ok) return result;
174
+ }
175
+ return ok(undefined);
176
+ }
177
+
178
+ export function writePrefilterUniforms(
179
+ device: FaceUniformsDevice,
180
+ buffer: Buffer,
181
+ subPassIdx: number,
182
+ roughness: number,
183
+ mipFaceSize: number,
184
+ ): Result<void, RhiError> {
185
+ if (subPassIdx < 0 || subPassIdx >= PREFILTER_SUBPASS_COUNT) {
186
+ return invalidUniformInput(
187
+ `subPassIdx is an integer in the inclusive range [0, ${PREFILTER_SUBPASS_COUNT - 1}]`,
188
+ `writePrefilterUniforms received subPassIdx=${subPassIdx}`,
189
+ );
190
+ }
191
+ const payload = new Float32Array([roughness, mipFaceSize, 0, 0]);
192
+ return device.queue.writeBuffer(buffer, subPassIdx * DYNAMIC_OFFSET_STRIDE, payload);
193
+ }
194
+
195
+ export function writeAllPrefilterUniforms(
196
+ device: FaceUniformsDevice,
197
+ buffer: Buffer,
198
+ ): Result<void, RhiError> {
199
+ for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
200
+ const roughness = mip / (PREFILTER_MIP_LEVELS - 1);
201
+ const mipFaceSize = PREFILTER_SIZE / 2 ** mip;
202
+ for (let face = 0; face < FACE_COUNT; face++) {
203
+ const result = writePrefilterUniforms(
204
+ device,
205
+ buffer,
206
+ mip * FACE_COUNT + face,
207
+ roughness,
208
+ mipFaceSize,
209
+ );
210
+ if (!result.ok) return result;
211
+ }
212
+ }
213
+ return ok(undefined);
214
+ }
104
215
 
105
216
  // AssetError is constructed without importing the AssetRegistry's class; the
106
217
  // store builds the 4-field surface (.code / .expected / .hint / .detail)
@@ -158,28 +269,6 @@ function makeImageError<C extends ImageErrorCode>(
158
269
  return new RuntimeImageError<C>(detail);
159
270
  }
160
271
 
161
- // Extended device shape adding `queue.writeTexture` on top of
162
- // `MipmapBlitDevice` (the mipmap-generator only needs the encoder + queue
163
- // submit surface; uploadTexture additionally writes pixel bytes via
164
- // `queue.writeTexture` -- spec anchor: WebGPU 26.6.4.4).
165
- type MipmapBlitDeviceWithWriteTexture = MipmapBlitDevice & {
166
- readonly queue: MipmapBlitDevice['queue'] & {
167
- // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
168
- writeTexture(dst: any, data: any, layout: any, size: any): Result<void, any>;
169
- };
170
- };
171
-
172
- // Extended device shape adding `createBuffer` + `queue.writeBuffer` on top of
173
- // `MipmapBlitDevice` for the user-mesh GPU upload path.
174
- type MipmapBlitDeviceWithBuffer = MipmapBlitDevice & {
175
- // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
176
- createBuffer(desc: any): Result<any, any>;
177
- readonly queue: MipmapBlitDevice['queue'] & {
178
- // biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
179
- writeBuffer(buffer: any, offset: number, data: any): Result<void, any>;
180
- };
181
- };
182
-
183
272
  /** Cube-POD register-relay injected by the wire layer (D-3). */
184
273
  type RegisterCube = (
185
274
  world: World,
@@ -215,13 +304,11 @@ interface TextureGpuEntry {
215
304
  interface CubemapGpuEntry {
216
305
  status: 'pending' | 'ready' | 'failed';
217
306
  texture: GpuTexture | null;
218
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU cubemap view (not a GpuResource)
219
- view: any;
220
- // biome-ignore lint/suspicious/noExplicitAny: per-face 2D views (6 faces; not GpuResources)
221
- faceViews: readonly any[];
307
+ view: TextureView | undefined;
308
+ faceViews: readonly TextureView[];
222
309
  }
223
310
 
224
- interface MeshGpuEntry {
311
+ export interface MeshGpuHandles {
225
312
  readonly vertexBuffer: GpuBuffer;
226
313
  /**
227
314
  * Index buffer wrapper, or `null` for a vertex-only mesh
@@ -239,15 +326,31 @@ interface MeshGpuEntry {
239
326
  readonly vboBytes: number;
240
327
  /** Allocation byte size for the ibo, or 0 when indexBuffer is null. */
241
328
  readonly iboBytes: number;
242
- /** Immutable geometry-owned layout projection used for all resident stride checks. */
243
- readonly layoutProjection: VertexLayoutProjection;
244
329
  readonly indexCount: number;
245
330
  readonly indexFormat: 'uint16' | 'uint32';
246
- /** Vertex count is derived from the geometry-owned projection stride. */
331
+ /**
332
+ * Vertex stride discriminator (feat-20260611). `'12F'` = 48 B
333
+ * (position+normal+uv+tangent); `'18F'` = 72 B (12F + skinIndex(uint16x4) +
334
+ * skinWeight(float32x4)). Mirrors `MeshRenderData.layout` in render-data.ts
335
+ * and `MeshRenderData.layout` -- the residency and render-data layout
336
+ * fields are the same union and move together.
337
+ */
338
+ readonly layout: '12F' | '18F';
339
+ /** Geometry-owned vertex layout projection used by every GPU consumer. */
340
+ readonly layoutProjection: VertexLayoutProjection;
341
+ /**
342
+ * Number of UV sets the interleaved buffer carries (1 = single `uv`, +1 per
343
+ * `uv1..uv7`). feat-20260629-multi-uv-set-support: threaded to the record
344
+ * stage so the forward PSO's vertex layout stride matches the buffer for
345
+ * meshes with a real extra UV set. Mirrors MeshRenderData.uvSetCount /
346
+ * MeshRenderData.uvSetCount.
347
+ */
348
+ readonly uvSetCount: number;
349
+ /** Vertex count uses 18F for skin or the geometry-owned canonical base stride. */
247
350
  readonly vertexCount: number;
248
351
  /** True when `MeshAsset.indices` is present (indexed draw path). */
249
352
  readonly indexed: boolean;
250
- /** Primitive topology (default 'triangle-list'). Mirrors MeshGpuHandles. */
353
+ /** Primitive topology (default 'triangle-list'). */
251
354
  readonly topology: PrimitiveTopology;
252
355
  /**
253
356
  * Submeshes from MeshAsset.submeshes, carried through to the record stage
@@ -257,41 +360,22 @@ interface MeshGpuEntry {
257
360
  readonly submeshes: readonly import('@forgeax/engine-types').Submesh[];
258
361
  }
259
362
 
260
- function meshStrideBytes(layoutProjection: VertexLayoutProjection): number {
261
- if (layoutProjection.arrayStride <= 0) {
262
- throw new Error('mesh layout projection must have a positive array stride');
263
- }
264
- return layoutProjection.arrayStride;
265
- }
266
-
267
- function submeshesEqual(a: readonly Submesh[], b: readonly Submesh[]): boolean {
268
- return (
269
- a.length === b.length &&
270
- a.every((left, index) => {
271
- const right = b[index];
272
- return (
273
- right !== undefined &&
274
- left.indexOffset === right.indexOffset &&
275
- left.indexCount === right.indexCount &&
276
- left.vertexCount === right.vertexCount &&
277
- left.topology === right.topology &&
278
- left.materialSlot === right.materialSlot
279
- );
280
- })
281
- );
282
- }
283
-
284
363
  /**
285
364
  * GPU residency store. Owns the per-handle GPU caches and the upload
286
365
  * primitives that build them. Constructed once per renderer; wired with the
287
- * GPU device via `configureGpuDevice` after `Renderer.ready` resolves.
366
+ * GPU device via `configureGpuDevice` after `Renderer.initialization` resolves.
288
367
  */
289
- export class GpuResourceStore {
368
+ export class GpuResidencyCache {
290
369
  /** Monotonic token for mesh GPU residency or contents changes. */
291
370
  meshResidencyEpoch = 0;
292
371
  /** Monotonic token for texture/sampler identities used by material bind groups. */
293
372
  materialResourceEpoch = 0;
294
- private gpuDevice: MipmapBlitDevice | undefined = undefined;
373
+ private gpuDevice: RhiDevice | undefined = undefined;
374
+ /** RHI-only owner for IBL projection; never populated with a raw GPUDevice. */
375
+ private iblDevice: RhiDevice | undefined = undefined;
376
+ /** RHI Result-returning shader factory paired with `iblDevice`. */
377
+ private iblShaderModuleFactory: IblShaderModuleFactory | undefined = undefined;
378
+ private deviceScope: DeviceScope | undefined;
295
379
  // Shader-module factory injected at `configureGpuDevice`; threaded into the
296
380
  // mipmap-pipeline prewarm + the IBL precompute path. `undefined` until wired.
297
381
  private asyncCreateShaderModule: MipmapShaderModuleFactory | undefined = undefined;
@@ -305,32 +389,45 @@ export class GpuResourceStore {
305
389
  // path (_uploadCubemapFromEquirect) when `rgba16floatRenderable` is false.
306
390
  private caps: RhiCaps | undefined = undefined;
307
391
 
308
- private readonly textureGpuHandles: Map<number, TextureGpuEntry> = new Map();
309
- private readonly samplerGpuHandles: Map<number, Sampler> = new Map();
392
+ private readonly textureGpuHandles: Map<string | number, TextureGpuEntry> = new Map();
393
+ private readonly samplerGpuHandles: Map<string | number, Sampler> = new Map();
310
394
  private readonly cubemapGpuHandles: Map<number, CubemapGpuEntry> = new Map();
311
395
  // Maps source EquirectAsset handle id -> minted cubemap handle so the same
312
396
  // equirect source always resolves to the same cubemap (idempotent, A2).
313
397
  private readonly cubemapIdempotentMap: Map<number, Handle<'EquirectAsset', 'shared'>> = new Map();
314
- private readonly meshGpuHandles: Map<number, MeshGpuEntry> = new Map();
398
+ private readonly meshGpuHandles: Map<string | number, MeshGpuHandles> = new Map();
315
399
 
316
400
  /**
317
- * feat-20260723-cross-world-gpu-cache-collision: compose a cache key from
318
- * the raw handle slot and the worldId so two worlds that independently
319
- * allocate assets at the same slot number never collide in the GPU store.
320
- * worldId=0 produces the bare slot (backward compat with single-world path).
401
+ * Compose a cache key from the raw handle slot and its owning World. The
402
+ * draw-time world index is only a position in one frame's `worlds[]`; it is
403
+ * not an asset identity and can be reused by a later World. World objects
404
+ * therefore receive a renderer-local namespace, while numeric callers keep
405
+ * the legacy namespace for low-level tests and built-in upload paths.
321
406
  */
322
- private worldKey(slot: number, worldId: number): number {
323
- return worldId === 0 ? slot : worldId * 0x1000000 + slot;
407
+ private readonly worldNamespaces = new WeakMap<World, number>();
408
+ private nextWorldNamespace = 1;
409
+
410
+ private worldKey(slot: number, world: World | number): string | number {
411
+ if (typeof world === 'number') {
412
+ return world === 0 ? slot : world * 0x1000000 + slot;
413
+ }
414
+ let namespace = this.worldNamespaces.get(world);
415
+ if (namespace === undefined) {
416
+ namespace = this.nextWorldNamespace;
417
+ this.nextWorldNamespace += 1;
418
+ this.worldNamespaces.set(world, namespace);
419
+ }
420
+ return `world:${namespace}:${slot}`;
324
421
  }
325
422
 
326
423
  /**
327
424
  * Wire the GPU device, shader-module factory, and cube-POD register relay.
328
- * Called once by createRenderer after `Renderer.ready` resolves. This
329
- * performs NO replay of CPU asset state:
425
+ * Called once by createRenderer after `Renderer.initialization` resolves. Unlike the
426
+ * pre-extraction AssetRegistry.configureGpuDevice, this performs NO replay:
330
427
  * the pull model builds resources lazily at first `ensureResident` access.
331
428
  */
332
429
  configureGpuDevice(
333
- device: MipmapBlitDevice,
430
+ device: RhiDevice,
334
431
  asyncCreateShaderModule: MipmapShaderModuleFactory | undefined,
335
432
  registerCube: RegisterCube,
336
433
  caps: RhiCaps,
@@ -342,6 +439,25 @@ export class GpuResourceStore {
342
439
  this.caps = caps;
343
440
  }
344
441
 
442
+ /**
443
+ * Bind the IBL projection to the same opaque RhiDevice used by assembly.
444
+ * This is deliberately separate from the legacy-compatible mipmap fixture
445
+ * seam: unit fixtures that only exercise ordinary residency do not need an
446
+ * IBL device, while a real IBL projection never accepts a raw GPUDevice.
447
+ */
448
+ configureIblDevice(
449
+ device: RhiDevice,
450
+ shaderModuleFactory: IblShaderModuleFactory | undefined,
451
+ ): void {
452
+ this.iblDevice = device;
453
+ this.iblShaderModuleFactory = shaderModuleFactory;
454
+ }
455
+
456
+ /** Bind residency ownership to the renderer's single DeviceScope tree. */
457
+ bindDeviceScope(scope: DeviceScope): void {
458
+ this.deviceScope = scope;
459
+ }
460
+
345
461
  /**
346
462
  * Wrap a raw RHI Buffer handle into a GpuBuffer. The wrapper holds a
347
463
  * reference to the device so `.destroy()` forwards to
@@ -353,19 +469,28 @@ export class GpuResourceStore {
353
469
  * surfaces as a non-null assertion rather than a structured error.
354
470
  */
355
471
  private wrapBuf(rawHandle: Buffer): GpuBuffer {
356
- const device = this.gpuDevice as MipmapBlitDevice & RhiDevice;
357
- return new GpuBuffer(device, rawHandle);
358
- }
359
-
360
- private destroyMeshBuffer(buffer: GpuBuffer): void {
361
- const result = buffer.destroy();
362
- if (!result.ok) this.errorRegistry?.fire(result.error);
472
+ const device = this.gpuDevice;
473
+ if (device === undefined) {
474
+ throw new Error('GpuResidencyCache.wrapBuf called before configureGpuDevice');
475
+ }
476
+ const resource = new GpuBuffer(device, rawHandle);
477
+ this.deviceScope?._adopt('buffer', resource, (value: GpuBuffer) => {
478
+ if (!value.isDestroyed) value.destroy();
479
+ });
480
+ return resource;
363
481
  }
364
482
 
365
483
  /** Wrap a raw RHI Texture handle into a GpuTexture (mirror of wrapBuf). */
366
484
  private wrapTex(rawHandle: Texture): GpuTexture {
367
- const device = this.gpuDevice as MipmapBlitDevice & RhiDevice;
368
- return new GpuTexture(device, rawHandle);
485
+ const device = this.gpuDevice;
486
+ if (device === undefined) {
487
+ throw new Error('GpuResidencyCache.wrapTex called before configureGpuDevice');
488
+ }
489
+ const resource = new GpuTexture(device, rawHandle);
490
+ this.deviceScope?._adopt('texture', resource, (value: GpuTexture) => {
491
+ if (!value.isDestroyed) value.destroy();
492
+ });
493
+ return resource;
369
494
  }
370
495
 
371
496
  /**
@@ -386,16 +511,17 @@ export class GpuResourceStore {
386
511
  */
387
512
  destroyAll(): void {
388
513
  if (this.meshGpuHandles.size > 0) this.meshResidencyEpoch += 1;
514
+ const scopeOwnsResources = this.deviceScope !== undefined;
389
515
  // The cubemap path registers two entries (sourceId + cubeId) sharing one
390
516
  // GpuTexture wrapper, so destroyAll must dedupe on the wrapper identity
391
517
  // before forwarding `.destroy()` -- otherwise the second call surfaces
392
518
  // `'destroy-after-destroy'` from the RHI shim. The `isDestroyed` getter
393
519
  // on the wrapper is the dedupe gate (architecture-principles §6).
394
520
  const destroyTex = (gpuTex: GpuTexture): void => {
395
- if (!gpuTex.isDestroyed) gpuTex.destroy();
521
+ if (!scopeOwnsResources && !gpuTex.isDestroyed) gpuTex.destroy();
396
522
  };
397
523
  const destroyBuf = (gpuBuf: GpuBuffer): void => {
398
- if (!gpuBuf.isDestroyed) gpuBuf.destroy();
524
+ if (!scopeOwnsResources && !gpuBuf.isDestroyed) gpuBuf.destroy();
399
525
  };
400
526
 
401
527
  for (const entry of this.textureGpuHandles.values()) {
@@ -439,7 +565,7 @@ export class GpuResourceStore {
439
565
  */
440
566
  evictTexture(
441
567
  handle: Handle<'TextureAsset', 'shared'>,
442
- worldId: number = 0,
568
+ worldId: World | number = 0,
443
569
  ): { freed: number; errors: RhiError[] } {
444
570
  const id = this.worldKey(handleSlot(handle), worldId);
445
571
  const entry = this.textureGpuHandles.get(id);
@@ -465,7 +591,7 @@ export class GpuResourceStore {
465
591
 
466
592
  evictMesh(
467
593
  handle: Handle<'MeshAsset', 'shared'>,
468
- worldId: number = 0,
594
+ worldId: World | number = 0,
469
595
  ): { freed: number; errors: RhiError[] } {
470
596
  const id = this.worldKey(handleSlot(handle), worldId);
471
597
  const entry = this.meshGpuHandles.get(id);
@@ -533,9 +659,15 @@ export class GpuResourceStore {
533
659
  const errors: RhiError[] = [];
534
660
  let meshResidencyChanged = false;
535
661
  let materialResidencyChanged = false;
662
+ const liveSlots = new Set([...liveSet].map((slot) => String(slot)));
663
+ const isLiveKey = (key: string | number): boolean => {
664
+ const slot =
665
+ typeof key === 'number' ? String(key % 0x1000000) : key.slice(key.lastIndexOf(':') + 1);
666
+ return liveSlots.has(slot);
667
+ };
536
668
 
537
669
  for (const key of this.textureGpuHandles.keys()) {
538
- if (!liveSet.has(key)) {
670
+ if (!isLiveKey(key)) {
539
671
  const entry = this.textureGpuHandles.get(key);
540
672
  if (entry !== undefined) {
541
673
  if (!entry.texture.isDestroyed) {
@@ -554,14 +686,14 @@ export class GpuResourceStore {
554
686
  }
555
687
 
556
688
  for (const key of this.samplerGpuHandles.keys()) {
557
- if (!liveSet.has(key)) {
689
+ if (!isLiveKey(key)) {
558
690
  this.samplerGpuHandles.delete(key);
559
691
  materialResidencyChanged = true;
560
692
  }
561
693
  }
562
694
 
563
695
  for (const key of this.cubemapGpuHandles.keys()) {
564
- if (!liveSet.has(key)) {
696
+ if (!liveSlots.has(String(key))) {
565
697
  const entry = this.cubemapGpuHandles.get(key);
566
698
  if (entry !== undefined) {
567
699
  // A 'failed' entry holds no GPU texture (texture:null) — nothing to free.
@@ -580,7 +712,7 @@ export class GpuResourceStore {
580
712
  }
581
713
 
582
714
  for (const key of this.meshGpuHandles.keys()) {
583
- if (!liveSet.has(key)) {
715
+ if (!isLiveKey(key)) {
584
716
  const entry = this.meshGpuHandles.get(key);
585
717
  if (entry !== undefined) {
586
718
  if (!entry.vertexBuffer.isDestroyed) {
@@ -615,7 +747,7 @@ export class GpuResourceStore {
615
747
 
616
748
  /**
617
749
  * Prewarm the mipmap pipeline cache for the given texture formats (D-9).
618
- * Called from `createRenderer.ready` (already async): builds the one-time
750
+ * Called from `createRenderer.initialization` (already async): builds the one-time
619
751
  * mipmap shader module + per-format render pipeline into the mipmap-generator
620
752
  * deviceCache so the per-texture mipmap blit at record-stage `ensureResident`
621
753
  * is pure synchronous encoder work. A format absent from this list will make
@@ -623,8 +755,8 @@ export class GpuResourceStore {
623
755
  * than lazily await a build (which would break the sync draw contract).
624
756
  */
625
757
  async prewarmMipmapPipeline(
626
- device: MipmapBlitDevice,
627
- formats: readonly GPUTextureFormat[],
758
+ device: RhiDevice,
759
+ formats: readonly TextureFormat[],
628
760
  ): Promise<Result<void, RhiError>> {
629
761
  const factory = this.asyncCreateShaderModule;
630
762
  if (factory === undefined) {
@@ -660,7 +792,7 @@ export class GpuResourceStore {
660
792
  */
661
793
  _getTextureGpuTexture(
662
794
  handle: Handle<'TextureAsset', 'shared'>,
663
- worldId: number = 0,
795
+ worldId: World | number = 0,
664
796
  ): GpuTexture | undefined {
665
797
  return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.texture;
666
798
  }
@@ -671,7 +803,7 @@ export class GpuResourceStore {
671
803
  */
672
804
  getTextureGpuView(
673
805
  handle: Handle<'TextureAsset', 'shared'>,
674
- worldId: number = 0,
806
+ worldId: World | number = 0,
675
807
  ): TextureView | undefined {
676
808
  return this.textureGpuHandles.get(this.worldKey(handleSlot(handle), worldId))?.view;
677
809
  }
@@ -684,12 +816,12 @@ export class GpuResourceStore {
684
816
  ensureSamplerResident(
685
817
  handle: Handle<'SamplerAsset', 'shared'>,
686
818
  pod: SamplerAsset,
687
- worldId: number = 0,
819
+ worldId: World | number = 0,
688
820
  ): Result<Sampler, RhiError> {
689
821
  const key = this.worldKey(handleSlot(handle), worldId);
690
822
  const existing = this.samplerGpuHandles.get(key);
691
823
  if (existing !== undefined) return ok(existing);
692
- const device = this.gpuDevice as (MipmapBlitDevice & RhiDevice) | undefined;
824
+ const device = this.gpuDevice;
693
825
  if (device === undefined) {
694
826
  return err(
695
827
  new RhiError({
@@ -719,14 +851,14 @@ export class GpuResourceStore {
719
851
  }
720
852
 
721
853
  /** Return the full-cube texture view, or `undefined` if not uploaded yet. */
722
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture view
723
- getCubemapGpuView(handle: Handle<'EquirectAsset', 'shared'>): any | undefined {
854
+ getCubemapGpuView(handle: Handle<'EquirectAsset', 'shared'>): TextureView | undefined {
724
855
  return this.cubemapGpuHandles.get(handleSlot(handle))?.view;
725
856
  }
726
857
 
727
858
  /** Return per-face 2D views (6 faces), or `undefined` if not uploaded yet. */
728
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture views
729
- getCubemapFaceViews(handle: Handle<'EquirectAsset', 'shared'>): readonly any[] | undefined {
859
+ getCubemapFaceViews(
860
+ handle: Handle<'EquirectAsset', 'shared'>,
861
+ ): readonly TextureView[] | undefined {
730
862
  return this.cubemapGpuHandles.get(handleSlot(handle))?.faceViews;
731
863
  }
732
864
 
@@ -753,11 +885,30 @@ export class GpuResourceStore {
753
885
  */
754
886
  getMeshGpuHandles(
755
887
  handle: Handle<'MeshAsset', 'shared'>,
756
- worldId: number = 0,
757
- ): MeshGpuEntry | undefined {
888
+ worldId: World | number = 0,
889
+ ): MeshGpuHandles | undefined {
758
890
  return this.meshGpuHandles.get(this.worldKey(handleSlot(handle), worldId));
759
891
  }
760
892
 
893
+ /**
894
+ * Drop one mutable shared mesh's GPU copy after ECS publishes `markChanged`.
895
+ * The next record pass resolves the same POD and rebuilds residency through
896
+ * the normal pull path, so ECS remains the payload authority.
897
+ */
898
+ invalidateMesh(handle: number, worldId: World | number = 0): void {
899
+ // SharedRefMutation publishes the encoded handle value, while the cache
900
+ // key is derived from the decoded slot. Keep the invalidation path aligned
901
+ // with getMeshGpuHandles/ensureResident so an in-place ECS payload change
902
+ // cannot leave the old VBO resident.
903
+ const key = this.worldKey(handleSlot(toShared<'MeshAsset'>(handle)), worldId);
904
+ const entry = this.meshGpuHandles.get(key);
905
+ if (entry === undefined) return;
906
+ entry.vertexBuffer.destroy();
907
+ if (entry.indexBuffer !== null) entry.indexBuffer.destroy();
908
+ this.meshGpuHandles.delete(key);
909
+ this.meshResidencyEpoch += 1;
910
+ }
911
+
761
912
  /**
762
913
  * Pull-model residency: build the GPU resource for `handle` from the
763
914
  * caller-provided POD on a miss, return the cached handles on a hit (D-2).
@@ -772,12 +923,21 @@ export class GpuResourceStore {
772
923
  * (`_uploadCubemapFromEquirect`). Builtin meshes are NOT routed here either
773
924
  * (createRenderer step-3 owns them; D-1).
774
925
  */
926
+ ensureResident(
927
+ handle: Handle<'MeshAsset', 'shared'>,
928
+ pod: TypesMeshAsset,
929
+ worldId?: World | number,
930
+ ): Result<MeshGpuHandles, RhiError | AssetError | ImageError>;
931
+ ensureResident(
932
+ handle: Handle<'TextureAsset', 'shared'>,
933
+ pod: TextureAsset,
934
+ worldId?: World | number,
935
+ ): Result<TextureGpuEntry, RhiError | AssetError | ImageError>;
775
936
  ensureResident(
776
937
  handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
777
938
  pod: TypesMeshAsset | TextureAsset,
778
- worldId: number = 0,
779
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU handle entry union
780
- ): Result<any, RhiError | AssetError | ImageError> {
939
+ worldId: World | number = 0,
940
+ ): Result<TextureGpuEntry | MeshGpuHandles, RhiError | AssetError | ImageError> {
781
941
  const id = handleSlot(handle);
782
942
  // Hit: O(1) cache lookup, never re-projects (AC-09 -- deriveRenderData*
783
943
  // runs only on a miss). The miss arms below dispatch on `pod.kind` with NO
@@ -825,7 +985,7 @@ export class GpuResourceStore {
825
985
  handle: Handle<'TextureAsset', 'shared'>,
826
986
  pod: TextureAsset,
827
987
  decoded: DecodedImage,
828
- worldId: number = 0,
988
+ worldId: World | number = 0,
829
989
  ): Promise<Result<void, AssetError | ImageError | RhiError>> {
830
990
  const device = this.gpuDevice;
831
991
  const projected = deriveRenderDataTexture(pod);
@@ -844,7 +1004,7 @@ export class GpuResourceStore {
844
1004
  new RhiError({
845
1005
  code: 'rhi-not-available',
846
1006
  expected: 'asyncCreateShaderModule wired by configureGpuDevice',
847
- hint: 'GpuResourceStore.uploadTexture mipmap branch needs a shader-module factory; rhi-webgpu / rhi-wgpu shims expose it via pack.createShaderModule. Explicit-rhi instances must surface the factory on the RhiInstance.',
1007
+ hint: 'GpuResidencyCache.uploadTexture mipmap branch needs a shader-module factory; rhi-webgpu / rhi-wgpu shims expose it via pack.createShaderModule. Explicit-rhi instances must surface the factory on the RhiInstance.',
848
1008
  }),
849
1009
  );
850
1010
  }
@@ -854,7 +1014,7 @@ export class GpuResourceStore {
854
1014
  new RhiError({
855
1015
  code: 'rhi-not-available',
856
1016
  expected: `mipmap pipeline for format ${tex.format} builds`,
857
- hint: 'GpuResourceStore.uploadTexture could not build the mipmap pipeline',
1017
+ hint: 'GpuResidencyCache.uploadTexture could not build the mipmap pipeline',
858
1018
  }),
859
1019
  );
860
1020
  }
@@ -900,7 +1060,7 @@ export class GpuResourceStore {
900
1060
  pod: TextureAsset,
901
1061
  decoded: DecodedImage,
902
1062
  renderData: TextureRenderData,
903
- worldId: number = 0,
1063
+ worldId: World | number = 0,
904
1064
  ): Result<TextureGpuEntry, AssetError | ImageError | RhiError> {
905
1065
  const device = this.gpuDevice;
906
1066
  const prepared = this.prepareTextureUpload(handle, pod, decoded, renderData);
@@ -909,7 +1069,7 @@ export class GpuResourceStore {
909
1069
  return err(
910
1070
  new RhiError({
911
1071
  code: 'rhi-not-available',
912
- expected: 'GpuResourceStore.configureGpuDevice wired before ensureResident',
1072
+ expected: 'GpuResidencyCache.configureGpuDevice wired before ensureResident',
913
1073
  hint: 'a texture ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
914
1074
  }),
915
1075
  );
@@ -957,8 +1117,7 @@ export class GpuResourceStore {
957
1117
  decoded: DecodedImage,
958
1118
  renderData: TextureRenderData,
959
1119
  ): Result<
960
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
961
- { id: number; tex: TextureAsset; levels: number; gpuTexture: any | undefined },
1120
+ { id: number; tex: TextureAsset; levels: number; gpuTexture: Texture | undefined },
962
1121
  AssetError | ImageError | RhiError
963
1122
  > {
964
1123
  const id = handleSlot(handle);
@@ -996,9 +1155,7 @@ export class GpuResourceStore {
996
1155
  // upload before allocating a handle; binding a texture that the backend
997
1156
  // accepted syntactically but cannot use later surfaces only as a generic
998
1157
  // invalid-bind-group submit error.
999
- const maxDimension =
1000
- (device as unknown as { readonly limits?: { readonly maxTextureDimension2D?: number } })
1001
- .limits?.maxTextureDimension2D ?? 16_384;
1158
+ const maxDimension = device.limits.maxTextureDimension2D;
1002
1159
  if (tex.width > maxDimension || tex.height > maxDimension) {
1003
1160
  return err(
1004
1161
  makeImageError({
@@ -1022,10 +1179,11 @@ export class GpuResourceStore {
1022
1179
  format: renderData.format,
1023
1180
  usage: renderData.usage,
1024
1181
  viewFormats: [],
1182
+ textureBindingViewDimension: undefined,
1025
1183
  });
1026
1184
  if (!createTexRes.ok) return createTexRes;
1027
1185
  const gpuTexture = createTexRes.value;
1028
- const queue = (device as unknown as MipmapBlitDeviceWithWriteTexture).queue;
1186
+ const queue = device.queue;
1029
1187
 
1030
1188
  if (renderData.compressed) {
1031
1189
  // feat-20260707 M5 / w36 (AC-08): block-compressed textures carry their
@@ -1137,7 +1295,7 @@ export class GpuResourceStore {
1137
1295
  return recordFailed(
1138
1296
  makeAssetError({
1139
1297
  code: 'asset-not-found',
1140
- expected: 'GpuResourceStore.configureGpuDevice wired with registerCube',
1298
+ expected: 'GpuResidencyCache.configureGpuDevice wired with registerCube',
1141
1299
  hint: 'call gpuStore.configureGpuDevice(device, factory, registerCube) before _uploadCubemapFromEquirect',
1142
1300
  }),
1143
1301
  );
@@ -1162,7 +1320,7 @@ export class GpuResourceStore {
1162
1320
  // guard above. The entry holds no live GPU resources yet (record binds the
1163
1321
  // white fallback while pending); the texture/views are filled in below when
1164
1322
  // the projection completes (status flips to 'ready'), or replaced by a
1165
- // 'failed' entry via recordFailed if any later step errors.
1323
+ // 'failed' entry via recordFailed if a later step errors.
1166
1324
  this.cubemapGpuHandles.set(sourceId, {
1167
1325
  status: 'pending',
1168
1326
  texture: null,
@@ -1231,45 +1389,28 @@ export class GpuResourceStore {
1231
1389
  // the cubemap and all three IBL side textures by IblPipelineCache.
1232
1390
  const outputFormat = this.caps?.rgba16floatRenderable === false ? 'rgba8unorm' : tex.format;
1233
1391
 
1234
- // Lazy projection runs from the record arm, where the device is always
1235
- // wired (research R-5). The device-not-wired queue + placeholder cube path
1236
- // is removed (D-7); a missing device here is a fail-fast, not a queue.
1237
- const device = this.gpuDevice;
1238
- if (device === undefined) {
1392
+ // Lazy projection runs from the record arm, where the RHI device is always
1393
+ // wired (research R-5). The IBL path is deliberately independent from the
1394
+ // ordinary mipmap fixture seam: it accepts only the opaque RhiDevice and
1395
+ // its Result-returning shader factory, never a raw GPUDevice or unwrap
1396
+ // fallback.
1397
+ const device = this.iblDevice;
1398
+ const shaderModuleFactory = this.iblShaderModuleFactory;
1399
+ const scope = this.deviceScope;
1400
+ if (device === undefined || shaderModuleFactory === undefined || scope === undefined) {
1239
1401
  return recordFailed(
1240
1402
  new RhiError({
1241
1403
  code: 'rhi-not-available',
1242
- expected: 'GpuResourceStore.configureGpuDevice wired before cubemap projection',
1243
- hint: 'a cubemap projection ran before the GPU device was wired; the record arm projects only after the device is configured',
1404
+ expected:
1405
+ 'GpuResidencyCache is configured with an RhiDevice, shader factory, and DeviceScope',
1406
+ hint: 'bind the renderer DeviceScope before a cubemap projection; raw GPU fallback is not supported',
1244
1407
  }),
1245
1408
  );
1246
1409
  }
1247
1410
 
1248
1411
  // Usage flags for the small equirect helper texture (sampled source for the
1249
1412
  // IBL precompute pass); the cube texture's usage comes from the projection.
1250
- // biome-ignore lint/suspicious/noExplicitAny: union of shim/raw return shapes
1251
- const unwrap = (r: any): any => {
1252
- if (r === null || r === undefined) return undefined;
1253
- if (typeof r === 'object' && 'ok' in r) return r.ok ? r.value : undefined;
1254
- return r;
1255
- };
1256
- // biome-ignore lint/suspicious/noExplicitAny: shim/raw device split
1257
- const makeTextureView = (dev: any, texture: any, desc: any): any => {
1258
- if (typeof dev.createTextureView === 'function') {
1259
- const u = unwrap(dev.createTextureView(texture, desc));
1260
- if (u !== undefined) return u;
1261
- }
1262
- if (texture !== null && texture !== undefined && typeof texture.createView === 'function') {
1263
- try {
1264
- return texture.createView(desc);
1265
- } catch {
1266
- return undefined;
1267
- }
1268
- }
1269
- return undefined;
1270
- };
1271
-
1272
- const gpuTextureRet = device.createTexture({
1413
+ const gpuTextureResult = device.createTexture({
1273
1414
  label: `cubemap-${sourceId}`,
1274
1415
  size: { width: cubeFaceSize, height: cubeFaceSize, depthOrArrayLayers: 6 },
1275
1416
  mipLevelCount: 1,
@@ -1278,52 +1419,29 @@ export class GpuResourceStore {
1278
1419
  format: outputFormat,
1279
1420
  usage: projected.cubeUsage,
1280
1421
  viewFormats: [],
1422
+ textureBindingViewDimension: undefined,
1281
1423
  });
1282
- const gpuTexture = unwrap(gpuTextureRet);
1283
- if (gpuTexture === undefined) {
1284
- return recordFailed(
1285
- makeAssetError({
1286
- code: 'ibl-precompute-not-dispatched',
1287
- expected: `device.createTexture cubemap-${sourceId} to return a valid texture`,
1288
- hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
1289
- }),
1290
- );
1291
- }
1424
+ if (!gpuTextureResult.ok) return recordFailed(gpuTextureResult.error);
1425
+ const gpuTexture = gpuTextureResult.value;
1292
1426
 
1293
- const cubeView = makeTextureView(device, gpuTexture, {
1427
+ const cubeViewResult = device.createTextureView(gpuTexture, {
1294
1428
  label: `cubemap-view-${sourceId}`,
1295
1429
  dimension: 'cube',
1296
1430
  arrayLayerCount: 6,
1297
1431
  });
1298
- if (cubeView === undefined) {
1299
- return recordFailed(
1300
- makeAssetError({
1301
- code: 'ibl-precompute-not-dispatched',
1302
- expected: `device.createTextureView cubemap-view-${sourceId} to return a valid view`,
1303
- hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
1304
- }),
1305
- );
1306
- }
1432
+ if (!cubeViewResult.ok) return recordFailed(cubeViewResult.error);
1433
+ const cubeView = cubeViewResult.value;
1307
1434
 
1308
- // biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture views
1309
- const faceViews: any[] = [];
1435
+ const faceViews: TextureView[] = [];
1310
1436
  for (let face = 0; face < 6; face++) {
1311
- const fv = makeTextureView(device, gpuTexture, {
1437
+ const faceViewResult = device.createTextureView(gpuTexture, {
1312
1438
  label: `cubemap-face-${sourceId}-${face}`,
1313
1439
  dimension: '2d',
1314
1440
  baseArrayLayer: face,
1315
1441
  arrayLayerCount: 1,
1316
1442
  });
1317
- if (fv === undefined) {
1318
- return recordFailed(
1319
- makeAssetError({
1320
- code: 'ibl-precompute-not-dispatched',
1321
- expected: `device.createTextureView cubemap-face-${sourceId}-${face} to return a valid view`,
1322
- hint: ASSET_ERROR_HINTS['ibl-precompute-not-dispatched'],
1323
- }),
1324
- );
1325
- }
1326
- faceViews.push(fv);
1443
+ if (!faceViewResult.ok) return recordFailed(faceViewResult.error);
1444
+ faceViews.push(faceViewResult.value);
1327
1445
  }
1328
1446
 
1329
1447
  // M-3 / w11: a single GpuTexture wrapper is shared between sourceId
@@ -1367,89 +1485,95 @@ export class GpuResourceStore {
1367
1485
 
1368
1486
  this.cubemapIdempotentMap.set(sourceId, cubeHandle);
1369
1487
 
1370
- if (this.asyncCreateShaderModule !== undefined) {
1371
- // biome-ignore lint/suspicious/noExplicitAny: device shim narrowed at runtime
1372
- const ipDevice = device as any;
1373
- const pipelinesRes = await createIblPipelines(
1374
- ipDevice,
1375
- // biome-ignore lint/suspicious/noExplicitAny: factory signatures are interoperable; device shim is structural
1376
- this.asyncCreateShaderModule as any,
1377
- outputFormat,
1488
+ const pipelinesResult = await createIblPipelines(
1489
+ scope,
1490
+ device,
1491
+ shaderModuleFactory,
1492
+ outputFormat,
1493
+ );
1494
+ if (!pipelinesResult.ok) {
1495
+ return recordFailed(
1496
+ makeAssetError({
1497
+ code: 'ibl-precompute-not-dispatched',
1498
+ expected: pipelinesResult.error.expected,
1499
+ hint: pipelinesResult.error.hint,
1500
+ }),
1501
+ );
1502
+ }
1503
+
1504
+ const equirectTextureResult = device.createTexture({
1505
+ label: `equirect-${sourceId}`,
1506
+ size: { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
1507
+ mipLevelCount: 1,
1508
+ sampleCount: 1,
1509
+ dimension: '2d',
1510
+ format: tex.format,
1511
+ usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
1512
+ viewFormats: [],
1513
+ textureBindingViewDimension: undefined,
1514
+ });
1515
+ if (!equirectTextureResult.ok) return recordFailed(equirectTextureResult.error);
1516
+ const equirectGpuTex = equirectTextureResult.value;
1517
+
1518
+ const equirectViewResult = device.createTextureView(equirectGpuTex, {
1519
+ label: `equirect-view-${sourceId}`,
1520
+ dimension: '2d',
1521
+ });
1522
+ if (!equirectViewResult.ok) return recordFailed(equirectViewResult.error);
1523
+ const equirectView = equirectViewResult.value;
1524
+
1525
+ const bytesPerPixel = tex.format === 'rgba32float' ? 16 : tex.format === 'rgba16float' ? 8 : 4;
1526
+ const equirectWriteResult = device.queue.writeTexture(
1527
+ { texture: equirectGpuTex },
1528
+ tex.data,
1529
+ { bytesPerRow: tex.width * bytesPerPixel, rowsPerImage: tex.height },
1530
+ { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
1531
+ );
1532
+ if (!equirectWriteResult.ok) return recordFailed(equirectWriteResult.error);
1533
+
1534
+ const faceBufferResult = createFaceUniformsBuffer(device);
1535
+ if (!faceBufferResult.ok) return recordFailed(faceBufferResult.error);
1536
+ const faceBuf = faceBufferResult.value;
1537
+ const prefilterBufferResult = createPrefilterUniformsBuffer(device);
1538
+ if (!prefilterBufferResult.ok) return recordFailed(prefilterBufferResult.error);
1539
+ const prefBuf = prefilterBufferResult.value;
1540
+
1541
+ const cubeVertexResult = device.createBuffer({
1542
+ label: 'ibl-cube-verts',
1543
+ size: CUBEMAP_FACE_VERTICES.byteLength,
1544
+ usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
1545
+ mappedAtCreation: false,
1546
+ });
1547
+ if (!cubeVertexResult.ok) return recordFailed(cubeVertexResult.error);
1548
+ const cubeVertex = cubeVertexResult.value;
1549
+
1550
+ const cubeVertexWriteResult = device.queue.writeBuffer(cubeVertex, 0, CUBEMAP_FACE_VERTICES);
1551
+ if (!cubeVertexWriteResult.ok) return recordFailed(cubeVertexWriteResult.error);
1552
+ const faceUniformsResult = writeAllFaceUniforms(device, faceBuf);
1553
+ if (!faceUniformsResult.ok) return recordFailed(faceUniformsResult.error);
1554
+ const prefilterUniformsResult = writeAllPrefilterUniforms(device, prefBuf);
1555
+ if (!prefilterUniformsResult.ok) return recordFailed(prefilterUniformsResult.error);
1556
+
1557
+ const runResult = runIblPrecompute({
1558
+ scope,
1559
+ device,
1560
+ equirectGpuTex,
1561
+ equirectView,
1562
+ cubeGpuTex: gpuTexture,
1563
+ cubeView,
1564
+ cubeFaceViews: faceViews,
1565
+ faceUniformsBuffer: faceBuf,
1566
+ prefilterUniformsBuffer: prefBuf,
1567
+ cubeVertexBuffer: cubeVertex,
1568
+ });
1569
+ if (!runResult.ok) {
1570
+ return recordFailed(
1571
+ makeAssetError({
1572
+ code: 'ibl-precompute-not-dispatched',
1573
+ expected: runResult.error.expected,
1574
+ hint: runResult.error.hint,
1575
+ }),
1378
1576
  );
1379
- if (pipelinesRes.ok) {
1380
- // biome-ignore lint/suspicious/noExplicitAny: device shim
1381
- const fdevice = device as any;
1382
- const equirectGpuTexRet = fdevice.createTexture({
1383
- label: `equirect-${sourceId}`,
1384
- size: { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
1385
- mipLevelCount: 1,
1386
- sampleCount: 1,
1387
- dimension: '2d',
1388
- format: tex.format,
1389
- usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
1390
- viewFormats: [],
1391
- });
1392
- const equirectGpuTex = unwrap(equirectGpuTexRet);
1393
- if (equirectGpuTex !== undefined && typeof fdevice.queue?.writeTexture === 'function') {
1394
- const bytesPerPixel =
1395
- tex.format === 'rgba32float' ? 16 : tex.format === 'rgba16float' ? 8 : 4;
1396
- fdevice.queue.writeTexture(
1397
- { texture: equirectGpuTex },
1398
- tex.data,
1399
- { bytesPerRow: tex.width * bytesPerPixel, rowsPerImage: tex.height },
1400
- { width: tex.width, height: tex.height, depthOrArrayLayers: 1 },
1401
- );
1402
- }
1403
- const equirectView =
1404
- equirectGpuTex !== undefined
1405
- ? makeTextureView(fdevice, equirectGpuTex, {
1406
- label: `equirect-view-${sourceId}`,
1407
- dimension: '2d',
1408
- })
1409
- : undefined;
1410
- const faceBuf = unwrap(createFaceUniformsBuffer(ipDevice));
1411
- const prefBuf = unwrap(createPrefilterUniformsBuffer(ipDevice));
1412
- const cubeVertex = unwrap(
1413
- fdevice.createBuffer({
1414
- label: 'ibl-cube-verts',
1415
- size: CUBEMAP_FACE_VERTICES.byteLength,
1416
- usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
1417
- mappedAtCreation: false,
1418
- }),
1419
- );
1420
- if (
1421
- equirectGpuTex !== undefined &&
1422
- equirectView !== undefined &&
1423
- faceBuf !== undefined &&
1424
- prefBuf !== undefined &&
1425
- cubeVertex !== undefined &&
1426
- typeof fdevice.queue?.writeBuffer === 'function'
1427
- ) {
1428
- fdevice.queue.writeBuffer(cubeVertex, 0, CUBEMAP_FACE_VERTICES);
1429
- writeAllFaceUniforms(ipDevice, faceBuf);
1430
- writeAllPrefilterUniforms(ipDevice, prefBuf);
1431
- const runRes = runIblPrecompute({
1432
- device: ipDevice,
1433
- equirectGpuTex,
1434
- equirectView,
1435
- cubeGpuTex: gpuTexture,
1436
- cubeView,
1437
- cubeFaceViews: faceViews,
1438
- faceUniformsBuffer: faceBuf,
1439
- prefilterUniformsBuffer: prefBuf,
1440
- cubeVertexBuffer: cubeVertex,
1441
- });
1442
- if (!runRes.ok) {
1443
- return recordFailed(
1444
- makeAssetError({
1445
- code: 'ibl-precompute-not-dispatched',
1446
- expected: runRes.error.expected,
1447
- hint: runRes.error.hint,
1448
- }),
1449
- );
1450
- }
1451
- }
1452
- }
1453
1577
  }
1454
1578
 
1455
1579
  return ok(cubeHandle);
@@ -1461,116 +1585,88 @@ export class GpuResourceStore {
1461
1585
  * `uploadMeshById` byte-for-byte. Returns the cached entry on success.
1462
1586
  */
1463
1587
  private uploadMeshById(
1464
- id: number,
1588
+ id: string | number,
1465
1589
  mesh: TypesMeshAsset,
1466
1590
  renderData: MeshRenderData,
1467
- ): Result<MeshGpuEntry, RhiError> {
1468
- const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
1591
+ ): Result<MeshGpuHandles, RhiError> {
1592
+ const device = this.gpuDevice;
1469
1593
  if (device === undefined) {
1470
1594
  return err(
1471
1595
  new RhiError({
1472
1596
  code: 'rhi-not-available',
1473
- expected: 'GpuResourceStore.configureGpuDevice wired before mesh ensureResident',
1597
+ expected: 'GpuResidencyCache.configureGpuDevice wired before mesh ensureResident',
1474
1598
  hint: 'a mesh ensureResident ran before the GPU device was wired; call gpuStore.configureGpuDevice in createRenderer',
1475
1599
  }),
1476
1600
  );
1477
1601
  }
1478
1602
 
1479
- // biome-ignore lint/suspicious/noExplicitAny: union of shim/raw return shapes
1480
- const unwrapBuffer = (r: any): any => {
1481
- if (r === null || r === undefined) return undefined;
1482
- if (typeof r === 'object' && 'ok' in r) return r.ok ? r.value : undefined;
1483
- return r;
1484
- };
1485
- // biome-ignore lint/suspicious/noExplicitAny: union of shim/raw write return
1486
- const writeOk = (r: any): boolean => {
1487
- if (r === undefined) return true;
1488
- if (typeof r === 'object' && 'ok' in r) return r.ok === true;
1489
- return true;
1490
- };
1491
-
1492
1603
  const indices = mesh.indices;
1493
1604
  const indexBytesUnpadded = indices === undefined ? 0 : indices.byteLength;
1494
1605
  const indexBytes = renderData.indexByteLength;
1495
1606
 
1496
- const vboRet = device.createBuffer({
1607
+ const vboResult = device.createBuffer({
1497
1608
  label: `mesh-${id}-vbo`,
1498
1609
  size: renderData.vertexByteLength,
1499
1610
  usage: renderData.vertexUsage,
1500
1611
  mappedAtCreation: false,
1501
1612
  });
1502
- const vbo = unwrapBuffer(vboRet);
1503
- if (vbo === undefined) {
1504
- return err(
1505
- new RhiError({
1506
- code: 'webgpu-runtime-error',
1507
- expected: `device.createBuffer mesh-${id}-vbo to return a valid buffer`,
1508
- hint: 'mesh vertex buffer allocation returned undefined',
1509
- }),
1510
- );
1511
- }
1613
+ if (!vboResult.ok) return vboResult;
1614
+ const vbo = vboResult.value;
1512
1615
  // Vertex-only mesh: skip the index buffer entirely. The indexed path below
1513
1616
  // is unchanged byte-for-byte when `indices` is present.
1514
- let ibo: unknown = null;
1617
+ let ibo: Buffer | null = null;
1515
1618
  if (indices !== undefined) {
1516
- const iboRet = device.createBuffer({
1619
+ const iboResult = device.createBuffer({
1517
1620
  label: `mesh-${id}-ibo`,
1518
1621
  size: indexBytes,
1519
1622
  usage: renderData.indexUsage,
1520
1623
  mappedAtCreation: false,
1521
1624
  });
1522
- ibo = unwrapBuffer(iboRet);
1523
- if (ibo === undefined) {
1524
- return err(
1525
- new RhiError({
1526
- code: 'webgpu-runtime-error',
1527
- expected: `device.createBuffer mesh-${id}-ibo to return a valid buffer`,
1528
- hint: 'mesh index buffer allocation returned undefined',
1529
- }),
1530
- );
1531
- }
1625
+ if (!iboResult.ok) return iboResult;
1626
+ ibo = iboResult.value;
1532
1627
  }
1533
1628
 
1534
- const vboWriteRet = device.queue.writeBuffer(vbo, 0, mesh.vertices);
1535
- if (!writeOk(vboWriteRet)) {
1536
- return err(
1537
- new RhiError({
1538
- code: 'webgpu-runtime-error',
1539
- expected: `queue.writeBuffer mesh-${id}-vbo to succeed`,
1540
- hint: 'mesh vertex buffer write failed',
1541
- }),
1542
- );
1543
- }
1629
+ const vboWriteResult = device.queue.writeBuffer(vbo, 0, mesh.vertices);
1630
+ if (!vboWriteResult.ok) return err(vboWriteResult.error);
1544
1631
 
1545
1632
  if (indices !== undefined) {
1546
1633
  const indexSrc = new Uint8Array(indexBytes);
1547
1634
  indexSrc.set(new Uint8Array(indices.buffer, indices.byteOffset, indexBytesUnpadded));
1548
- const iboWriteRet = device.queue.writeBuffer(ibo, 0, indexSrc);
1549
- if (!writeOk(iboWriteRet)) {
1635
+ if (ibo === null)
1550
1636
  return err(
1551
1637
  new RhiError({
1552
1638
  code: 'webgpu-runtime-error',
1553
- expected: `queue.writeBuffer mesh-${id}-ibo to succeed`,
1554
- hint: 'mesh index buffer write failed',
1639
+ expected: `mesh-${id} index buffer to be allocated before its upload`,
1640
+ hint: 'the indexed mesh upload lost its RHI index-buffer owner',
1555
1641
  }),
1556
1642
  );
1557
- }
1643
+ const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
1644
+ if (!iboWriteResult.ok) return err(iboWriteResult.error);
1558
1645
  }
1559
1646
 
1560
- const layoutProjection = renderData.layoutProjection;
1561
- const strideBytes = meshStrideBytes(layoutProjection);
1562
- const entry: MeshGpuEntry = {
1563
- vertexBuffer: this.wrapBuf(vbo as Buffer),
1564
- indexBuffer: ibo === null ? null : this.wrapBuf(ibo as Buffer),
1647
+ // The geometry projection already contains every packed attribute,
1648
+ // including skin and additional UV sets. Dividing by a second
1649
+ // attribute-derived count would double-count those fields and produce a
1650
+ // fractional vertex count for otherwise valid meshes.
1651
+ const extraUvSets = countExtraUvSets(mesh.attributes);
1652
+ const entry: MeshGpuHandles = {
1653
+ vertexBuffer: this.wrapBuf(vbo),
1654
+ indexBuffer: ibo === null ? null : this.wrapBuf(ibo),
1565
1655
  vboBytes: renderData.vertexByteLength,
1566
1656
  iboBytes: indices === undefined ? 0 : indexBytes,
1567
1657
  indexCount: renderData.indexCount,
1568
1658
  indexFormat: renderData.indexFormat,
1569
- vertexCount: mesh.vertices.byteLength / strideBytes,
1659
+ layout: renderData.layoutProjection.attributes.some(
1660
+ (attribute) => attribute.key === 'skinIndex' || attribute.key === 'skinWeight',
1661
+ )
1662
+ ? '18F'
1663
+ : '12F',
1664
+ layoutProjection: renderData.layoutProjection,
1665
+ uvSetCount: 1 + extraUvSets,
1666
+ vertexCount: mesh.vertices.byteLength / renderData.layoutProjection.arrayStride,
1570
1667
  indexed: indices !== undefined,
1571
1668
  topology: renderData.submeshes[0]?.topology ?? 'triangle-list',
1572
1669
  submeshes: renderData.submeshes,
1573
- layoutProjection,
1574
1670
  };
1575
1671
  this.meshGpuHandles.set(id, entry);
1576
1672
  this.meshResidencyEpoch += 1;
@@ -1578,220 +1674,114 @@ export class GpuResourceStore {
1578
1674
  }
1579
1675
 
1580
1676
  /**
1581
- * Update an existing resident mesh without changing its shape or metadata.
1582
- * A shape change must use a new asset handle; this method never reallocates
1583
- * or silently changes the draw contract.
1677
+ * Update an existing mesh's GPU buffer data in-place (or expand). The handle
1678
+ * must already be resident. Mirrors the pre-extraction `updateMeshById`.
1584
1679
  */
1585
1680
  private updateMeshById(
1586
- id: number,
1681
+ id: string | number,
1587
1682
  newVertices: Float32Array,
1588
- newIndices: Uint16Array | Uint32Array | undefined,
1683
+ newIndices: Uint16Array,
1589
1684
  submeshes?: readonly Submesh[],
1590
- ): Result<void, MeshUpdateErrorType | RhiError> {
1591
- const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
1592
- if (device === undefined) {
1593
- return err(
1594
- new RhiError({
1595
- code: 'rhi-not-available',
1596
- expected: 'GpuResourceStore.configureGpuDevice wired before mesh update',
1597
- hint: 'configure the renderer GPU device before updating a mesh',
1598
- }),
1599
- );
1600
- }
1685
+ ): void {
1686
+ const device = this.gpuDevice;
1687
+ if (device === undefined) return;
1601
1688
  const entry = this.meshGpuHandles.get(id);
1602
- if (entry === undefined) {
1603
- return err(
1604
- new MeshUpdateError({
1605
- code: 'mesh-not-resident',
1606
- detail: { handle: id },
1607
- }),
1608
- );
1609
- }
1689
+ if (entry === undefined) return;
1610
1690
 
1611
- const strideBytes = meshStrideBytes(entry.layoutProjection);
1612
- const expectedVertexBytes = entry.vertexCount * strideBytes;
1613
- const shapeMismatch = (expected: string, actual: string) =>
1614
- err(
1615
- new MeshUpdateError({
1616
- code: 'mesh-shape-mismatch',
1617
- detail: { handle: id, expected, actual },
1618
- }),
1619
- );
1691
+ const newVertexBytes = newVertices.byteLength;
1692
+ const indexBytesUnpadded = newIndices.byteLength;
1693
+ const newIndexBytes = ((indexBytesUnpadded + 3) >> 2) << 2;
1694
+
1695
+ // M-3 / w11: read allocation bytes from the entry's vboBytes / iboBytes
1696
+ // (tracked at upload time) instead of reaching into the opaque RHI Buffer
1697
+ // for a `.size` property -- the RHI Buffer interface is spec-aligned and
1698
+ // does NOT expose `.size`; allocation bytes remain derived cache facts.
1699
+ const existingIbo = entry.indexBuffer;
1700
+ const nextSubmeshes = submeshes ?? entry.submeshes;
1620
1701
  if (
1621
- newVertices.byteLength !== expectedVertexBytes ||
1622
- newVertices.byteLength % strideBytes !== 0
1702
+ existingIbo !== null &&
1703
+ newVertexBytes <= entry.vboBytes &&
1704
+ newIndexBytes <= entry.iboBytes
1623
1705
  ) {
1624
- return shapeMismatch(
1625
- `${expectedVertexBytes} vertex bytes at ${strideBytes}-byte stride`,
1626
- `${newVertices.byteLength} vertex bytes`,
1627
- );
1628
- }
1706
+ const vboWriteRet = device.queue.writeBuffer(entry.vertexBuffer.handle, 0, newVertices);
1707
+ if (!vboWriteRet.ok) return;
1629
1708
 
1630
- if (entry.indexed !== (newIndices !== undefined)) {
1631
- return shapeMismatch(
1632
- entry.indexed ? 'indexed mesh data' : 'vertex-only mesh data',
1633
- newIndices === undefined ? 'vertex-only mesh data' : 'indexed mesh data',
1634
- );
1635
- }
1636
- if (newIndices !== undefined) {
1637
- const indexFormat = newIndices instanceof Uint32Array ? 'uint32' : 'uint16';
1638
- const indexBytes = ((newIndices.byteLength + 3) >> 2) << 2;
1639
- if (
1640
- entry.indexBuffer === null ||
1641
- indexFormat !== entry.indexFormat ||
1642
- newIndices.length !== entry.indexCount ||
1643
- indexBytes !== entry.iboBytes
1644
- ) {
1645
- return shapeMismatch(
1646
- `${entry.indexFormat} index format with ${entry.indexCount} indices`,
1647
- `${indexFormat} index format with ${newIndices.length} indices`,
1648
- );
1649
- }
1650
- } else if (entry.indexBuffer !== null) {
1651
- return shapeMismatch('indexed mesh data', 'vertex-only mesh data');
1709
+ const indexSrc = new Uint8Array(newIndexBytes);
1710
+ indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, indexBytesUnpadded));
1711
+ const iboWriteRet = device.queue.writeBuffer(existingIbo.handle, 0, indexSrc);
1712
+ if (!iboWriteRet.ok) return;
1713
+
1714
+ this.meshGpuHandles.set(id, {
1715
+ vertexBuffer: entry.vertexBuffer,
1716
+ indexBuffer: existingIbo,
1717
+ vboBytes: entry.vboBytes,
1718
+ iboBytes: entry.iboBytes,
1719
+ indexCount: newIndices.length,
1720
+ indexFormat: 'uint16',
1721
+ layout: '12F',
1722
+ layoutProjection: entry.layoutProjection,
1723
+ uvSetCount: entry.uvSetCount,
1724
+ vertexCount: newVertices.length / PROCEDURAL_FLOATS_PER_VERTEX,
1725
+ indexed: true,
1726
+ topology: entry.topology,
1727
+ submeshes: nextSubmeshes,
1728
+ });
1729
+ this.meshResidencyEpoch += 1;
1730
+ return;
1652
1731
  }
1653
1732
 
1654
- const nextSubmeshes = submeshes ?? entry.submeshes;
1655
- if (!submeshesEqual(entry.submeshes, nextSubmeshes)) {
1656
- return shapeMismatch('unchanged submesh descriptors', 'different submesh descriptors');
1657
- }
1733
+ const vboResult = device.createBuffer({
1734
+ label: `mesh-${id}-vbo`,
1735
+ size: newVertexBytes,
1736
+ usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
1737
+ mappedAtCreation: false,
1738
+ });
1739
+ if (!vboResult.ok) return;
1740
+ const vbo = vboResult.value;
1741
+ const iboResult = device.createBuffer({
1742
+ label: `mesh-${id}-ibo`,
1743
+ size: newIndexBytes,
1744
+ usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
1745
+ mappedAtCreation: false,
1746
+ });
1747
+ if (!iboResult.ok) return;
1748
+ const ibo = iboResult.value;
1658
1749
 
1659
- const vertexWrite = device.queue.writeBuffer(entry.vertexBuffer.handle, 0, newVertices);
1660
- if (!vertexWrite.ok) return err(vertexWrite.error);
1661
- if (newIndices !== undefined && entry.indexBuffer !== null) {
1662
- const indexSrc = new Uint8Array(entry.iboBytes);
1663
- indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, newIndices.byteLength));
1664
- const indexWrite = device.queue.writeBuffer(entry.indexBuffer.handle, 0, indexSrc);
1665
- if (!indexWrite.ok) return err(indexWrite.error);
1666
- }
1667
- this.meshResidencyEpoch += 1;
1668
- return ok(undefined);
1669
- }
1750
+ const vboWriteResult = device.queue.writeBuffer(vbo, 0, newVertices);
1751
+ if (!vboWriteResult.ok) return;
1670
1752
 
1671
- /**
1672
- * Internal explicit rebuild path used when an owner has deliberately
1673
- * replaced a mesh shape. Public callers should register a new MeshAsset
1674
- * handle so CPU recovery data and GPU residency remain one authority.
1675
- */
1676
- private replaceMeshById(
1677
- id: number,
1678
- newVertices: Float32Array,
1679
- newIndices: Uint16Array | Uint32Array | undefined,
1680
- submeshes?: readonly Submesh[],
1681
- ): Result<void, MeshUpdateErrorType | RhiError> {
1682
- const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
1683
- const entry = this.meshGpuHandles.get(id);
1684
- if (device === undefined) {
1685
- return err(
1686
- new RhiError({
1687
- code: 'rhi-not-available',
1688
- expected: 'GpuResourceStore.configureGpuDevice wired before mesh replacement',
1689
- hint: 'configure the renderer GPU device before replacing a mesh',
1690
- }),
1691
- );
1692
- }
1693
- if (entry === undefined) {
1694
- return err(new MeshUpdateError({ code: 'mesh-not-resident', detail: { handle: id } }));
1695
- }
1696
- const strideBytes = meshStrideBytes(entry.layoutProjection);
1697
- if (newVertices.byteLength === 0 || newVertices.byteLength % strideBytes !== 0) {
1698
- return err(
1699
- new MeshUpdateError({
1700
- code: 'mesh-shape-mismatch',
1701
- detail: {
1702
- handle: id,
1703
- expected: `vertex bytes divisible by ${strideBytes}`,
1704
- actual: `${newVertices.byteLength} vertex bytes`,
1705
- },
1706
- }),
1707
- );
1708
- }
1709
- const newVertexBytes = newVertices.byteLength;
1710
- const newIndexBytes = newIndices === undefined ? 0 : ((newIndices.byteLength + 3) >> 2) << 2;
1711
- const unwrapBuffer = (r: unknown): unknown => {
1712
- if (r === null || r === undefined) return undefined;
1713
- if (typeof r === 'object' && 'ok' in r) {
1714
- return (r as { ok: boolean; value?: unknown }).ok
1715
- ? (r as unknown as { value: unknown }).value
1716
- : undefined;
1717
- }
1718
- return r;
1719
- };
1720
- const vbo = unwrapBuffer(
1721
- device.createBuffer({
1722
- label: `mesh-${id}-vbo`,
1723
- size: newVertexBytes,
1724
- usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
1725
- mappedAtCreation: false,
1726
- }),
1727
- );
1728
- if (vbo === undefined) {
1729
- return err(
1730
- new RhiError({
1731
- code: 'webgpu-runtime-error',
1732
- expected: `device.createBuffer mesh-${id}-vbo to return a valid buffer`,
1733
- hint: 'mesh replacement vertex buffer allocation failed',
1734
- }),
1735
- );
1736
- }
1737
- const ibo =
1738
- newIndices === undefined
1739
- ? null
1740
- : unwrapBuffer(
1741
- device.createBuffer({
1742
- label: `mesh-${id}-ibo`,
1743
- size: newIndexBytes,
1744
- usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
1745
- mappedAtCreation: false,
1746
- }),
1747
- );
1748
- if (newIndices !== undefined && ibo === undefined) {
1749
- this.destroyMeshBuffer(this.wrapBuf(vbo as Buffer));
1750
- return err(
1751
- new RhiError({
1752
- code: 'webgpu-runtime-error',
1753
- expected: `device.createBuffer mesh-${id}-ibo to return a valid buffer`,
1754
- hint: 'mesh replacement index buffer allocation failed',
1755
- }),
1756
- );
1757
- }
1758
- const vertexBuffer = this.wrapBuf(vbo as Buffer);
1759
- const indexBuffer = ibo === null ? null : this.wrapBuf(ibo as Buffer);
1760
- const vertexWrite = device.queue.writeBuffer(vertexBuffer.handle, 0, newVertices);
1761
- if (!vertexWrite.ok) {
1762
- this.destroyMeshBuffer(vertexBuffer);
1763
- if (indexBuffer !== null) this.destroyMeshBuffer(indexBuffer);
1764
- return err(vertexWrite.error);
1765
- }
1766
- if (newIndices !== undefined && indexBuffer !== null) {
1767
- const indexSrc = new Uint8Array(newIndexBytes);
1768
- indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, newIndices.byteLength));
1769
- const indexWrite = device.queue.writeBuffer(indexBuffer.handle, 0, indexSrc);
1770
- if (!indexWrite.ok) {
1771
- this.destroyMeshBuffer(vertexBuffer);
1772
- this.destroyMeshBuffer(indexBuffer);
1773
- return err(indexWrite.error);
1774
- }
1775
- }
1776
- const nextSubmeshes = submeshes ?? entry.submeshes;
1777
- const nextEntry: MeshGpuEntry = {
1778
- vertexBuffer,
1779
- indexBuffer,
1753
+ const indexSrc = new Uint8Array(newIndexBytes);
1754
+ indexSrc.set(new Uint8Array(newIndices.buffer, newIndices.byteOffset, indexBytesUnpadded));
1755
+ const iboWriteResult = device.queue.writeBuffer(ibo, 0, indexSrc);
1756
+ if (!iboWriteResult.ok) return;
1757
+
1758
+ this.meshGpuHandles.set(id, {
1759
+ vertexBuffer: this.wrapBuf(vbo),
1760
+ indexBuffer: this.wrapBuf(ibo),
1780
1761
  vboBytes: newVertexBytes,
1781
1762
  iboBytes: newIndexBytes,
1782
- indexCount: newIndices?.length ?? 0,
1783
- indexFormat: newIndices instanceof Uint32Array ? 'uint32' : 'uint16',
1763
+ indexCount: newIndices.length,
1764
+ indexFormat: 'uint16',
1765
+ layout: '12F',
1784
1766
  layoutProjection: entry.layoutProjection,
1785
- vertexCount: newVertexBytes / strideBytes,
1786
- indexed: newIndices !== undefined,
1787
- topology: nextSubmeshes[0]?.topology ?? entry.topology,
1767
+ uvSetCount: entry.uvSetCount,
1768
+ vertexCount: newVertices.length / PROCEDURAL_FLOATS_PER_VERTEX,
1769
+ indexed: true,
1770
+ topology: entry.topology,
1788
1771
  submeshes: nextSubmeshes,
1789
- };
1790
- this.meshGpuHandles.set(id, nextEntry);
1772
+ });
1791
1773
  this.meshResidencyEpoch += 1;
1792
- this.destroyMeshBuffer(entry.vertexBuffer);
1793
- if (entry.indexBuffer !== null) this.destroyMeshBuffer(entry.indexBuffer);
1794
- return ok(undefined);
1774
+
1775
+ // M-3 / w11: release the replaced buffers through their structured
1776
+ // GpuBuffer owner rather than reaching into opaque handles with a
1777
+ // backend-specific property.
1778
+ // `GpuBuffer.destroy()` routes through
1779
+ // `device.destroyBuffer(handle)` (RHI shim is the lifecycle SSOT;
1780
+ // architecture-principles §1 / charter §F1). Errors are swallowed here:
1781
+ // updateMeshById is the in-place reallocation path; the structured fail
1782
+ // surfaces on subsequent buffer use rather than blocking the resize.
1783
+ entry.vertexBuffer.destroy();
1784
+ if (existingIbo !== null) existingIbo.destroy();
1795
1785
  }
1796
1786
 
1797
1787
  /**
@@ -1801,101 +1791,12 @@ export class GpuResourceStore {
1801
1791
  updateMesh(
1802
1792
  handle: Handle<'MeshAsset', 'shared'>,
1803
1793
  newVertices: Float32Array,
1804
- newIndices: Uint16Array | Uint32Array | undefined,
1805
- worldId: number = 0,
1794
+ newIndices: Uint16Array,
1795
+ worldId: World | number = 0,
1806
1796
  submeshes?: readonly Submesh[],
1807
- ): Result<void, MeshUpdateErrorType | RhiError> {
1808
- const id = this.worldKey(handleSlot(handle), worldId);
1809
- return this.updateMeshById(id, newVertices, newIndices, submeshes);
1810
- }
1811
-
1812
- /** Explicit internal rebuild path; public callers should use a new asset handle. */
1813
- replaceMesh(
1814
- handle: Handle<'MeshAsset', 'shared'>,
1815
- newVertices: Float32Array,
1816
- newIndices: Uint16Array | Uint32Array | undefined,
1817
- worldId: number = 0,
1818
- submeshes?: readonly Submesh[],
1819
- ): Result<void, MeshUpdateErrorType | RhiError> {
1820
- const id = this.worldKey(handleSlot(handle), worldId);
1821
- return this.replaceMeshById(id, newVertices, newIndices, submeshes);
1822
- }
1823
-
1824
- /** Upload a contiguous vertex range using the resident mesh stride. */
1825
- updateMeshVertexRange(
1826
- handle: Handle<'MeshAsset', 'shared'>,
1827
- vertices: Float32Array,
1828
- firstVertex: number,
1829
- vertexCount: number,
1830
- worldId: number = 0,
1831
- ): Result<void, MeshUpdateErrorType | RhiError> {
1797
+ ): void {
1832
1798
  const id = this.worldKey(handleSlot(handle), worldId);
1833
- const device = this.gpuDevice as MipmapBlitDeviceWithBuffer | undefined;
1834
- if (device === undefined) {
1835
- return err(
1836
- new RhiError({
1837
- code: 'rhi-not-available',
1838
- expected: 'GpuResourceStore.configureGpuDevice wired before mesh range update',
1839
- hint: 'configure the renderer GPU device before updating a mesh range',
1840
- }),
1841
- );
1842
- }
1843
- const entry = this.meshGpuHandles.get(id);
1844
- if (entry === undefined) {
1845
- return err(new MeshUpdateError({ code: 'mesh-not-resident', detail: { handle: id } }));
1846
- }
1847
- const strideBytes = meshStrideBytes(entry.layoutProjection);
1848
- const invalidLocation = (
1849
- reason: Exclude<MeshRangeInvalidReason, 'stride-mismatch'>,
1850
- ): Result<void, MeshUpdateErrorType | RhiError> =>
1851
- err(
1852
- new MeshUpdateError({
1853
- code: 'mesh-range-invalid',
1854
- detail: {
1855
- handle: id,
1856
- firstVertex,
1857
- vertexCount,
1858
- residentVertexCount: entry.vertexCount,
1859
- reason,
1860
- },
1861
- }),
1862
- );
1863
- const invalidStride = (
1864
- expectedByteLength: number,
1865
- actualByteLength: number,
1866
- ): Result<void, MeshUpdateErrorType | RhiError> =>
1867
- err(
1868
- new MeshUpdateError({
1869
- code: 'mesh-range-invalid',
1870
- detail: {
1871
- handle: id,
1872
- firstVertex,
1873
- vertexCount,
1874
- residentVertexCount: entry.vertexCount,
1875
- reason: 'stride-mismatch',
1876
- expectedByteLength,
1877
- actualByteLength,
1878
- },
1879
- }),
1880
- );
1881
- if (!Number.isInteger(firstVertex) || !Number.isInteger(vertexCount)) {
1882
- return invalidLocation('non-integer');
1883
- }
1884
- if (firstVertex < 0 || vertexCount < 0) return invalidLocation('negative');
1885
- if (vertexCount === 0) return invalidLocation('empty');
1886
- if (firstVertex + vertexCount > entry.vertexCount) return invalidLocation('out-of-bounds');
1887
- const expectedByteLength = vertexCount * strideBytes;
1888
- if (vertices.byteLength !== expectedByteLength) {
1889
- return invalidStride(expectedByteLength, vertices.byteLength);
1890
- }
1891
- const result = device.queue.writeBuffer(
1892
- entry.vertexBuffer.handle,
1893
- firstVertex * strideBytes,
1894
- vertices,
1895
- );
1896
- if (!result.ok) return err(result.error);
1897
- this.meshResidencyEpoch += 1;
1898
- return ok(undefined);
1799
+ this.updateMeshById(id, newVertices, newIndices, submeshes);
1899
1800
  }
1900
1801
  }
1901
1802