@forgeax/engine-render 0.1.27 → 0.1.29

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 (655) hide show
  1. package/README.md +842 -124
  2. package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
  3. package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
  4. package/dist/assembly/dynamic-geometry-host.d.ts +52 -0
  5. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
  6. package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
  7. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +3 -1
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/gpu-driven-owner.d.ts +10 -0
  11. package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
  12. package/dist/assembly/host-contract.d.ts +19 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +7 -0
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material-shader-policy.d.ts +3 -1
  17. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  18. package/dist/assembly/recovery/failure-location.d.ts +12 -0
  19. package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
  20. package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
  21. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  22. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  23. package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
  24. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
  25. package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
  26. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  27. package/dist/assembly/renderer-helpers.d.ts +3 -0
  28. package/dist/assembly/renderer-helpers.d.ts.map +1 -1
  29. package/dist/assembly/renderer-inspection.d.ts +4 -0
  30. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  31. package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
  32. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
  33. package/dist/assembly/skin-palette-owner.d.ts +8 -0
  34. package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
  35. package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
  36. package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
  37. package/dist/assembly/webgpu-device-loss.d.ts +15 -0
  38. package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
  39. package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
  40. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
  41. package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
  42. package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
  43. package/dist/assembly/webgpu-ready.d.ts +7 -3
  44. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  45. package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
  46. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
  47. package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
  48. package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
  49. package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
  50. package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
  51. package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
  52. package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
  53. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
  54. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
  55. package/dist/assembly/webgpu-renderer.d.ts +7 -141
  56. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  57. package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
  58. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
  59. package/dist/authoring.mjs +3 -3
  60. package/dist/{chunk-ADHHVYLW.mjs → chunk-33XZFTQU.mjs} +245 -39
  61. package/dist/chunk-33XZFTQU.mjs.map +1 -0
  62. package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
  63. package/dist/chunk-BLXGONR2.mjs.map +1 -0
  64. package/dist/{chunk-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
  65. package/dist/chunk-BQVQOQJH.mjs.map +1 -0
  66. package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
  67. package/dist/chunk-BTWH7BP4.mjs.map +1 -0
  68. package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
  69. package/dist/chunk-FUODTTIA.mjs.map +1 -0
  70. package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
  71. package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
  72. package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
  73. package/dist/{chunk-E34VL5VI.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
  74. package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
  75. package/dist/chunk-NICXSHAT.mjs.map +1 -0
  76. package/dist/{chunk-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
  77. package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
  78. package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
  79. package/dist/chunk-PA5DRJNI.mjs.map +1 -0
  80. package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
  81. package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
  82. package/dist/components/camera.d.ts +62 -2
  83. package/dist/components/camera.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +1 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/components/instances.d.ts +6 -20
  87. package/dist/components/instances.d.ts.map +1 -1
  88. package/dist/components/scene-instance.d.ts +2 -1
  89. package/dist/components/scene-instance.d.ts.map +1 -1
  90. package/dist/components/screen-space-reflection.d.ts +14 -0
  91. package/dist/components/screen-space-reflection.d.ts.map +1 -0
  92. package/dist/construct-renderer.mjs +13805 -7186
  93. package/dist/construct-renderer.mjs.map +1 -1
  94. package/dist/device/gpu-residency.d.ts +11 -17
  95. package/dist/device/gpu-residency.d.ts.map +1 -1
  96. package/dist/device/mesh-residency-lifetime.d.ts +21 -0
  97. package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
  98. package/dist/dynamic-geometry.d.ts +125 -0
  99. package/dist/dynamic-geometry.d.ts.map +1 -0
  100. package/dist/environment/background.d.ts +6 -0
  101. package/dist/environment/background.d.ts.map +1 -0
  102. package/dist/errors/gpu-driven.d.ts +6 -3
  103. package/dist/errors/gpu-driven.d.ts.map +1 -1
  104. package/dist/errors/render.d.ts +7 -4
  105. package/dist/errors/render.d.ts.map +1 -1
  106. package/dist/extract/camera.d.ts.map +1 -1
  107. package/dist/extract/gpu-driven.d.ts +33 -3
  108. package/dist/extract/gpu-driven.d.ts.map +1 -1
  109. package/dist/features/host.d.ts +23 -1
  110. package/dist/features/host.d.ts.map +1 -1
  111. package/dist/features/noise-texture.d.ts +17 -0
  112. package/dist/features/noise-texture.d.ts.map +1 -0
  113. package/dist/features/plan.d.ts +83 -2
  114. package/dist/features/plan.d.ts.map +1 -1
  115. package/dist/features/prepared-gpu-work.d.ts +37 -4
  116. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  117. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  118. package/dist/features/prepared-graphics.d.ts +5 -0
  119. package/dist/features/prepared-graphics.d.ts.map +1 -1
  120. package/dist/features/render-graph-compute.d.ts +5 -3
  121. package/dist/features/render-graph-compute.d.ts.map +1 -1
  122. package/dist/features/render-graph-contribution.d.ts +11 -3
  123. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  124. package/dist/features/render-graph-raster.d.ts +19 -3
  125. package/dist/features/render-graph-raster.d.ts.map +1 -1
  126. package/dist/features/types.d.ts +11 -0
  127. package/dist/features/types.d.ts.map +1 -1
  128. package/dist/fullscreen-post-process-pass.d.ts +2 -0
  129. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  130. package/dist/gpu-driven/batch-topology.d.ts +34 -1
  131. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  132. package/dist/gpu-driven/pbr-program.d.ts +10 -0
  133. package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
  134. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  135. package/dist/gpu-driven/production-raster.d.ts +144 -23
  136. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  137. package/dist/gpu-driven/shadow-views.d.ts +126 -0
  138. package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
  139. package/dist/gpu-driven/view-gpu.d.ts +18 -2
  140. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  141. package/dist/gpu-scene-schema.d.ts +2 -8
  142. package/dist/gpu-scene-schema.d.ts.map +1 -1
  143. package/dist/gpu-scene.d.ts +14 -0
  144. package/dist/gpu-scene.d.ts.map +1 -1
  145. package/dist/gpu-texture-usage.d.ts +1 -0
  146. package/dist/gpu-texture-usage.d.ts.map +1 -1
  147. package/dist/hdrp-buffers.d.ts +6 -0
  148. package/dist/hdrp-buffers.d.ts.map +1 -1
  149. package/dist/ibl/frame-resources.d.ts +8 -0
  150. package/dist/ibl/frame-resources.d.ts.map +1 -0
  151. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  152. package/dist/index.d.ts +21 -8
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.mjs +8 -7
  155. package/dist/index.mjs.map +1 -1
  156. package/dist/inspection-types.d.ts +164 -0
  157. package/dist/inspection-types.d.ts.map +1 -1
  158. package/dist/instances-derived-bounds.d.ts +11 -0
  159. package/dist/instances-derived-bounds.d.ts.map +1 -1
  160. package/dist/instances.d.ts +89 -44
  161. package/dist/instances.d.ts.map +1 -1
  162. package/dist/internal.d.ts +3 -0
  163. package/dist/internal.d.ts.map +1 -1
  164. package/dist/internal.mjs +118 -10
  165. package/dist/internal.mjs.map +1 -1
  166. package/dist/lifecycle.d.ts +2 -1
  167. package/dist/lifecycle.d.ts.map +1 -1
  168. package/dist/material-row.d.ts +5 -0
  169. package/dist/material-row.d.ts.map +1 -0
  170. package/dist/materials.d.ts +15 -0
  171. package/dist/materials.d.ts.map +1 -1
  172. package/dist/pbr-pipeline.d.ts +21 -5
  173. package/dist/pbr-pipeline.d.ts.map +1 -1
  174. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  175. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
  176. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
  177. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
  178. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
  179. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
  180. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
  181. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
  182. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
  183. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
  184. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
  185. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
  186. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
  187. package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
  188. package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
  189. package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
  190. package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
  191. package/dist/pipeline/standard-output/graph.d.ts +15 -0
  192. package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
  193. package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
  194. package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
  195. package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
  196. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
  197. package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
  198. package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
  199. package/dist/pipeline/standard-output/resources.d.ts +10 -0
  200. package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
  201. package/dist/pipeline/standard-output/types.d.ts +39 -0
  202. package/dist/pipeline/standard-output/types.d.ts.map +1 -0
  203. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  204. package/dist/pipeline/standard-post.d.ts +2 -2
  205. package/dist/pipeline/standard-post.d.ts.map +1 -1
  206. package/dist/pipeline-builder.d.ts +2 -1
  207. package/dist/pipeline-builder.d.ts.map +1 -1
  208. package/dist/pipeline-spec-types.d.ts +4 -0
  209. package/dist/pipeline-spec-types.d.ts.map +1 -1
  210. package/dist/pipeline-spec.d.ts +1 -9
  211. package/dist/pipeline-spec.d.ts.map +1 -1
  212. package/dist/points-lines/standard-owner.d.ts +1 -1
  213. package/dist/points-lines/standard-owner.d.ts.map +1 -1
  214. package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
  215. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  216. package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
  217. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
  218. package/dist/record/frame-lighting.d.ts +3 -3
  219. package/dist/record/frame-lighting.d.ts.map +1 -1
  220. package/dist/record/frame-snapshot.d.ts +106 -5
  221. package/dist/record/frame-snapshot.d.ts.map +1 -1
  222. package/dist/record/frame-targets.d.ts.map +1 -1
  223. package/dist/record/frame.d.ts +39 -4
  224. package/dist/record/frame.d.ts.map +1 -1
  225. package/dist/record/main-pass-geometry.d.ts +7 -8
  226. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  227. package/dist/record/main-pass-material.d.ts +38 -1
  228. package/dist/record/main-pass-material.d.ts.map +1 -1
  229. package/dist/record/main-pass-sprite-draws.d.ts +2 -10
  230. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  231. package/dist/record/main-pass.d.ts +12 -2
  232. package/dist/record/main-pass.d.ts.map +1 -1
  233. package/dist/record/mesh-ssbo.d.ts +15 -6
  234. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  235. package/dist/record/prepared-material-bindings.d.ts +19 -0
  236. package/dist/record/prepared-material-bindings.d.ts.map +1 -0
  237. package/dist/record/recovery-pipeline.d.ts +2 -9
  238. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  239. package/dist/record/render-context.d.ts +76 -26
  240. package/dist/record/render-context.d.ts.map +1 -1
  241. package/dist/record/shadow-pass.d.ts +62 -7
  242. package/dist/record/shadow-pass.d.ts.map +1 -1
  243. package/dist/record/skybox-post-pass.d.ts +1 -1
  244. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  245. package/dist/record/typed-frame-graph.d.ts +35 -7
  246. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  247. package/dist/record/view-ubo.d.ts +1 -0
  248. package/dist/record/view-ubo.d.ts.map +1 -1
  249. package/dist/recovery/render-system-candidate.d.ts +10 -2
  250. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  251. package/dist/recovery/render-system-roots.d.ts +11 -0
  252. package/dist/recovery/render-system-roots.d.ts.map +1 -0
  253. package/dist/recovery/types.d.ts +76 -0
  254. package/dist/recovery/types.d.ts.map +1 -0
  255. package/dist/reflection/record-owner.d.ts +0 -2
  256. package/dist/reflection/record-owner.d.ts.map +1 -1
  257. package/dist/render-contract.d.ts +185 -30
  258. package/dist/render-contract.d.ts.map +1 -1
  259. package/dist/render-graph-primitives.d.ts +1 -0
  260. package/dist/render-graph-primitives.d.ts.map +1 -1
  261. package/dist/render-pipeline.d.ts +64 -5
  262. package/dist/render-pipeline.d.ts.map +1 -1
  263. package/dist/render-system-extract-tail.d.ts +7 -5
  264. package/dist/render-system-extract-tail.d.ts.map +1 -1
  265. package/dist/render-system-extract.d.ts +101 -41
  266. package/dist/render-system-extract.d.ts.map +1 -1
  267. package/dist/render-system-projections.d.ts +22 -0
  268. package/dist/render-system-projections.d.ts.map +1 -0
  269. package/dist/render-system.d.ts +34 -77
  270. package/dist/render-system.d.ts.map +1 -1
  271. package/dist/scene/render-scene-types.d.ts +14 -7
  272. package/dist/scene/render-scene-types.d.ts.map +1 -1
  273. package/dist/scene/render-scene.d.ts +29 -11
  274. package/dist/scene/render-scene.d.ts.map +1 -1
  275. package/dist/scene/shadow-visibility.d.ts +5 -0
  276. package/dist/scene/shadow-visibility.d.ts.map +1 -0
  277. package/dist/ssr/admission.d.ts +70 -1
  278. package/dist/ssr/admission.d.ts.map +1 -1
  279. package/dist/ssr/compose.d.ts +13 -0
  280. package/dist/ssr/compose.d.ts.map +1 -0
  281. package/dist/ssr/errors.d.ts +35 -0
  282. package/dist/ssr/errors.d.ts.map +1 -1
  283. package/dist/ssr/graph.d.ts +60 -0
  284. package/dist/ssr/graph.d.ts.map +1 -0
  285. package/dist/ssr/history.d.ts +97 -0
  286. package/dist/ssr/history.d.ts.map +1 -0
  287. package/dist/ssr/hiz.d.ts +37 -0
  288. package/dist/ssr/hiz.d.ts.map +1 -0
  289. package/dist/ssr/inspection.d.ts +42 -0
  290. package/dist/ssr/inspection.d.ts.map +1 -0
  291. package/dist/ssr/resources.d.ts +58 -0
  292. package/dist/ssr/resources.d.ts.map +1 -0
  293. package/dist/ssr/temporal.d.ts +38 -0
  294. package/dist/ssr/temporal.d.ts.map +1 -0
  295. package/dist/systems/skin-palette-allocator.d.ts +32 -10
  296. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  297. package/dist/systems/skin-palette-types.d.ts +15 -0
  298. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  299. package/dist/systems/transparent-dispatch.d.ts +9 -0
  300. package/dist/systems/transparent-dispatch.d.ts.map +1 -0
  301. package/dist/targets/material-source.d.ts +7 -0
  302. package/dist/targets/material-source.d.ts.map +1 -1
  303. package/dist/temporal/frame-coordinator.d.ts +7 -0
  304. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  305. package/dist/temporal/gpu.d.ts +8 -0
  306. package/dist/temporal/gpu.d.ts.map +1 -1
  307. package/dist/temporal/history.d.ts.map +1 -1
  308. package/dist/temporal/index.mjs +6 -6
  309. package/dist/temporal/inspection.d.ts.map +1 -1
  310. package/dist/temporal/standard-scene-data.d.ts +2 -1
  311. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  312. package/dist/temporal/temporal-view.d.ts +2 -0
  313. package/dist/temporal/temporal-view.d.ts.map +1 -1
  314. package/dist/typed-render-graph-primitives.d.ts +35 -1
  315. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  316. package/dist/typed-shadow-passes.d.ts +3 -1
  317. package/dist/typed-shadow-passes.d.ts.map +1 -1
  318. package/dist/volume/temporal.d.ts +6 -0
  319. package/dist/volume/temporal.d.ts.map +1 -1
  320. package/package.json +21 -20
  321. package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
  322. package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
  323. package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
  324. package/src/__tests__/auto-exposure.browser.test.ts +33 -0
  325. package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
  326. package/src/__tests__/detached-inspection.integration.test.ts +5 -0
  327. package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
  328. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
  329. package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
  330. package/src/__tests__/dynamic-geometry-host.unit.test.ts +1123 -0
  331. package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
  332. package/src/__tests__/factory-contract.integration.test.ts +73 -22
  333. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
  334. package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
  335. package/src/__tests__/feature-noise.unit.test.ts +38 -0
  336. package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
  337. package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
  338. package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
  339. package/src/__tests__/frame-targets.unit.test.ts +24 -0
  340. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
  341. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
  342. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
  343. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
  344. package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
  345. package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
  346. package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
  347. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
  348. package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
  349. package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
  350. package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
  351. package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
  352. package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
  353. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
  354. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
  355. package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
  356. package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
  357. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
  358. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
  359. package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
  360. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
  361. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
  362. package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
  363. package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
  364. package/src/__tests__/gpu-scene.unit.test.ts +559 -33
  365. package/src/__tests__/instances-culling.integration.test.ts +36 -10
  366. package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
  367. package/src/__tests__/instances-store.unit.test.ts +242 -39
  368. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
  369. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
  370. package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
  371. package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
  372. package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
  373. package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
  374. package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
  375. package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
  376. package/src/__tests__/material-texture-source.unit.test.ts +87 -0
  377. package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
  378. package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
  379. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
  380. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +53 -0
  381. package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
  382. package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
  383. package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
  384. package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
  385. package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
  386. package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
  387. package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
  388. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
  389. package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
  390. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
  391. package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
  392. package/src/__tests__/public-surface.test-d.ts +3 -0
  393. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
  394. package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
  395. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
  396. package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
  397. package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
  398. package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
  399. package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
  400. package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
  401. package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
  402. package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
  403. package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
  404. package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
  405. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
  406. package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
  407. package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
  408. package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
  409. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
  410. package/src/__tests__/render-target-material-source.test-d.ts +27 -3
  411. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +61 -1
  412. package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
  413. package/src/__tests__/shader-manifest-fixture.ts +39 -0
  414. package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
  415. package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
  416. package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
  417. package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
  418. package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
  419. package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
  420. package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
  421. package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
  422. package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
  423. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
  424. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
  425. package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
  426. package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
  427. package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
  428. package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
  429. package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
  430. package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
  431. package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
  432. package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
  433. package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
  434. package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
  435. package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
  436. package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
  437. package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
  438. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
  439. package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
  440. package/src/__tests__/ssr-reprojection-probe.ts +90 -0
  441. package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
  442. package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
  443. package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
  444. package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
  445. package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
  446. package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
  447. package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
  448. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
  449. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
  450. package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
  451. package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
  452. package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
  453. package/src/__tests__/standard-output.browser.test.ts +15 -0
  454. package/src/__tests__/standard-output.dawn.test.ts +15 -0
  455. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
  456. package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
  457. package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
  458. package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
  459. package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
  460. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
  461. package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
  462. package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
  463. package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
  464. package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
  465. package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
  466. package/src/__tests__/taa-stability-feedback.ts +341 -0
  467. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
  468. package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
  469. package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
  470. package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
  471. package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
  472. package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
  473. package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
  474. package/src/__tests__/visibility-extract.perf.test.ts +20 -8
  475. package/src/__tests__/visibility-instances.integration.test.ts +19 -7
  476. package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
  477. package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
  478. package/src/assembly/dynamic-geometry-host.ts +887 -0
  479. package/src/assembly/dynamic-geometry-runtime.ts +71 -0
  480. package/src/assembly/factory.ts +4 -0
  481. package/src/assembly/gpu-driven-owner.ts +29 -0
  482. package/src/assembly/host-contract.ts +36 -0
  483. package/src/assembly/material/assembly.ts +38 -0
  484. package/src/assembly/material-shader-policy.ts +71 -28
  485. package/src/assembly/recovery/failure-location.ts +62 -0
  486. package/src/assembly/recovery/renderer-recover.ts +19 -17
  487. package/src/assembly/renderer-facade.ts +29 -0
  488. package/src/assembly/renderer-feature-inspection.ts +13 -0
  489. package/src/assembly/renderer-frame-transaction.ts +63 -0
  490. package/src/assembly/renderer-helpers.ts +26 -0
  491. package/src/assembly/renderer-inspection.ts +6 -0
  492. package/src/assembly/shader-prewarm-policy.ts +32 -0
  493. package/src/assembly/skin-palette-owner.ts +21 -0
  494. package/src/assembly/ssr-shader-prewarm.ts +41 -0
  495. package/src/assembly/webgpu-device-loss.ts +75 -0
  496. package/src/assembly/webgpu-pbr-ready.ts +232 -0
  497. package/src/assembly/webgpu-ready-mipmap.ts +26 -0
  498. package/src/assembly/webgpu-ready.ts +142 -121
  499. package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
  500. package/src/assembly/webgpu-renderer-contract.ts +69 -0
  501. package/src/assembly/webgpu-renderer-guards.ts +32 -0
  502. package/src/assembly/webgpu-renderer-observation.ts +43 -0
  503. package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
  504. package/src/assembly/webgpu-renderer.ts +675 -614
  505. package/src/assembly/webgpu-vertex-layouts.ts +233 -87
  506. package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
  507. package/src/components/camera.ts +287 -7
  508. package/src/components/index.ts +1 -0
  509. package/src/components/instances.ts +45 -18
  510. package/src/components/scene-instance.ts +2 -1
  511. package/src/components/screen-space-reflection.ts +21 -0
  512. package/src/components/sprite-animation.ts +3 -3
  513. package/src/components/sprite-instances.ts +1 -1
  514. package/src/device/gpu-residency.ts +96 -93
  515. package/src/device/mesh-residency-lifetime.ts +73 -0
  516. package/src/dynamic-geometry.ts +736 -0
  517. package/src/environment/background.ts +263 -0
  518. package/src/errors/gpu-driven.ts +39 -5
  519. package/src/errors/render.ts +27 -3
  520. package/src/extract/camera.ts +26 -1
  521. package/src/extract/gpu-driven.ts +185 -10
  522. package/src/features/host.ts +237 -19
  523. package/src/features/noise-texture.ts +91 -0
  524. package/src/features/plan.ts +553 -24
  525. package/src/features/prepared-gpu-work.ts +631 -49
  526. package/src/features/prepared-graphics-store.ts +31 -0
  527. package/src/features/prepared-graphics.ts +5 -0
  528. package/src/features/render-graph-compute.ts +41 -8
  529. package/src/features/render-graph-contribution.ts +141 -3
  530. package/src/features/render-graph-raster.ts +143 -30
  531. package/src/features/types.ts +11 -0
  532. package/src/fullscreen-post-process-pass.ts +3 -0
  533. package/src/gpu-driven/batch-topology.ts +424 -102
  534. package/src/gpu-driven/pbr-program.ts +31 -0
  535. package/src/gpu-driven/prepared-draw.ts +29 -4
  536. package/src/gpu-driven/production-raster.ts +2267 -694
  537. package/src/gpu-driven/shadow-views.ts +539 -0
  538. package/src/gpu-driven/view-gpu.ts +188 -41
  539. package/src/gpu-scene-schema.ts +52 -11
  540. package/src/gpu-scene.ts +186 -99
  541. package/src/gpu-texture-usage.ts +1 -0
  542. package/src/hdrp-buffers.ts +72 -36
  543. package/src/ibl/frame-resources.ts +38 -0
  544. package/src/ibl/skylight-bind-group.ts +5 -4
  545. package/src/index.ts +125 -1
  546. package/src/inspection-types.ts +191 -0
  547. package/src/instances-derived-bounds.ts +258 -34
  548. package/src/instances.ts +459 -150
  549. package/src/internal.ts +22 -0
  550. package/src/lifecycle.ts +2 -0
  551. package/src/material-row.ts +145 -0
  552. package/src/materials.ts +44 -8
  553. package/src/pbr-pipeline.ts +159 -17
  554. package/src/pipeline/standard-forward-lane.ts +52 -24
  555. package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
  556. package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
  557. package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
  558. package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
  559. package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
  560. package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
  561. package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
  562. package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
  563. package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
  564. package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
  565. package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
  566. package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
  567. package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
  568. package/src/pipeline/standard-output/color-transform.ts +124 -0
  569. package/src/pipeline/standard-output/graph.ts +139 -0
  570. package/src/pipeline/standard-output/lut-admission.ts +121 -0
  571. package/src/pipeline/standard-output/lut-gpu.ts +399 -0
  572. package/src/pipeline/standard-output/lut-state.ts +176 -0
  573. package/src/pipeline/standard-output/resources.ts +121 -0
  574. package/src/pipeline/standard-output/types.ts +58 -0
  575. package/src/pipeline/standard-pipeline.ts +192 -59
  576. package/src/pipeline/standard-post.ts +186 -27
  577. package/src/pipeline-builder.ts +16 -6
  578. package/src/pipeline-spec-types.ts +4 -0
  579. package/src/pipeline-spec.ts +37 -29
  580. package/src/points-lines/standard-owner.ts +6 -14
  581. package/src/prepare/prepared-graphics-resolver.ts +9 -2
  582. package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
  583. package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
  584. package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
  585. package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
  586. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
  587. package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
  588. package/src/record/dynamic-geometry-consumption.ts +59 -0
  589. package/src/record/frame-lighting.ts +12 -11
  590. package/src/record/frame-snapshot.ts +133 -23
  591. package/src/record/frame-targets.ts +9 -0
  592. package/src/record/frame.ts +727 -133
  593. package/src/record/main-pass-geometry.ts +162 -98
  594. package/src/record/main-pass-material.ts +115 -26
  595. package/src/record/main-pass-sprite-draws.ts +18 -107
  596. package/src/record/main-pass.ts +78 -8
  597. package/src/record/material-uniforms.ts +1 -1
  598. package/src/record/mesh-ssbo.ts +59 -15
  599. package/src/record/prepared-material-bindings.ts +234 -0
  600. package/src/record/recovery-pipeline.ts +59 -136
  601. package/src/record/render-context.ts +92 -22
  602. package/src/record/shadow-pass.ts +1231 -209
  603. package/src/record/skybox-post-pass.ts +14 -3
  604. package/src/record/typed-frame-graph.ts +1006 -53
  605. package/src/record/view-ubo.ts +11 -0
  606. package/src/recovery/render-system-candidate.ts +90 -66
  607. package/src/recovery/render-system-roots.ts +113 -0
  608. package/src/recovery/types.ts +77 -0
  609. package/src/reflection/record-owner.ts +26 -54
  610. package/src/render-contract.ts +219 -25
  611. package/src/render-graph-primitives.ts +6 -2
  612. package/src/render-pipeline.ts +70 -1
  613. package/src/render-system-extract-tail.ts +409 -160
  614. package/src/render-system-extract.ts +376 -102
  615. package/src/render-system-projections.ts +241 -0
  616. package/src/render-system.ts +692 -791
  617. package/src/scene/render-scene-types.ts +13 -4
  618. package/src/scene/render-scene.ts +569 -355
  619. package/src/scene/shadow-visibility.ts +21 -0
  620. package/src/ssr/admission.ts +380 -14
  621. package/src/ssr/compose.ts +144 -0
  622. package/src/ssr/errors.ts +48 -0
  623. package/src/ssr/graph.ts +777 -0
  624. package/src/ssr/history.ts +436 -0
  625. package/src/ssr/hiz.ts +124 -0
  626. package/src/ssr/inspection.ts +124 -0
  627. package/src/ssr/resources.ts +202 -0
  628. package/src/ssr/temporal.ts +164 -0
  629. package/src/systems/skin-palette-allocator.ts +495 -148
  630. package/src/systems/skin-palette-types.ts +17 -0
  631. package/src/systems/transparent-dispatch.ts +90 -0
  632. package/src/targets/material-source.ts +34 -0
  633. package/src/temporal/coverage.ts +1 -1
  634. package/src/temporal/frame-coordinator.ts +23 -2
  635. package/src/temporal/gpu.ts +74 -12
  636. package/src/temporal/history.ts +3 -1
  637. package/src/temporal/inspection.ts +2 -1
  638. package/src/temporal/standard-scene-data.ts +3 -1
  639. package/src/temporal/temporal-view.ts +3 -0
  640. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
  641. package/src/typed-render-graph-primitives.ts +266 -74
  642. package/src/typed-shadow-passes.ts +122 -12
  643. package/src/volume/temporal.ts +10 -0
  644. package/dist/chunk-ADHHVYLW.mjs.map +0 -1
  645. package/dist/chunk-FJ6P52EE.mjs.map +0 -1
  646. package/dist/chunk-GE3SDD72.mjs.map +0 -1
  647. package/dist/chunk-GF523LHF.mjs.map +0 -1
  648. package/dist/chunk-HKXTW355.mjs.map +0 -1
  649. package/dist/chunk-JDWARUOI.mjs.map +0 -1
  650. package/dist/chunk-KM2NOX2I.mjs.map +0 -1
  651. package/dist/chunk-TMIASV2N.mjs.map +0 -1
  652. package/dist/chunk-XDP6XE5V.mjs.map +0 -1
  653. package/dist/chunk-ZZ4YQ474.mjs.map +0 -1
  654. package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
  655. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
@@ -0,0 +1,736 @@
1
+ import type { Handle, MeshAsset } from '@forgeax/engine-types';
2
+ import { err, ok, type Result } from '@forgeax/engine-types';
3
+
4
+ /** A standard MeshAsset candidate prepared for one attached World generation. */
5
+ export interface DynamicGeometryPrepareInput {
6
+ readonly world: object;
7
+ /** Live ECS entity carrying MeshFilter + MeshRenderer on the attached World. */
8
+ readonly entity?: number;
9
+ /** Optional derived-physics entity whose fixed-step publication gates draw. */
10
+ readonly physicsEntity?: number;
11
+ readonly mesh: MeshAsset;
12
+ /** Existing World.sharedRefs handle whose standard GPU residency is prepared. */
13
+ readonly meshHandle?: Handle<'MeshAsset', 'shared'>;
14
+ readonly revision: number;
15
+ /** Material identity is a consumer fact; MaterialAsset stays in the World. */
16
+ readonly materialIdentity?: string;
17
+ /** Topology changes invalidate motion/history consumers. */
18
+ readonly topologyRevision?: number;
19
+ /** Captured by the Renderer host from the World FixedTime resource. */
20
+ readonly fixedStep?: number;
21
+ }
22
+
23
+ /** Explicit ECS/fixed-step ordering proof used by Renderer admission. */
24
+ export interface DynamicGeometryOrdering {
25
+ readonly world: object;
26
+ readonly fixedStep: number;
27
+ }
28
+
29
+ export type DynamicGeometryCandidateState =
30
+ | 'prepared'
31
+ | 'accepted'
32
+ | 'published'
33
+ | 'cancelled'
34
+ | 'retired';
35
+
36
+ export interface DynamicGeometryCandidate {
37
+ readonly candidateId: string;
38
+ readonly generation: number;
39
+ readonly owner: object;
40
+ readonly world: object;
41
+ readonly entity?: number;
42
+ readonly physicsEntity?: number;
43
+ /** False when the pure lifecycle was used without a shared MeshAsset handle. */
44
+ readonly gpuReady: boolean;
45
+ /** CPU/GPU geometry bytes reserved by this candidate budget. */
46
+ readonly meshBytes: number;
47
+ readonly revision: number;
48
+ readonly topologyRevision: number;
49
+ readonly materialIdentity?: string;
50
+ readonly meshHandle?: Handle<'MeshAsset', 'shared'>;
51
+ readonly fixedStep?: number;
52
+ readonly mesh: MeshAsset;
53
+ readonly state: DynamicGeometryCandidateState;
54
+ }
55
+
56
+ /** Receipt that ties candidate publication to the sole Renderer FrameReceipt. */
57
+ export interface DynamicGeometryReceipt {
58
+ readonly candidateId: string;
59
+ readonly generation: number;
60
+ readonly revision: number;
61
+ readonly topologyRevision: number;
62
+ readonly fixedStep?: number;
63
+ readonly frameId: number;
64
+ readonly deviceGeneration: number;
65
+ readonly frame: {
66
+ readonly frameId: number;
67
+ readonly deviceGeneration: number;
68
+ readonly completed?: Promise<unknown>;
69
+ };
70
+ /** Whether the Renderer record stage consumed this candidate for this frame. */
71
+ readonly recordStageConsumed: boolean;
72
+ /** Actual frame fixed step; may be newer than the preparation step. */
73
+ readonly publicationFixedStep?: number;
74
+ }
75
+
76
+ export interface DynamicGeometryInspection {
77
+ readonly generation: number;
78
+ readonly prepared: number;
79
+ readonly accepted: number;
80
+ readonly published: number;
81
+ readonly retired: number;
82
+ readonly latestRevision?: number;
83
+ readonly latestFrameId?: number;
84
+ readonly historyInvalidations: number;
85
+ readonly invalidated: number;
86
+ readonly meshBytes: number;
87
+ readonly maxMeshBytes: number;
88
+ }
89
+
90
+ export type DynamicGeometryErrorCode =
91
+ | 'dynamic-geometry-disposed'
92
+ | 'dynamic-geometry-world-not-attached'
93
+ | 'dynamic-geometry-invalid'
94
+ | 'dynamic-geometry-gpu-failed'
95
+ | 'dynamic-geometry-gpu-not-ready'
96
+ | 'dynamic-geometry-budget-exceeded'
97
+ | 'dynamic-geometry-stale'
98
+ | 'dynamic-geometry-generation-mismatch'
99
+ | 'dynamic-geometry-candidate-not-found'
100
+ | 'dynamic-geometry-candidate-state'
101
+ | 'dynamic-geometry-receipt-mismatch'
102
+ | 'dynamic-geometry-ordering-required';
103
+
104
+ export interface DynamicGeometryErrorDetail {
105
+ readonly code: DynamicGeometryErrorCode;
106
+ readonly candidateId?: string;
107
+ readonly revision?: number;
108
+ readonly generation?: number;
109
+ readonly actual?: unknown;
110
+ }
111
+
112
+ export class DynamicGeometryError extends Error {
113
+ readonly code: DynamicGeometryErrorCode;
114
+ readonly expected: string;
115
+ readonly hint: string;
116
+ readonly detail: DynamicGeometryErrorDetail;
117
+
118
+ constructor(
119
+ code: DynamicGeometryErrorCode,
120
+ expected: string,
121
+ hint: string,
122
+ detail: Omit<DynamicGeometryErrorDetail, 'code'> = {},
123
+ ) {
124
+ super(`${code}: ${expected}`);
125
+ this.name = 'DynamicGeometryError';
126
+ this.code = code;
127
+ this.expected = expected;
128
+ this.hint = hint;
129
+ this.detail = Object.freeze({ code, ...detail });
130
+ }
131
+ }
132
+
133
+ interface CandidateRecord {
134
+ readonly world: object;
135
+ candidate: DynamicGeometryCandidate;
136
+ /** Published history remains queryable for its completion fence, but a
137
+ * newer topology must prevent that history from being consumed again. */
138
+ historyInvalidated: boolean;
139
+ acceptedFrame?: { readonly frameId: number; readonly deviceGeneration: number };
140
+ }
141
+
142
+ /**
143
+ * A public candidate is a credential, not a mutable state object. Keep the
144
+ * owner/world check in `current` cheap, then compare every scalar admission
145
+ * fact before a caller can cancel, retire, or inspect another record by
146
+ * copying its candidate id. The renderer-owned MeshAsset copy remains the
147
+ * content source for the actual draw.
148
+ */
149
+ export function sameCandidateCredential(
150
+ candidate: DynamicGeometryCandidate,
151
+ committed: DynamicGeometryCandidate,
152
+ ): boolean {
153
+ return (
154
+ candidate.candidateId === committed.candidateId &&
155
+ candidate.owner === committed.owner &&
156
+ candidate.world === committed.world &&
157
+ candidate.generation === committed.generation &&
158
+ candidate.entity === committed.entity &&
159
+ candidate.physicsEntity === committed.physicsEntity &&
160
+ candidate.gpuReady === committed.gpuReady &&
161
+ candidate.meshBytes === committed.meshBytes &&
162
+ candidate.revision === committed.revision &&
163
+ candidate.topologyRevision === committed.topologyRevision &&
164
+ candidate.materialIdentity === committed.materialIdentity &&
165
+ candidate.meshHandle === committed.meshHandle &&
166
+ candidate.fixedStep === committed.fixedStep
167
+ );
168
+ }
169
+
170
+ function cloneTypedArray<T extends Float32Array | Uint16Array | Uint32Array>(value: T): T {
171
+ return new (value.constructor as new (source: T) => T)(value);
172
+ }
173
+
174
+ function cloneMesh(mesh: MeshAsset): MeshAsset {
175
+ const attributes = Object.fromEntries(
176
+ Object.entries(mesh.attributes).map(([key, value]) => [
177
+ key,
178
+ value instanceof ArrayBuffer
179
+ ? value.slice(0)
180
+ : value === undefined
181
+ ? undefined
182
+ : cloneTypedArray(value as never),
183
+ ]),
184
+ ) as MeshAsset['attributes'];
185
+ return Object.freeze({
186
+ ...mesh,
187
+ vertices: new Float32Array(mesh.vertices),
188
+ ...(mesh.indices === undefined ? {} : { indices: cloneTypedArray(mesh.indices) }),
189
+ attributes,
190
+ ...(mesh.aabb === undefined ? {} : { aabb: new Float32Array(mesh.aabb) }),
191
+ submeshes: Object.freeze(mesh.submeshes.map((submesh) => Object.freeze({ ...submesh }))),
192
+ materialSlots: Object.freeze(mesh.materialSlots.map((slot) => Object.freeze({ ...slot }))),
193
+ });
194
+ }
195
+
196
+ function cloneCandidate(candidate: DynamicGeometryCandidate): DynamicGeometryCandidate {
197
+ return Object.freeze({ ...candidate, mesh: cloneMesh(candidate.mesh) });
198
+ }
199
+
200
+ function validateMesh(mesh: MeshAsset): string | undefined {
201
+ if (mesh.kind !== 'mesh') return 'MeshAsset.kind must be mesh';
202
+ if (
203
+ !(mesh.vertices instanceof Float32Array) ||
204
+ mesh.vertices.length === 0 ||
205
+ mesh.vertices.length % 3 !== 0
206
+ ) {
207
+ return 'MeshAsset.vertices must be a non-empty Float32Array with xyz triples';
208
+ }
209
+ if (
210
+ mesh.indices !== undefined &&
211
+ !(mesh.indices instanceof Uint16Array || mesh.indices instanceof Uint32Array)
212
+ ) {
213
+ return 'MeshAsset.indices must be Uint16Array or Uint32Array';
214
+ }
215
+ if (mesh.submeshes.length === 0)
216
+ return 'MeshAsset.submeshes must contain at least one topology range';
217
+ if (mesh.materialSlots.length < mesh.submeshes.length) {
218
+ return 'MeshAsset.materialSlots must cover every submesh';
219
+ }
220
+ if (mesh.vertices.some((value) => !Number.isFinite(value)))
221
+ return 'MeshAsset.vertices must be finite';
222
+ return undefined;
223
+ }
224
+
225
+ function meshByteSize(mesh: MeshAsset): number {
226
+ let bytes = mesh.vertices.byteLength + (mesh.indices?.byteLength ?? 0);
227
+ for (const value of Object.values(mesh.attributes)) {
228
+ if (value instanceof ArrayBuffer) bytes += value.byteLength;
229
+ else if (value !== undefined && ArrayBuffer.isView(value)) bytes += value.byteLength;
230
+ }
231
+ if (mesh.aabb !== undefined) bytes += mesh.aabb.byteLength;
232
+ // Submesh/material metadata is intentionally bounded by the candidate
233
+ // count. The reserved byte budget is for typed geometry and GPU upload
234
+ // inputs, which are the resources that can exhaust the renderer.
235
+ return bytes;
236
+ }
237
+
238
+ /**
239
+ * Renderer-owned lifecycle for standard MeshAsset candidates. It deliberately
240
+ * has no device, buffer, queue, or frame clock; the host supplies generation
241
+ * and FrameReceipt facts at the boundaries below.
242
+ */
243
+ export interface DynamicGeometryLifecycle {
244
+ prepare(
245
+ input: DynamicGeometryPrepareInput,
246
+ generation: number,
247
+ ): Result<DynamicGeometryCandidate, DynamicGeometryError>;
248
+ accept(
249
+ candidate: DynamicGeometryCandidate,
250
+ ordering?: DynamicGeometryOrdering,
251
+ ): Result<DynamicGeometryCandidate, DynamicGeometryError>;
252
+ cancel(
253
+ candidate: DynamicGeometryCandidate,
254
+ completion?: Promise<unknown>,
255
+ ): Result<void, DynamicGeometryError>;
256
+ retire(candidate: DynamicGeometryCandidate): Result<void, DynamicGeometryError>;
257
+ /** Release the count/byte budget after receipt-bound GPU cleanup completes. */
258
+ finalizeRetirement(candidate: DynamicGeometryCandidate): Result<void, DynamicGeometryError>;
259
+ receipt(candidate: DynamicGeometryCandidate): DynamicGeometryReceipt | undefined;
260
+ publishFrame(
261
+ frame: {
262
+ readonly frameId: number;
263
+ readonly deviceGeneration: number;
264
+ readonly completed?: Promise<unknown>;
265
+ },
266
+ worlds?: readonly object[],
267
+ fixedStep?: number,
268
+ canPublish?: (candidate: DynamicGeometryCandidate) => boolean,
269
+ ): readonly DynamicGeometryReceipt[];
270
+ invalidateWorld(world: object, completion?: Promise<unknown>): void;
271
+ invalidateGeneration(generation: number): void;
272
+ inspect(): DynamicGeometryInspection;
273
+ dispose(): void;
274
+ }
275
+
276
+ export function createDynamicGeometryLifecycle(
277
+ maxCandidates = 32,
278
+ maxMeshBytes = 64 * 1024 * 1024,
279
+ ): DynamicGeometryLifecycle {
280
+ const records = new Map<string, CandidateRecord>();
281
+ const owner = {};
282
+ let sequence = 0;
283
+ let currentGeneration = 0;
284
+ let disposed = false;
285
+ let historyInvalidations = 0;
286
+ let latestRevision: number | undefined;
287
+ let latestFrameId: number | undefined;
288
+ let retired = 0;
289
+ let invalidated = 0;
290
+ let meshBytes = 0;
291
+ const latestRevisionByWorld = new WeakMap<object, Map<number | undefined, number>>();
292
+ const latestTopologyRevisionByWorld = new WeakMap<object, Map<number | undefined, number>>();
293
+ const publishedReceipts = new Map<string, DynamicGeometryReceipt>();
294
+
295
+ const failure = (
296
+ code: DynamicGeometryErrorCode,
297
+ expected: string,
298
+ hint: string,
299
+ detail: Omit<DynamicGeometryErrorDetail, 'code'> = {},
300
+ ): Result<never, DynamicGeometryError> =>
301
+ err(new DynamicGeometryError(code, expected, hint, detail));
302
+
303
+ const current = (candidate: DynamicGeometryCandidate): CandidateRecord | undefined => {
304
+ const record = records.get(candidate.candidateId);
305
+ if (
306
+ record === undefined ||
307
+ candidate.owner !== owner ||
308
+ record.candidate.owner !== candidate.owner ||
309
+ record.candidate.world !== candidate.world ||
310
+ record.candidate.generation !== candidate.generation
311
+ )
312
+ return undefined;
313
+ return record;
314
+ };
315
+
316
+ return {
317
+ prepare(input, generation) {
318
+ if (disposed)
319
+ return failure('dynamic-geometry-disposed', 'renderer is alive', 'rebuild the Renderer');
320
+ if (input.world === null || typeof input.world !== 'object') {
321
+ return failure(
322
+ 'dynamic-geometry-world-not-attached',
323
+ 'candidate belongs to an attached World',
324
+ 'attach the World before preparing geometry',
325
+ );
326
+ }
327
+ if (!Number.isInteger(input.revision) || input.revision < 0) {
328
+ return failure(
329
+ 'dynamic-geometry-invalid',
330
+ 'geometry revision is a non-negative integer',
331
+ 'advance the consumer revision monotonically',
332
+ { revision: input.revision },
333
+ );
334
+ }
335
+ if (generation !== currentGeneration && currentGeneration !== 0) {
336
+ return failure(
337
+ 'dynamic-geometry-generation-mismatch',
338
+ 'candidate uses the active renderer generation',
339
+ 'discard stale generation work and prepare again',
340
+ { generation, actual: currentGeneration },
341
+ );
342
+ }
343
+ if (records.size >= maxCandidates) {
344
+ return failure(
345
+ 'dynamic-geometry-budget-exceeded',
346
+ 'candidate count remains within the bounded renderer budget',
347
+ 'cancel or retire an earlier candidate before retrying',
348
+ { actual: records.size },
349
+ );
350
+ }
351
+ const invalid = validateMesh(input.mesh);
352
+ if (invalid !== undefined)
353
+ return failure(
354
+ 'dynamic-geometry-invalid',
355
+ 'MeshAsset satisfies the standard geometry contract',
356
+ invalid,
357
+ );
358
+ const candidateBytes = meshByteSize(input.mesh);
359
+ if (
360
+ !Number.isSafeInteger(candidateBytes) ||
361
+ candidateBytes < 0 ||
362
+ meshBytes > maxMeshBytes - candidateBytes
363
+ ) {
364
+ return failure(
365
+ 'dynamic-geometry-budget-exceeded',
366
+ 'candidate mesh bytes remain within the bounded renderer resource budget',
367
+ 'cancel or retire an earlier candidate before retrying',
368
+ { actual: { meshBytes, candidateBytes, maxMeshBytes } },
369
+ );
370
+ }
371
+ const candidateId = `mesh:${generation}:${++sequence}:${input.revision}`;
372
+ const candidate: DynamicGeometryCandidate = Object.freeze({
373
+ candidateId,
374
+ generation,
375
+ owner,
376
+ world: input.world,
377
+ ...(input.entity === undefined ? {} : { entity: input.entity }),
378
+ ...(input.physicsEntity === undefined ? {} : { physicsEntity: input.physicsEntity }),
379
+ gpuReady: input.meshHandle !== undefined,
380
+ meshBytes: candidateBytes,
381
+ revision: input.revision,
382
+ topologyRevision: input.topologyRevision ?? input.revision,
383
+ ...(input.materialIdentity === undefined
384
+ ? {}
385
+ : { materialIdentity: input.materialIdentity }),
386
+ ...(input.meshHandle === undefined ? {} : { meshHandle: input.meshHandle }),
387
+ ...(input.fixedStep === undefined ? {} : { fixedStep: input.fixedStep }),
388
+ mesh: cloneMesh(input.mesh),
389
+ state: 'prepared',
390
+ });
391
+ records.set(candidateId, {
392
+ world: input.world,
393
+ candidate,
394
+ historyInvalidated: false,
395
+ });
396
+ meshBytes += candidateBytes;
397
+ currentGeneration = generation;
398
+ // Keep the credential handed to the consumer separate from the
399
+ // renderer-owned copy. Mesh typed arrays remain mutable at runtime even
400
+ // when exposed through a readonly TypeScript field.
401
+ return ok(cloneCandidate(candidate));
402
+ },
403
+ accept(candidate, ordering) {
404
+ if (disposed)
405
+ return failure('dynamic-geometry-disposed', 'renderer is alive', 'rebuild the Renderer');
406
+ const record = current(candidate);
407
+ if (record === undefined)
408
+ return failure(
409
+ 'dynamic-geometry-candidate-not-found',
410
+ 'candidate is owned by this renderer',
411
+ 'discard the stale credential',
412
+ );
413
+ if (candidate.generation !== currentGeneration) {
414
+ return failure(
415
+ 'dynamic-geometry-generation-mismatch',
416
+ 'candidate generation matches the active renderer generation',
417
+ 'prepare against the current renderer generation',
418
+ { candidateId: candidate.candidateId },
419
+ );
420
+ }
421
+ if (record.candidate.state !== 'prepared') {
422
+ return failure(
423
+ 'dynamic-geometry-candidate-state',
424
+ 'candidate is prepared exactly once before acceptance',
425
+ 'retain the accepted or published receipt',
426
+ { candidateId: candidate.candidateId },
427
+ );
428
+ }
429
+ if (!sameCandidateCredential(candidate, record.candidate)) {
430
+ return failure(
431
+ 'dynamic-geometry-receipt-mismatch',
432
+ 'acceptance uses the exact prepared candidate credential',
433
+ 'discard the altered credential and use the value returned by prepare',
434
+ { candidateId: candidate.candidateId },
435
+ );
436
+ }
437
+ if (ordering !== undefined) {
438
+ if (
439
+ ordering.world !== record.world ||
440
+ !Number.isInteger(ordering.fixedStep) ||
441
+ ordering.fixedStep < 0
442
+ ) {
443
+ return failure(
444
+ 'dynamic-geometry-ordering-required',
445
+ 'acceptance ordering names the candidate World and a non-negative fixed step',
446
+ 'submit the PhysicsWorld publication ordering for the same attached World',
447
+ { candidateId: candidate.candidateId, actual: ordering },
448
+ );
449
+ }
450
+ }
451
+ const newest = [...records.values()]
452
+ .filter(
453
+ (entry) =>
454
+ entry.candidate.candidateId !== candidate.candidateId &&
455
+ entry.world === record.world &&
456
+ entry.candidate.entity === record.candidate.entity &&
457
+ (entry.candidate.state === 'prepared' || entry.candidate.state === 'accepted'),
458
+ )
459
+ .map((entry) => entry.candidate.revision)
460
+ .concat(latestRevisionByWorld.get(record.world)?.get(record.candidate.entity) ?? -1)
461
+ .sort((a, b) => b - a)[0];
462
+ if (newest !== undefined && record.candidate.revision <= newest) {
463
+ return failure(
464
+ 'dynamic-geometry-stale',
465
+ 'accepted geometry revision is newer than the active candidate',
466
+ 'advance topology revision before accepting',
467
+ { candidateId: candidate.candidateId },
468
+ );
469
+ }
470
+ const accepted = Object.freeze({
471
+ ...record.candidate,
472
+ ...(ordering === undefined ? {} : { fixedStep: ordering.fixedStep }),
473
+ state: 'accepted' as const,
474
+ });
475
+ const previousTopologies = [...records.values()]
476
+ .filter(
477
+ (entry) =>
478
+ entry.candidate.candidateId !== candidate.candidateId &&
479
+ entry.world === record.world &&
480
+ entry.candidate.entity === record.candidate.entity,
481
+ )
482
+ .map((entry) => entry.candidate.topologyRevision);
483
+ const previousLatestTopology = latestTopologyRevisionByWorld
484
+ .get(record.world)
485
+ ?.get(record.candidate.entity);
486
+ const newestTopology = previousTopologies
487
+ .concat(previousLatestTopology === undefined ? [] : [previousLatestTopology])
488
+ .sort((a, b) => b - a)[0];
489
+ if (newestTopology !== undefined && accepted.topologyRevision < newestTopology) {
490
+ return failure(
491
+ 'dynamic-geometry-stale',
492
+ 'accepted topology revision is not older than the active geometry history',
493
+ 'advance topology revision before accepting a replacement candidate',
494
+ { candidateId: candidate.candidateId },
495
+ );
496
+ }
497
+ if (newestTopology !== undefined && accepted.topologyRevision > newestTopology) {
498
+ historyInvalidations += 1;
499
+ // Invalidate publication eligibility, not resource ownership. The host
500
+ // cancels superseded candidates with their allocation completion fence.
501
+ for (const previous of records.values()) {
502
+ if (
503
+ previous.world !== record.world ||
504
+ previous.candidate.entity !== record.candidate.entity ||
505
+ previous.candidate.candidateId === candidate.candidateId ||
506
+ previous.candidate.topologyRevision >= accepted.topologyRevision
507
+ )
508
+ continue;
509
+ if (!previous.historyInvalidated) {
510
+ previous.historyInvalidated = true;
511
+ invalidated += 1;
512
+ }
513
+ }
514
+ }
515
+ record.candidate = accepted;
516
+ return ok(cloneCandidate(accepted));
517
+ },
518
+ cancel(candidate, completion) {
519
+ const record = current(candidate);
520
+ if (record === undefined)
521
+ return failure(
522
+ 'dynamic-geometry-candidate-not-found',
523
+ 'candidate is owned by this renderer',
524
+ 'discard the stale credential',
525
+ );
526
+ if (!sameCandidateCredential(candidate, record.candidate))
527
+ return failure(
528
+ 'dynamic-geometry-receipt-mismatch',
529
+ 'cancellation uses the exact prepared candidate credential',
530
+ 'discard the altered credential and use the value returned by prepare',
531
+ { candidateId: candidate.candidateId },
532
+ );
533
+ if (record.candidate.state !== 'prepared' && record.candidate.state !== 'accepted') {
534
+ return failure(
535
+ 'dynamic-geometry-candidate-state',
536
+ 'only prepared or accepted geometry can be cancelled once',
537
+ 'retire published geometry or await the existing terminal cleanup',
538
+ );
539
+ }
540
+ record.candidate = Object.freeze({ ...record.candidate, state: 'cancelled' as const });
541
+ const release = (): void => {
542
+ if (records.get(candidate.candidateId) !== record) return;
543
+ records.delete(candidate.candidateId);
544
+ meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
545
+ };
546
+ if (completion === undefined) release();
547
+ else void completion.then(release, release);
548
+ return ok(undefined);
549
+ },
550
+ retire(candidate) {
551
+ const record = current(candidate);
552
+ if (record === undefined)
553
+ return failure(
554
+ 'dynamic-geometry-candidate-not-found',
555
+ 'retirement uses a published candidate owned by this renderer',
556
+ 'discard the stale credential and do not evict any unrelated mesh',
557
+ { candidateId: candidate.candidateId },
558
+ );
559
+ if (!sameCandidateCredential(candidate, record.candidate))
560
+ return failure(
561
+ 'dynamic-geometry-receipt-mismatch',
562
+ 'retirement uses the exact published candidate credential',
563
+ 'discard the altered credential and use the value returned by publish',
564
+ { candidateId: candidate.candidateId },
565
+ );
566
+ if (record.candidate.state !== 'published') {
567
+ return failure(
568
+ 'dynamic-geometry-candidate-state',
569
+ 'only published geometry can enter receipt-bound retirement',
570
+ 'publish the candidate from a submitted frame first',
571
+ );
572
+ }
573
+ record.candidate = Object.freeze({ ...record.candidate, state: 'retired' as const });
574
+ publishedReceipts.delete(candidate.candidateId);
575
+ retired += 1;
576
+ // Retiring is the logical state transition. Keep the candidate record
577
+ // and its typed bytes until the owner calls `finalizeRetirement` after
578
+ // every record-stage completion fence and GPU lease are gone. This is
579
+ // what makes the declared count/byte budget include in-flight work.
580
+ return ok(undefined);
581
+ },
582
+ finalizeRetirement(candidate) {
583
+ const record = current(candidate);
584
+ if (record === undefined)
585
+ return failure(
586
+ 'dynamic-geometry-candidate-not-found',
587
+ 'retirement finalization uses a candidate retained by this lifecycle',
588
+ 'discard the stale credential or finish the current receipt-bound retirement',
589
+ );
590
+ if (!sameCandidateCredential(candidate, record.candidate))
591
+ return failure(
592
+ 'dynamic-geometry-receipt-mismatch',
593
+ 'retirement finalization uses the exact published candidate credential',
594
+ 'discard the altered credential and use the value returned by publication',
595
+ { candidateId: candidate.candidateId },
596
+ );
597
+ if (record.candidate.state !== 'retired')
598
+ return failure(
599
+ 'dynamic-geometry-candidate-state',
600
+ 'only a retired candidate can release its deferred budget',
601
+ 'retire the published candidate before finalizing its receipt-bound cleanup',
602
+ { candidateId: candidate.candidateId },
603
+ );
604
+ records.delete(candidate.candidateId);
605
+ meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
606
+ return ok(undefined);
607
+ },
608
+ publishFrame(frame, worlds, fixedStep, canPublish) {
609
+ if (disposed || frame.deviceGeneration !== currentGeneration) return [];
610
+ const receipts: DynamicGeometryReceipt[] = [];
611
+ for (const record of records.values()) {
612
+ const canRepeatPublished =
613
+ record.candidate.state === 'published' &&
614
+ // Attached Renderer publication revalidates the entity binding in
615
+ // `canPublish`, so a history-invalidated candidate can still record
616
+ // a real draw for its live entity. Detached lifecycle publication
617
+ // remains one-shot once that history is invalidated.
618
+ (canPublish !== undefined || !record.historyInvalidated);
619
+ if (record.candidate.state !== 'accepted' && !canRepeatPublished) continue;
620
+ if (worlds !== undefined && !worlds.includes(record.world)) continue;
621
+ if (
622
+ record.candidate.fixedStep !== undefined &&
623
+ (fixedStep === undefined || record.candidate.fixedStep > fixedStep)
624
+ ) {
625
+ continue;
626
+ }
627
+ if (canPublish !== undefined && !canPublish(record.candidate)) continue;
628
+ record.candidate = Object.freeze({ ...record.candidate, state: 'published' as const });
629
+ const receipt = Object.freeze({
630
+ candidateId: record.candidate.candidateId,
631
+ generation: record.candidate.generation,
632
+ revision: record.candidate.revision,
633
+ topologyRevision: record.candidate.topologyRevision,
634
+ ...(record.candidate.fixedStep === undefined
635
+ ? {}
636
+ : { fixedStep: record.candidate.fixedStep }),
637
+ frameId: frame.frameId,
638
+ deviceGeneration: frame.deviceGeneration,
639
+ recordStageConsumed: canPublish !== undefined,
640
+ ...(fixedStep === undefined ? {} : { publicationFixedStep: fixedStep }),
641
+ frame: Object.freeze({ ...frame }),
642
+ });
643
+ receipts.push(receipt);
644
+ publishedReceipts.set(record.candidate.candidateId, receipt);
645
+ latestRevision = record.candidate.revision;
646
+ latestFrameId = frame.frameId;
647
+ if (!latestRevisionByWorld.has(record.world))
648
+ latestRevisionByWorld.set(record.world, new Map());
649
+ latestRevisionByWorld
650
+ .get(record.world)
651
+ ?.set(record.candidate.entity, record.candidate.revision);
652
+ if (!latestTopologyRevisionByWorld.has(record.world))
653
+ latestTopologyRevisionByWorld.set(record.world, new Map());
654
+ latestTopologyRevisionByWorld
655
+ .get(record.world)
656
+ ?.set(record.candidate.entity, record.candidate.topologyRevision);
657
+ }
658
+ return receipts;
659
+ },
660
+ invalidateWorld(world, completion) {
661
+ for (const [id, record] of records) {
662
+ if (record.world !== world) continue;
663
+ // An earlier terminal operation already owns this reservation's fence.
664
+ if (record.candidate.state === 'cancelled' || record.candidate.state === 'retired')
665
+ continue;
666
+ publishedReceipts.delete(id);
667
+ record.candidate = Object.freeze({ ...record.candidate, state: 'cancelled' as const });
668
+ const release = (): void => {
669
+ if (records.get(id) !== record) return;
670
+ records.delete(id);
671
+ meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
672
+ };
673
+ if (completion === undefined) release();
674
+ else void completion.then(release, release);
675
+ invalidated += 1;
676
+ }
677
+ latestRevisionByWorld.delete(world);
678
+ latestTopologyRevisionByWorld.delete(world);
679
+ },
680
+ invalidateGeneration(generation) {
681
+ if (generation === currentGeneration) return;
682
+ currentGeneration = generation;
683
+ const invalidatedWorlds = new Set<object>();
684
+ for (const [id, record] of records) {
685
+ if (record.candidate.generation !== generation) {
686
+ records.delete(id);
687
+ publishedReceipts.delete(id);
688
+ meshBytes = Math.max(0, meshBytes - record.candidate.meshBytes);
689
+ invalidatedWorlds.add(record.world);
690
+ invalidated += 1;
691
+ }
692
+ }
693
+ for (const world of invalidatedWorlds) {
694
+ latestRevisionByWorld.delete(world);
695
+ latestTopologyRevisionByWorld.delete(world);
696
+ }
697
+ },
698
+ receipt(candidate) {
699
+ const receipt = publishedReceipts.get(candidate.candidateId);
700
+ const record = records.get(candidate.candidateId);
701
+ if (record === undefined || !sameCandidateCredential(candidate, record.candidate)) {
702
+ return undefined;
703
+ }
704
+ return receipt?.generation === candidate.generation ? receipt : undefined;
705
+ },
706
+ inspect() {
707
+ let prepared = 0;
708
+ let accepted = 0;
709
+ let published = 0;
710
+ for (const record of records.values()) {
711
+ if (record.candidate.state === 'prepared') prepared += 1;
712
+ else if (record.candidate.state === 'accepted') accepted += 1;
713
+ else if (record.candidate.state === 'published') published += 1;
714
+ }
715
+ return Object.freeze({
716
+ generation: currentGeneration,
717
+ prepared,
718
+ accepted,
719
+ published,
720
+ retired,
721
+ ...(latestRevision === undefined ? {} : { latestRevision }),
722
+ ...(latestFrameId === undefined ? {} : { latestFrameId }),
723
+ historyInvalidations,
724
+ invalidated,
725
+ meshBytes,
726
+ maxMeshBytes,
727
+ });
728
+ },
729
+ dispose() {
730
+ disposed = true;
731
+ records.clear();
732
+ publishedReceipts.clear();
733
+ meshBytes = 0;
734
+ },
735
+ };
736
+ }