@forgeax/engine-render 0.1.21 → 0.1.24

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 (584) hide show
  1. package/README.md +281 -38
  2. package/dist/assembly/backend-contract.d.ts +13 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/bundler-contract.d.ts +9 -0
  5. package/dist/assembly/bundler-contract.d.ts.map +1 -0
  6. package/dist/assembly/device-feature-admission.d.ts +8 -0
  7. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  8. package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
  9. package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
  10. package/dist/assembly/factory.d.ts +7 -235
  11. package/dist/assembly/factory.d.ts.map +1 -1
  12. package/dist/assembly/host-contract.d.ts +10 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +3 -2
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
  17. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
  18. package/dist/assembly/material-shader-policy.d.ts +168 -0
  19. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  20. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  21. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  22. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  23. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  25. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  26. package/dist/assembly/recovery/generation.d.ts +46 -0
  27. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  28. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  29. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  30. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  31. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  32. package/dist/assembly/renderer-facade.d.ts +10 -0
  33. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  34. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  35. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  36. package/dist/assembly/renderer-helpers.d.ts +19 -0
  37. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  38. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  39. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  40. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  41. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  43. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-ready.d.ts +55 -0
  45. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  47. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  48. package/dist/authoring.mjs +1 -1
  49. package/dist/{chunk-PCHBPYML.mjs → chunk-GE3SDD72.mjs} +37 -5
  50. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  51. package/dist/chunk-KZFTK6DO.mjs +1191 -0
  52. package/dist/chunk-KZFTK6DO.mjs.map +1 -0
  53. package/dist/{chunk-FS2KNUQH.mjs → chunk-Q752SHI5.mjs} +332 -568
  54. package/dist/chunk-Q752SHI5.mjs.map +1 -0
  55. package/dist/{chunk-6OPXDQ2E.mjs → chunk-RR5L4SRS.mjs} +33 -294
  56. package/dist/chunk-RR5L4SRS.mjs.map +1 -0
  57. package/dist/chunk-TFH5I7JJ.mjs +296 -0
  58. package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
  59. package/dist/chunk-UQYS7FRM.mjs +1003 -0
  60. package/dist/chunk-UQYS7FRM.mjs.map +1 -0
  61. package/dist/chunk-YKB5DQV7.mjs +345 -0
  62. package/dist/chunk-YKB5DQV7.mjs.map +1 -0
  63. package/dist/{chunk-5YADUCA7.mjs → chunk-YRUYGA4A.mjs} +1189 -1217
  64. package/dist/chunk-YRUYGA4A.mjs.map +1 -0
  65. package/dist/chunk-Z3YYH2MB.mjs +5082 -0
  66. package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
  67. package/dist/cluster-binner.d.ts +5 -0
  68. package/dist/cluster-binner.d.ts.map +1 -1
  69. package/dist/components/index.d.ts +3 -0
  70. package/dist/components/index.d.ts.map +1 -1
  71. package/dist/components/light-helpers.d.ts +4 -0
  72. package/dist/components/light-helpers.d.ts.map +1 -1
  73. package/dist/components/light-probe.d.ts +9 -0
  74. package/dist/components/light-probe.d.ts.map +1 -0
  75. package/dist/components/rect-area-light.d.ts +12 -0
  76. package/dist/components/rect-area-light.d.ts.map +1 -0
  77. package/dist/components/scene-instance.d.ts +4 -0
  78. package/dist/components/scene-instance.d.ts.map +1 -1
  79. package/dist/components/spot-light.d.ts +3 -0
  80. package/dist/components/spot-light.d.ts.map +1 -1
  81. package/dist/construct-renderer.mjs +27276 -23232
  82. package/dist/construct-renderer.mjs.map +1 -1
  83. package/dist/device/device-scope.d.ts +2 -0
  84. package/dist/device/device-scope.d.ts.map +1 -1
  85. package/dist/device/gpu-residency.d.ts +32 -5
  86. package/dist/device/gpu-residency.d.ts.map +1 -1
  87. package/dist/device/lifecycle-transaction.d.ts +2 -0
  88. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  89. package/dist/environment/frame.d.ts +5 -0
  90. package/dist/environment/frame.d.ts.map +1 -1
  91. package/dist/environment/inspection.d.ts +7 -0
  92. package/dist/environment/inspection.d.ts.map +1 -1
  93. package/dist/environment/lifecycle.d.ts +20 -2
  94. package/dist/environment/lifecycle.d.ts.map +1 -1
  95. package/dist/errors/gpu-driven.d.ts +26 -0
  96. package/dist/errors/gpu-driven.d.ts.map +1 -0
  97. package/dist/errors/index.d.ts +1 -0
  98. package/dist/errors/index.d.ts.map +1 -1
  99. package/dist/errors/recover.d.ts +67 -3
  100. package/dist/errors/recover.d.ts.map +1 -1
  101. package/dist/errors/render.d.ts +39 -1
  102. package/dist/errors/render.d.ts.map +1 -1
  103. package/dist/extract/contracts.d.ts +899 -0
  104. package/dist/extract/contracts.d.ts.map +1 -0
  105. package/dist/extract/gpu-driven.d.ts +50 -0
  106. package/dist/extract/gpu-driven.d.ts.map +1 -0
  107. package/dist/extract/visibility.d.ts.map +1 -1
  108. package/dist/features/host.d.ts +2 -0
  109. package/dist/features/host.d.ts.map +1 -1
  110. package/dist/features/prepared-gpu-work.d.ts +3 -0
  111. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  112. package/dist/features/prepared-graphics-store.d.ts +2 -0
  113. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  114. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  115. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  116. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  117. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  118. package/dist/gpu-driven/production-raster.d.ts +126 -2
  119. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  120. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  121. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  122. package/dist/gpu-scene-schema.d.ts +14 -0
  123. package/dist/gpu-scene-schema.d.ts.map +1 -1
  124. package/dist/gpu-scene.d.ts +9 -1
  125. package/dist/gpu-scene.d.ts.map +1 -1
  126. package/dist/gpu-usage.d.ts +0 -1
  127. package/dist/gpu-usage.d.ts.map +1 -1
  128. package/dist/hdrp-buffers.d.ts +3 -3
  129. package/dist/hdrp-buffers.d.ts.map +1 -1
  130. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  131. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  132. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  133. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  134. package/dist/index.d.ts +13 -2
  135. package/dist/index.d.ts.map +1 -1
  136. package/dist/index.mjs +90 -5
  137. package/dist/index.mjs.map +1 -1
  138. package/dist/inspection-types.d.ts +163 -1
  139. package/dist/inspection-types.d.ts.map +1 -1
  140. package/dist/internal.d.ts +9 -0
  141. package/dist/internal.d.ts.map +1 -1
  142. package/dist/internal.mjs +23 -2
  143. package/dist/internal.mjs.map +1 -1
  144. package/dist/light-buffer-layout.d.ts +60 -148
  145. package/dist/light-buffer-layout.d.ts.map +1 -1
  146. package/dist/materials.d.ts +34 -10
  147. package/dist/materials.d.ts.map +1 -1
  148. package/dist/mesh-material-bindings.d.ts +56 -0
  149. package/dist/mesh-material-bindings.d.ts.map +1 -1
  150. package/dist/occlusion-proxy.d.ts +4 -0
  151. package/dist/occlusion-proxy.d.ts.map +1 -0
  152. package/dist/pbr-pipeline.d.ts +58 -21
  153. package/dist/pbr-pipeline.d.ts.map +1 -1
  154. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  155. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
  156. package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
  157. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
  158. package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
  159. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
  160. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
  161. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  162. package/dist/pipeline-builder.d.ts +2 -0
  163. package/dist/pipeline-builder.d.ts.map +1 -1
  164. package/dist/pipeline-spec.d.ts +1 -1
  165. package/dist/pipeline-spec.d.ts.map +1 -1
  166. package/dist/plugin.d.ts.map +1 -1
  167. package/dist/points-lines/prepare.d.ts +3 -0
  168. package/dist/points-lines/prepare.d.ts.map +1 -1
  169. package/dist/points-lines/standard-owner.d.ts +33 -0
  170. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  171. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  172. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  173. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  174. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  175. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  176. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  177. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  178. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  179. package/dist/record/frame-lighting.d.ts +8 -6
  180. package/dist/record/frame-lighting.d.ts.map +1 -1
  181. package/dist/record/frame-snapshot.d.ts +55 -10
  182. package/dist/record/frame-snapshot.d.ts.map +1 -1
  183. package/dist/record/frame-targets.d.ts.map +1 -1
  184. package/dist/record/frame.d.ts +21 -5
  185. package/dist/record/frame.d.ts.map +1 -1
  186. package/dist/record/gpu-timing.d.ts +19 -7
  187. package/dist/record/gpu-timing.d.ts.map +1 -1
  188. package/dist/record/helpers.d.ts +0 -20
  189. package/dist/record/helpers.d.ts.map +1 -1
  190. package/dist/record/main-pass-geometry.d.ts +20 -2
  191. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  192. package/dist/record/main-pass-material.d.ts +10 -18
  193. package/dist/record/main-pass-material.d.ts.map +1 -1
  194. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  195. package/dist/record/main-pass.d.ts.map +1 -1
  196. package/dist/record/mesh-ssbo.d.ts +11 -3
  197. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  198. package/dist/record/recovery-pipeline.d.ts +30 -0
  199. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  200. package/dist/record/render-context.d.ts +82 -9
  201. package/dist/record/render-context.d.ts.map +1 -1
  202. package/dist/record/shadow-pass.d.ts.map +1 -1
  203. package/dist/record/typed-frame-graph.d.ts +49 -2
  204. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  205. package/dist/record/view-ubo.d.ts +1 -2
  206. package/dist/record/view-ubo.d.ts.map +1 -1
  207. package/dist/recovery/render-system-candidate.d.ts +65 -0
  208. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  209. package/dist/reflection/inspection.d.ts +10 -0
  210. package/dist/reflection/inspection.d.ts.map +1 -0
  211. package/dist/reflection/projection.d.ts +49 -0
  212. package/dist/reflection/projection.d.ts.map +1 -1
  213. package/dist/reflection/record-owner.d.ts +60 -0
  214. package/dist/reflection/record-owner.d.ts.map +1 -0
  215. package/dist/render-contract.d.ts +78 -15
  216. package/dist/render-contract.d.ts.map +1 -1
  217. package/dist/render-pipeline.d.ts +7 -0
  218. package/dist/render-pipeline.d.ts.map +1 -1
  219. package/dist/render-system-extract-tail.d.ts +12 -0
  220. package/dist/render-system-extract-tail.d.ts.map +1 -0
  221. package/dist/render-system-extract.d.ts +252 -30
  222. package/dist/render-system-extract.d.ts.map +1 -1
  223. package/dist/render-system.d.ts +140 -5
  224. package/dist/render-system.d.ts.map +1 -1
  225. package/dist/scene/probe-blend-record.d.ts +22 -0
  226. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  227. package/dist/scene/probe-blend.d.ts +142 -0
  228. package/dist/scene/probe-blend.d.ts.map +1 -0
  229. package/dist/scene/render-scene-types.d.ts +0 -6
  230. package/dist/scene/render-scene-types.d.ts.map +1 -1
  231. package/dist/scene/render-scene.d.ts +122 -9
  232. package/dist/scene/render-scene.d.ts.map +1 -1
  233. package/dist/scene/visibility/bounds.d.ts +20 -0
  234. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  235. package/dist/scene/visibility/budget.d.ts +16 -0
  236. package/dist/scene/visibility/budget.d.ts.map +1 -0
  237. package/dist/scene/visibility/facet.d.ts +73 -0
  238. package/dist/scene/visibility/facet.d.ts.map +1 -0
  239. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  240. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  241. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  242. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  243. package/dist/scene/visibility/inspection.d.ts +132 -0
  244. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  245. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  246. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  247. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  248. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  249. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  250. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  251. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  252. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  253. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  254. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  255. package/dist/scene/visibility/types.d.ts +34 -0
  256. package/dist/scene/visibility/types.d.ts.map +1 -0
  257. package/dist/ssr/admission.d.ts +122 -0
  258. package/dist/ssr/admission.d.ts.map +1 -0
  259. package/dist/ssr/composition.d.ts +32 -0
  260. package/dist/ssr/composition.d.ts.map +1 -0
  261. package/dist/ssr/errors.d.ts +39 -0
  262. package/dist/ssr/errors.d.ts.map +1 -0
  263. package/dist/ssr/identity.d.ts +13 -0
  264. package/dist/ssr/identity.d.ts.map +1 -0
  265. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  266. package/dist/temporal/frame-coordinator.d.ts +13 -0
  267. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  268. package/dist/temporal/frame.d.ts +2 -0
  269. package/dist/temporal/frame.d.ts.map +1 -1
  270. package/dist/temporal/index.mjs +6 -5
  271. package/dist/temporal/taa-history-store.d.ts +15 -1
  272. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  273. package/dist/temporal/transaction.d.ts +5 -0
  274. package/dist/temporal/transaction.d.ts.map +1 -1
  275. package/dist/temporal/view.d.ts +7 -0
  276. package/dist/temporal/view.d.ts.map +1 -1
  277. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  278. package/dist/typed-render-graph-primitives.d.ts +4 -0
  279. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  280. package/dist/volume/passes.d.ts +8 -4
  281. package/dist/volume/passes.d.ts.map +1 -1
  282. package/package.json +20 -20
  283. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
  284. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  285. package/src/__tests__/device-feature-admission.unit.test.ts +19 -1
  286. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  287. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  288. package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
  289. package/src/__tests__/environment-generation.integration.test.ts +39 -0
  290. package/src/__tests__/evidence-identity.ts +13 -0
  291. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  292. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  293. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  294. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  295. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  296. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  297. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  298. package/src/__tests__/factory-contract.integration.test.ts +154 -6
  299. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  300. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  301. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  302. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
  303. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
  304. package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
  305. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  306. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  307. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
  308. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  309. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  310. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  311. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  312. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  313. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  314. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  315. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  316. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  317. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  318. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  319. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  320. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  321. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  322. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  323. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  324. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  325. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  326. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  327. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  328. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  329. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  330. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  331. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  332. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  333. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  334. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  335. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  336. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  337. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  338. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  339. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  340. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  341. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  342. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  343. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  344. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  345. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  346. package/src/__tests__/material-contract-inventory.unit.test.ts +41 -9
  347. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  348. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  349. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  350. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  351. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  352. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  353. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  354. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  355. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  356. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  357. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  358. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  359. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  360. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  361. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  362. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  363. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  364. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  365. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  366. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  367. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  368. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  369. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  370. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  371. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  372. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  373. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  374. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  375. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  376. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  377. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  378. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  379. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  380. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  381. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  382. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  383. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  384. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  385. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  386. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  387. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  388. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  389. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  390. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  391. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +160 -17
  392. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  393. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  394. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  395. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  396. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  397. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  398. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  399. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  400. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  401. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  402. package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
  403. package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
  404. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  405. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  406. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  407. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  408. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  409. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
  410. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
  411. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  412. package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
  413. package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
  414. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  415. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  416. package/src/__tests__/transmission-prewarm.unit.test.ts +111 -32
  417. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  418. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  419. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  420. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  421. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  422. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  423. package/src/assembly/backend-contract.ts +13 -0
  424. package/src/assembly/bundler-contract.ts +9 -0
  425. package/src/assembly/device-feature-admission.ts +26 -9
  426. package/src/assembly/extended-lighting-fallback.ts +216 -0
  427. package/src/assembly/factory.ts +81 -9634
  428. package/src/assembly/host-contract.ts +10 -0
  429. package/src/assembly/material/assembly.ts +18 -2
  430. package/src/assembly/material/standard-layer-projection.ts +16 -0
  431. package/src/assembly/material-shader-policy.ts +854 -0
  432. package/src/assembly/material-shader-prewarm.ts +88 -0
  433. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  434. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  435. package/src/assembly/recovery/generation.ts +141 -0
  436. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  437. package/src/assembly/recovery/renderer-recover.ts +906 -0
  438. package/src/assembly/renderer-facade.ts +342 -0
  439. package/src/assembly/renderer-frame-transaction.ts +45 -0
  440. package/src/assembly/renderer-helpers.ts +99 -0
  441. package/src/assembly/renderer-lifecycle.ts +159 -0
  442. package/src/assembly/shader-prewarm-policy.ts +157 -0
  443. package/src/assembly/webgpu-ready-contract.ts +14 -0
  444. package/src/assembly/webgpu-ready.ts +4079 -0
  445. package/src/assembly/webgpu-renderer.ts +4095 -0
  446. package/src/cluster-binner.ts +31 -0
  447. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  448. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  449. package/src/components/camera.ts +1 -1
  450. package/src/components/index.ts +3 -0
  451. package/src/components/light-helpers.ts +52 -1
  452. package/src/components/light-probe.ts +13 -0
  453. package/src/components/rect-area-light.ts +15 -0
  454. package/src/components/scene-instance.ts +6 -0
  455. package/src/components/spot-light.ts +3 -0
  456. package/src/debug-draw-glue.ts +1 -1
  457. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  458. package/src/device/device-scope.ts +35 -6
  459. package/src/device/gpu-residency.ts +334 -35
  460. package/src/device/lifecycle-transaction.ts +12 -0
  461. package/src/environment/frame.ts +6 -0
  462. package/src/environment/inspection.ts +10 -0
  463. package/src/environment/lifecycle.ts +122 -9
  464. package/src/errors/gpu-driven.ts +92 -0
  465. package/src/errors/index.ts +1 -0
  466. package/src/errors/recover.ts +108 -5
  467. package/src/errors/render.ts +72 -1
  468. package/src/extract/camera.ts +3 -3
  469. package/src/extract/contracts.ts +974 -0
  470. package/src/extract/gpu-driven.ts +108 -0
  471. package/src/extract/visibility.ts +0 -23
  472. package/src/features/host.ts +16 -0
  473. package/src/features/prepared-gpu-work.ts +20 -2
  474. package/src/features/prepared-graphics-store.ts +17 -0
  475. package/src/gpu-driven/batch-topology.ts +206 -28
  476. package/src/gpu-driven/prepared-draw.ts +208 -0
  477. package/src/gpu-driven/production-raster.ts +1046 -39
  478. package/src/gpu-driven/view-gpu.ts +411 -29
  479. package/src/gpu-scene-schema.ts +13 -0
  480. package/src/gpu-scene.ts +137 -67
  481. package/src/gpu-usage.ts +0 -1
  482. package/src/hdrp-buffers.ts +6 -6
  483. package/src/ibl/IblPipelineCache.ts +134 -50
  484. package/src/ibl/skylight-bind-group.ts +26 -0
  485. package/src/index.ts +83 -1
  486. package/src/inspection-types.ts +220 -1
  487. package/src/internal.ts +38 -0
  488. package/src/light-buffer-layout.ts +157 -324
  489. package/src/materials.ts +278 -47
  490. package/src/mesh-material-bindings.ts +59 -0
  491. package/src/occlusion-proxy.ts +64 -0
  492. package/src/pbr-pipeline.ts +290 -60
  493. package/src/pipeline/standard-forward-lane.ts +47 -0
  494. package/src/pipeline/standard-lighting/graph.ts +2 -1
  495. package/src/pipeline/standard-lighting/light-frame.ts +3 -2
  496. package/src/pipeline/standard-lighting/topology.ts +1 -1
  497. package/src/pipeline/standard-lighting/transport.ts +2 -1
  498. package/src/pipeline/standard-pipeline.ts +2 -0
  499. package/src/pipeline-builder.ts +5 -2
  500. package/src/pipeline-spec.ts +12 -1
  501. package/src/plugin.ts +4 -0
  502. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  503. package/src/points-lines/prepare.ts +9 -0
  504. package/src/points-lines/standard-owner.ts +403 -0
  505. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  506. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  507. package/src/prepare/extended-lighting/resources.ts +389 -0
  508. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  509. package/src/prepare/extended-lighting/state.ts +92 -0
  510. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  511. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  512. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  513. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  514. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  515. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  516. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  517. package/src/record/__tests__/temporal-payload.unit.test.ts +29 -28
  518. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +21 -2
  519. package/src/record/frame-lighting.ts +252 -53
  520. package/src/record/frame-snapshot.ts +72 -12
  521. package/src/record/frame-targets.ts +11 -1
  522. package/src/record/frame.ts +416 -95
  523. package/src/record/gpu-timing.ts +108 -44
  524. package/src/record/helpers.ts +1 -59
  525. package/src/record/main-pass-geometry.ts +434 -44
  526. package/src/record/main-pass-material.ts +324 -210
  527. package/src/record/main-pass-sprite-draws.ts +58 -92
  528. package/src/record/main-pass.ts +94 -6
  529. package/src/record/mesh-ssbo.ts +79 -46
  530. package/src/record/recovery-pipeline.ts +695 -0
  531. package/src/record/render-context.ts +99 -7
  532. package/src/record/shadow-pass.ts +66 -40
  533. package/src/record/typed-frame-graph.ts +270 -84
  534. package/src/record/view-ubo.ts +17 -27
  535. package/src/recovery/render-system-candidate.ts +839 -0
  536. package/src/reflection/inspection.ts +67 -0
  537. package/src/reflection/projection.ts +196 -1
  538. package/src/reflection/record-owner.ts +1283 -0
  539. package/src/render-contract.ts +82 -12
  540. package/src/render-pipeline.ts +5 -0
  541. package/src/render-system-extract-tail.ts +2983 -0
  542. package/src/render-system-extract.ts +290 -2784
  543. package/src/render-system.ts +1597 -1192
  544. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  545. package/src/scene/probe-blend-record.ts +54 -0
  546. package/src/scene/probe-blend.ts +813 -0
  547. package/src/scene/render-scene-types.ts +0 -7
  548. package/src/scene/render-scene.ts +972 -184
  549. package/src/scene/visibility/bounds.ts +57 -0
  550. package/src/scene/visibility/budget.ts +40 -0
  551. package/src/scene/visibility/facet.ts +400 -0
  552. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  553. package/src/scene/visibility/gpu-lod.ts +119 -0
  554. package/src/scene/visibility/inspection.ts +313 -0
  555. package/src/scene/visibility/lod-selector.ts +84 -0
  556. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  557. package/src/scene/visibility/occlusion-pass.ts +165 -0
  558. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  559. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  560. package/src/scene/visibility/types.ts +50 -0
  561. package/src/ssr/admission.ts +451 -0
  562. package/src/ssr/composition.ts +73 -0
  563. package/src/ssr/errors.ts +76 -0
  564. package/src/ssr/identity.ts +12 -0
  565. package/src/systems/skin-palette-allocator.ts +1 -1
  566. package/src/temporal/frame-coordinator.ts +30 -1
  567. package/src/temporal/frame.ts +14 -1
  568. package/src/temporal/taa-history-store.ts +46 -2
  569. package/src/temporal/temporal-view.ts +1 -1
  570. package/src/temporal/transaction.ts +9 -0
  571. package/src/temporal/view.ts +11 -0
  572. package/src/tilemap-chunk-extract-system.ts +5 -5
  573. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  574. package/src/typed-render-graph-primitives.ts +48 -0
  575. package/src/volume/passes.ts +46 -49
  576. package/dist/.tsbuildinfo +0 -1
  577. package/dist/chunk-5YADUCA7.mjs.map +0 -1
  578. package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
  579. package/dist/chunk-BXBQQKFQ.mjs +0 -67
  580. package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
  581. package/dist/chunk-FS2KNUQH.mjs.map +0 -1
  582. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  583. package/dist/chunk-VFPLTKCU.mjs +0 -192
  584. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -0,0 +1,108 @@
1
+ import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
2
+ import type { MeshAsset } from '@forgeax/engine-types';
3
+ import {
4
+ type GpuDrivenDrawRange,
5
+ type PreparedGpuDrivenDraw,
6
+ type PrepareGpuDrivenDrawInput,
7
+ prepareGpuDrivenDraw,
8
+ } from '../gpu-driven/prepared-draw';
9
+ import type { MaterialSnapshot } from '../render-system-extract';
10
+
11
+ export {
12
+ GpuDrivenPreparationError,
13
+ type GpuDrivenPreparationErrorCode,
14
+ type GpuDrivenPreparationErrorDetail,
15
+ type GpuDrivenPreparationReason,
16
+ } from '../errors/gpu-driven';
17
+ export {
18
+ type GpuDrivenDrawRange,
19
+ type GpuDrivenGeometryReceipt,
20
+ type GpuDrivenSkinReceipt,
21
+ type PreparedGpuDrivenDraw,
22
+ type PrepareGpuDrivenDrawInput,
23
+ prepareGpuDrivenDraw,
24
+ } from '../gpu-driven/prepared-draw';
25
+
26
+ export interface GpuDrivenDrawSnapshot {
27
+ readonly kind: GpuDrivenDrawRange['kind'];
28
+ readonly first: GpuDrivenDrawRange['first'];
29
+ readonly count: GpuDrivenDrawRange['count'];
30
+ readonly baseVertex: GpuDrivenDrawRange['baseVertex'];
31
+ readonly materialSlot: number;
32
+ readonly topology: GpuDrivenDrawRange['topology'];
33
+ readonly pipelineClass: string;
34
+ readonly materialResourceClass: string;
35
+ /** Producer-owned prepared ABI facts used by the PBR GPU lane. */
36
+ readonly prepared?: PreparedGpuDrivenDraw;
37
+ }
38
+
39
+ /**
40
+ * Resolves prepared facts once at extract time; record never re-reads assets.
41
+ * The resolver surfaces UV, reflection, vertex, alpha, resource, and
42
+ * generation failures as GpuDrivenPreparationError before recording.
43
+ */
44
+ export function resolvePreparedGpuDrivenDraw(input: PrepareGpuDrivenDrawInput):
45
+ | { readonly ok: true; readonly value: PreparedGpuDrivenDraw }
46
+ | {
47
+ readonly ok: false;
48
+ readonly error: import('../errors/gpu-driven').GpuDrivenPreparationError;
49
+ } {
50
+ return prepareGpuDrivenDraw(input);
51
+ }
52
+
53
+ /** Keeps the artifact type visible at this owner boundary for contract checks. */
54
+ export type PreparedGpuDrivenArtifact = MaterialShaderArtifact;
55
+
56
+ /**
57
+ * Derives the resource identity consumed by the existing GPU-driven batch
58
+ * owner. The extract stage is the only place that resolves material values;
59
+ * this helper keeps the batch adapter from re-reading assets later.
60
+ */
61
+ export function gpuDrivenMaterialResourceClass(material: MaterialSnapshot): string {
62
+ const textures = [...(material.textureHandles?.entries() ?? [])]
63
+ .map(([name, handle]) => [name, Number(handle)] as const)
64
+ .sort(([left], [right]) => left.localeCompare(right));
65
+ const samplers = [...(material.samplerHandles?.entries() ?? [])]
66
+ .map(([name, handle]) => [name, Number(handle)] as const)
67
+ .sort(([left], [right]) => left.localeCompare(right));
68
+ return JSON.stringify({
69
+ textures,
70
+ samplers,
71
+ video: [...(material.videoTextureFields?.keys() ?? [])].sort(),
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Projects already-resolved mesh facts into the existing GPU-driven draw
77
+ * snapshot. No World or AssetRegistry access is permitted at this boundary.
78
+ */
79
+ export function buildGpuDrivenDraws(input: {
80
+ readonly submeshes: readonly MeshAsset['submeshes'][number][];
81
+ readonly indexed: boolean;
82
+ readonly materials: readonly MaterialSnapshot[];
83
+ readonly fallbackMaterial: MaterialSnapshot;
84
+ readonly prepare?: (
85
+ draw: GpuDrivenDrawSnapshot,
86
+ material: MaterialSnapshot,
87
+ ) => PreparedGpuDrivenDraw | undefined;
88
+ }): GpuDrivenDrawSnapshot[] {
89
+ let nonIndexedFirst = 0;
90
+ return input.submeshes.flatMap((submesh) => {
91
+ const drawMaterial = input.materials[submesh.materialSlot] ?? input.fallbackMaterial;
92
+ const first = input.indexed ? submesh.indexOffset : nonIndexedFirst;
93
+ nonIndexedFirst += submesh.vertexCount;
94
+ if (drawMaterial.transparent === true) return [];
95
+ const draw: GpuDrivenDrawSnapshot = {
96
+ kind: input.indexed ? ('indexed' as const) : ('non-indexed' as const),
97
+ first,
98
+ count: input.indexed ? submesh.indexCount : submesh.vertexCount,
99
+ baseVertex: 0,
100
+ materialSlot: submesh.materialSlot,
101
+ topology: submesh.topology,
102
+ pipelineClass: `${drawMaterial.materialShaderId ?? 'forgeax::default-unlit'}|${submesh.topology}|${JSON.stringify(drawMaterial.renderState ?? null)}`,
103
+ materialResourceClass: gpuDrivenMaterialResourceClass(drawMaterial),
104
+ };
105
+ const prepared = input.prepare?.(draw, drawMaterial);
106
+ return [prepared === undefined ? draw : { ...draw, prepared }];
107
+ });
108
+ }
@@ -1,5 +1,4 @@
1
1
  import type { EntityHandle, World } from '@forgeax/engine-ecs';
2
- import { createWorldProjection } from '@forgeax/engine-ecs/projection';
3
2
  import {
4
3
  projectHierarchy,
5
4
  type SceneHierarchyDiagnostic,
@@ -38,14 +37,6 @@ interface ResolutionState {
38
37
  readonly source: VisibilitySource;
39
38
  }
40
39
 
41
- interface VisibilityCacheEntry {
42
- readonly changes: ReturnType<typeof createWorldProjection>;
43
- readonly hierarchy: SceneHierarchySnapshot;
44
- readonly snapshot: VisibilitySnapshot;
45
- }
46
-
47
- const VISIBILITY_CACHE = new WeakMap<World, VisibilityCacheEntry>();
48
-
49
40
  /**
50
41
  * Resolve author intent against the scene-owned valid parent projection.
51
42
  * Diagnostics are preserved for callers to repair hierarchy input before retry.
@@ -54,19 +45,6 @@ export function resolveVisibility(
54
45
  world: World,
55
46
  hierarchy: SceneHierarchySnapshot = projectHierarchy(world),
56
47
  ): VisibilitySnapshot {
57
- const cached = VISIBILITY_CACHE.get(world);
58
- const changes = cached?.changes ?? createWorldProjection(world, { components: [Visibility] });
59
- if (cached !== undefined) {
60
- const evidence = changes.poll();
61
- if (
62
- evidence.status === 'delta' &&
63
- evidence.changes.length === 0 &&
64
- cached.hierarchy === hierarchy
65
- ) {
66
- return cached.snapshot;
67
- }
68
- }
69
-
70
48
  // Visibility is an opt-in component. Query only the owning archetypes
71
49
  // instead of walking every Entity row with an optional column; the latter
72
50
  // made a World with no visibility intents pay a full-scene scan on each
@@ -158,6 +136,5 @@ export function resolveVisibility(
158
136
  return resolveEntity?.(entity)?.effective ?? 'visible';
159
137
  },
160
138
  };
161
- VISIBILITY_CACHE.set(world, { changes, hierarchy, snapshot });
162
139
  return snapshot;
163
140
  }
@@ -1,6 +1,7 @@
1
1
  import type { EntityHandle } from '@forgeax/engine-ecs';
2
2
  import type { RhiCaps, TextureFormat } from '@forgeax/engine-rhi';
3
3
  import { err, ok, type Result } from '@forgeax/engine-types';
4
+ import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
4
5
  import {
5
6
  type RenderError,
6
7
  RenderFeatureCapabilityMissingError,
@@ -175,6 +176,7 @@ export interface RenderFeatureHost {
175
176
  batches: readonly RenderFeaturePreparedResourceBatch[],
176
177
  ): Result<void, RenderError>;
177
178
  recover(input: RenderFeatureRecoverInput): Result<void, RenderError>;
179
+ createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
178
180
  diagnostics(): readonly RenderFeatureDiagnostics[];
179
181
  /** Subscribe to lifecycle projection changes; unchanged active frames are silent. */
180
182
  subscribeDiagnostics(listener: () => void): () => void;
@@ -915,6 +917,20 @@ class FeatureHostImpl implements RenderFeatureHost {
915
917
  return this.slots.map((slot) => slot.feature);
916
918
  }
917
919
 
920
+ createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown> {
921
+ return {
922
+ kind: 'feature',
923
+ create: () => {
924
+ if (!scope.isAlive()) throw new Error('Feature candidate scope is not active.');
925
+ // The root carries the detached host owner itself. A scalar marker is
926
+ // not evidence that feature planning/preparation actually ran; the
927
+ // candidate graph and host own the prepared resources.
928
+ return this;
929
+ },
930
+ cleanup: () => undefined,
931
+ };
932
+ }
933
+
918
934
  private publishDiagnosticsChanged(): void {
919
935
  for (const listener of this.diagnosticsListeners) {
920
936
  try {
@@ -11,6 +11,7 @@ import type {
11
11
  ShaderModule,
12
12
  } from '@forgeax/engine-rhi';
13
13
  import { type BindGroupLayoutDescriptor, err, ok, type Result } from '@forgeax/engine-types';
14
+ import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
14
15
  import { type RenderError, RenderFeaturePreparationFailedError } from '../errors/render';
15
16
  import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
16
17
  import type { RenderFeatureShaderModuleMode } from './types';
@@ -910,6 +911,8 @@ function createRenderFeatureGpuWorkSession(input: {
910
911
  * retains the per-feature prepared GPU state and recreates it after recovery.
911
912
  */
912
913
  export interface RenderFeatureGpuWorkOwner {
914
+ /** Candidate root carries this detached GPU-work owner itself. */
915
+ createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
913
916
  beginFeature(
914
917
  featureIdentity: string,
915
918
  generation: number,
@@ -935,9 +938,21 @@ export function createRenderFeatureGpuWorkOwner(input: {
935
938
  session: RenderFeatureGpuPrepareSession;
936
939
  }
937
940
  >();
938
-
939
- return {
941
+ let disposed = false;
942
+
943
+ const owner: RenderFeatureGpuWorkOwner = {
944
+ createRecoveryRoot: (scope) => ({
945
+ kind: 'feature',
946
+ create: () => {
947
+ if (!scope.isAlive()) throw new Error('Feature GPU candidate scope is not active.');
948
+ return owner;
949
+ },
950
+ cleanup: () => undefined,
951
+ }),
940
952
  beginFeature: (featureIdentity, generation, requestedShaderModuleMode = 'validated') => {
953
+ if (disposed) {
954
+ throw new Error('Render feature GPU work owner is disposed.');
955
+ }
941
956
  const shaderModuleMode = requestedShaderModuleMode;
942
957
  const existing = sessions.get(featureIdentity);
943
958
  if (existing?.generation === generation && existing.shaderModuleMode === shaderModuleMode) {
@@ -962,6 +977,8 @@ export function createRenderFeatureGpuWorkOwner(input: {
962
977
  resolveBuffer: (featureIdentity, reference) =>
963
978
  sessions.get(featureIdentity)?.session.resolveBuffer(reference),
964
979
  dispose: () => {
980
+ if (disposed) return ok(undefined);
981
+ disposed = true;
965
982
  let first: RenderError | undefined;
966
983
  for (const { session } of sessions.values()) {
967
984
  const disposed = session.dispose();
@@ -971,4 +988,5 @@ export function createRenderFeatureGpuWorkOwner(input: {
971
988
  return first === undefined ? ok(undefined) : err(first);
972
989
  },
973
990
  };
991
+ return owner;
974
992
  }
@@ -1,5 +1,6 @@
1
1
  import type { TextureFormat } from '@forgeax/engine-rhi';
2
2
  import { err, ok, type Result } from '@forgeax/engine-types';
3
+ import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
3
4
  import { type RenderError, RenderFeaturePreparationFailedError } from '../errors/render';
4
5
  import type {
5
6
  PreparedKind,
@@ -81,6 +82,7 @@ export interface PreparedGraphicsStore {
81
82
  beginFrame(featureIdentity: string, generation: number): PreparedGraphicsTransaction;
82
83
  snapshot(featureIdentity: string): PreparedGraphicsStoreSnapshot;
83
84
  invalidate(featureIdentity: string, generation: number): void;
85
+ createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
84
86
  }
85
87
 
86
88
  interface Slot {
@@ -418,6 +420,21 @@ class PreparedGraphicsStoreImpl implements PreparedGraphicsStore {
418
420
  }
419
421
  }
420
422
 
423
+ createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown> {
424
+ return {
425
+ kind: 'buffer',
426
+ create: () => {
427
+ if (!scope.isAlive()) throw new Error('Prepared graphics candidate scope is not active.');
428
+ const itemCount = [...this.slots.values()].reduce(
429
+ (count, slot) => count + slot.items.size,
430
+ 0,
431
+ );
432
+ return Object.freeze({ generation: scope.generation, itemCount });
433
+ },
434
+ cleanup: () => undefined,
435
+ };
436
+ }
437
+
421
438
  registerReference(reference: object, item: PreparedGraphicsItem): void {
422
439
  this.references.set(reference, item);
423
440
  }
@@ -1,6 +1,10 @@
1
1
  import type { PrimitiveTopology } from '@forgeax/engine-types';
2
2
  import type { BatchTopologyInspection } from '../inspection-types';
3
+ import type { CameraSnapshot } from '../render-contract';
3
4
  import type { RenderSceneApplyResult, RenderSceneSlot } from '../scene/render-scene-types';
5
+ import { type GpuLodRow, selectGpuLod } from '../scene/visibility/gpu-lod';
6
+ import { projectedHeight as measureProjectedHeight } from '../scene/visibility/lod-selector';
7
+ import type { PreparedGpuDrivenDraw } from './prepared-draw';
4
8
 
5
9
  export type { BatchTopologyInspection } from '../inspection-types';
6
10
 
@@ -14,6 +18,12 @@ export interface GpuDrivenBatchKey {
14
18
  readonly topology: PrimitiveTopology;
15
19
  readonly pipelineClass: string;
16
20
  readonly materialResourceClass: string;
21
+ /** Stable producer-owned identity; topology never derives this from a shader name. */
22
+ readonly preparedIdentity?: string;
23
+ /** Resource identity projected by the prepared material contract. */
24
+ readonly resourceIdentity?: string;
25
+ /** Closed admission lane for the prepared Standard PBR draw. */
26
+ readonly admission?: 'opaque' | 'alpha-mask';
17
27
  }
18
28
 
19
29
  export interface GpuDrivenCandidate {
@@ -21,6 +31,16 @@ export interface GpuDrivenCandidate {
21
31
  readonly generation: number;
22
32
  readonly drawItemIndex: number;
23
33
  readonly instanceOrdinal: number;
34
+ /** Absolute coverage rows uploaded to the GPU selector; root is implicit. */
35
+ readonly lodCoverages?: readonly number[];
36
+ readonly lodHysteresis?: number;
37
+ readonly projectedHeight?: number;
38
+ readonly lodRanges?: readonly {
39
+ readonly first: number;
40
+ readonly count: number;
41
+ readonly baseVertex: number;
42
+ }[];
43
+ readonly prepared?: PreparedGpuDrivenDraw;
24
44
  }
25
45
 
26
46
  export interface GpuDrivenBatch {
@@ -40,6 +60,32 @@ export interface SubmissionPlan {
40
60
  readonly visibleCapacity: number;
41
61
  }
42
62
 
63
+ export interface GpuLodCandidateInput {
64
+ readonly candidate: GpuDrivenCandidate;
65
+ readonly rows: readonly GpuLodRow[];
66
+ readonly projectedHeight: number;
67
+ readonly previousLevel: number;
68
+ readonly historyValid: boolean;
69
+ }
70
+
71
+ export interface GpuLodCompactCandidate {
72
+ readonly candidate: GpuDrivenCandidate;
73
+ readonly level: number;
74
+ readonly confidence: number;
75
+ }
76
+
77
+ /** Selects one level per stable topology candidate without mutating membership. */
78
+ export function compactGpuLodCandidates(
79
+ inputs: readonly GpuLodCandidateInput[],
80
+ ): readonly GpuLodCompactCandidate[] {
81
+ return Object.freeze(
82
+ inputs.map((input) => {
83
+ const selection = selectGpuLod(input.rows, input);
84
+ return Object.freeze({ candidate: input.candidate, ...selection });
85
+ }),
86
+ );
87
+ }
88
+
43
89
  interface MutableBatch {
44
90
  readonly batchId: number;
45
91
  readonly generation: number;
@@ -56,27 +102,97 @@ function eligibleKeys(slot: RenderSceneSlot): readonly GpuDrivenBatchKey[] {
56
102
  snapshot.instances?.instanceCount === 0 ||
57
103
  snapshot.localAabb === undefined ||
58
104
  snapshot.skin !== undefined ||
105
+ snapshot.morph !== undefined ||
59
106
  snapshot.spriteInstances !== undefined
60
107
  ) {
61
108
  return [];
62
109
  }
63
- return draws.map((draw) => ({
64
- assetHandle: snapshot.assetHandle,
65
- drawKind: draw.kind,
66
- first: draw.first,
67
- count: draw.count,
68
- baseVertex: draw.baseVertex,
69
- materialSlot: draw.materialSlot,
70
- topology: draw.topology,
71
- pipelineClass: draw.pipelineClass,
72
- materialResourceClass: draw.materialResourceClass,
73
- }));
110
+ return draws.flatMap((draw) => {
111
+ const prepared = draw.prepared;
112
+ const material = snapshot.materials[draw.materialSlot] ?? snapshot.material;
113
+ if (prepared !== undefined) {
114
+ const isStandardPbr =
115
+ (material.materialShaderId === 'forgeax::default-standard-pbr' &&
116
+ prepared.identity.material === 'forgeax::default-standard-pbr') ||
117
+ (material.materialShaderId === 'forgeax::default-standard-pbr-skin' &&
118
+ prepared.identity.material === 'forgeax::default-standard-pbr-skin');
119
+ const isAlphaBlend =
120
+ material.transparent === true || material.renderState?.blend !== undefined;
121
+ if (!isStandardPbr || isAlphaBlend || prepared.identity.deformation !== 'rigid') return [];
122
+ const preparedIdentity = `${prepared.identity.material}|${prepared.identity.geometry}|${prepared.identity.deformation}`;
123
+ const admission =
124
+ typeof material.paramSnapshot?.alphaCutoff === 'number' ? 'alpha-mask' : 'opaque';
125
+ return [
126
+ {
127
+ assetHandle: snapshot.assetHandle,
128
+ drawKind: draw.kind,
129
+ first: prepared.first,
130
+ count: prepared.count,
131
+ baseVertex: prepared.baseVertex,
132
+ materialSlot: draw.materialSlot,
133
+ topology: prepared.topology,
134
+ pipelineClass: preparedIdentity,
135
+ materialResourceClass: draw.materialResourceClass,
136
+ preparedIdentity,
137
+ resourceIdentity: draw.materialResourceClass,
138
+ admission,
139
+ },
140
+ ];
141
+ }
142
+ return [
143
+ {
144
+ assetHandle: snapshot.assetHandle,
145
+ drawKind: draw.kind,
146
+ first: draw.first,
147
+ count: draw.count,
148
+ baseVertex: draw.baseVertex,
149
+ materialSlot: draw.materialSlot,
150
+ topology: draw.topology,
151
+ pipelineClass: draw.pipelineClass,
152
+ materialResourceClass: draw.materialResourceClass,
153
+ },
154
+ ];
155
+ });
74
156
  }
75
157
 
76
158
  function keyText(key: GpuDrivenBatchKey): string {
77
159
  return JSON.stringify(key);
78
160
  }
79
161
 
162
+ /**
163
+ * A GPU indirect draw has one index/vertex range for the whole batch. LOD
164
+ * selection is per candidate, so sharing a batch between LOD candidates would
165
+ * let `finalizeView` pick the first candidate's range for every visible
166
+ * instance. Keep LOD candidates in single-candidate batches; ordinary meshes
167
+ * retain the existing compatibility batching path.
168
+ */
169
+ function candidateBatchText(
170
+ key: GpuDrivenBatchKey,
171
+ primitiveIndex: number,
172
+ drawItemIndex: number,
173
+ instanceOrdinal: number,
174
+ hasLod: boolean,
175
+ ): string {
176
+ return hasLod
177
+ ? `${keyText(key)}|lod:${primitiveIndex}:${drawItemIndex}:${instanceOrdinal}`
178
+ : keyText(key);
179
+ }
180
+
181
+ function membershipTexts(slot: RenderSceneSlot): readonly string[] {
182
+ const keys = eligibleKeys(slot);
183
+ const instanceCount = slot.snapshot.instances?.instanceCount ?? 1;
184
+ const hasLod = (slot.snapshot.lods?.length ?? 0) > 0;
185
+ const texts: string[] = [];
186
+ for (let drawItemIndex = 0; drawItemIndex < keys.length; drawItemIndex += 1) {
187
+ const key = keys[drawItemIndex];
188
+ if (key === undefined) continue;
189
+ for (let instanceOrdinal = 0; instanceOrdinal < instanceCount; instanceOrdinal += 1) {
190
+ texts.push(candidateBatchText(key, slot.slot, drawItemIndex, instanceOrdinal, hasLod));
191
+ }
192
+ }
193
+ return texts;
194
+ }
195
+
80
196
  function alignInstanceBase(value: number): number {
81
197
  // Raster binds each batch's visible-index and compact-transform segment at
82
198
  // a static storage-buffer offset. WebGPU requires those offsets to satisfy
@@ -86,6 +202,42 @@ function alignInstanceBase(value: number): number {
86
202
  return Math.ceil(value / 64) * 64;
87
203
  }
88
204
 
205
+ export function projectedHeightForCandidate(slot: RenderSceneSlot, camera: CameraSnapshot): number {
206
+ const aabb = slot.snapshot.localAabb;
207
+ if (aabb === undefined || aabb.length < 6) return Number.NaN;
208
+ const halfX = Math.abs((aabb[3] ?? 0) - (aabb[0] ?? 0)) * 0.5;
209
+ const halfY = Math.abs((aabb[4] ?? 0) - (aabb[1] ?? 0)) * 0.5;
210
+ const halfZ = Math.abs((aabb[5] ?? 0) - (aabb[2] ?? 0)) * 0.5;
211
+ const world = slot.snapshot.transform.world;
212
+ // Transform the local AABB's half extents through the absolute value of the
213
+ // world 3x3. This is the conservative world-space sphere bound for every
214
+ // rotation and signed/non-uniform scale, and keeps CPU projection aligned
215
+ // with the world transform uploaded to the GPU selector.
216
+ const worldHalfX =
217
+ Math.abs(world[0] ?? 0) * halfX +
218
+ Math.abs(world[4] ?? 0) * halfY +
219
+ Math.abs(world[8] ?? 0) * halfZ;
220
+ const worldHalfY =
221
+ Math.abs(world[1] ?? 0) * halfX +
222
+ Math.abs(world[5] ?? 0) * halfY +
223
+ Math.abs(world[9] ?? 0) * halfZ;
224
+ const worldHalfZ =
225
+ Math.abs(world[2] ?? 0) * halfX +
226
+ Math.abs(world[6] ?? 0) * halfY +
227
+ Math.abs(world[10] ?? 0) * halfZ;
228
+ const radius = Math.hypot(worldHalfX, worldHalfY, worldHalfZ);
229
+ const dx = (world[12] ?? 0) - (camera.position[0] ?? 0);
230
+ const dy = (world[13] ?? 0) - (camera.position[1] ?? 0);
231
+ const dz = (world[14] ?? 0) - (camera.position[2] ?? 0);
232
+ return measureProjectedHeight({
233
+ radius,
234
+ depth: Math.hypot(dx, dy, dz),
235
+ projection: camera.projection,
236
+ fov: camera.fov,
237
+ orthoHeight: Math.abs(camera.orthoTop - camera.orthoBottom),
238
+ });
239
+ }
240
+
89
241
  /** Stable compatibility grouping; per-view visibility never mutates this owner. */
90
242
  export class BatchTopology {
91
243
  private readonly batches = new Map<string, MutableBatch>();
@@ -120,15 +272,17 @@ export class BatchTopology {
120
272
  changed = this.remove(slot.slot) || changed;
121
273
  changed = this.add(slot) || changed;
122
274
  }
275
+ // Most updated slots are transform-only. A full renderable update can
276
+ // still carry a changed material/geometry/draw topology, so compare the
277
+ // stable batch memberships and patch only when that key actually changed.
278
+ // This keeps moving entities on the zero-work transform lane while making
279
+ // same-entity resource edits visible to the GPU-driven owner.
123
280
  for (const slot of delta.updatedSlots) {
124
- const instanceCount = slot.snapshot.instances?.instanceCount ?? 1;
125
- const nextTexts = eligibleKeys(slot).flatMap((key) =>
126
- Array.from({ length: instanceCount }, () => keyText(key)),
127
- );
128
- const current = this.membershipByPrimitive.get(slot.slot) ?? [];
281
+ const previous = this.membershipByPrimitive.get(slot.slot) ?? [];
282
+ const next = membershipTexts(slot);
129
283
  if (
130
- current.length === nextTexts.length &&
131
- current.every((membership, index) => membership.batchText === nextTexts[index])
284
+ previous.length === next.length &&
285
+ previous.every((membership, index) => membership.batchText === next[index])
132
286
  ) {
133
287
  continue;
134
288
  }
@@ -199,26 +353,50 @@ export class BatchTopology {
199
353
  this.ineligiblePrimitives.delete(slot.slot);
200
354
  const memberships: Array<{ batchText: string; candidateKey: string }> = [];
201
355
  const instanceCount = slot.snapshot.instances?.instanceCount ?? 1;
356
+ const hasLod = (slot.snapshot.lods?.length ?? 0) > 0;
202
357
  for (let drawItemIndex = 0; drawItemIndex < keys.length; drawItemIndex += 1) {
203
358
  const key = keys[drawItemIndex];
204
359
  if (key === undefined) continue;
205
- const text = keyText(key);
206
- let batch = this.batches.get(text);
207
- if (batch === undefined) {
208
- const reused = this.freeBatchIds.pop();
209
- const batchId = reused ?? this.generationByBatchId.length;
210
- const generation = reused === undefined ? 0 : (this.generationByBatchId[batchId] ?? -1) + 1;
211
- this.generationByBatchId[batchId] = generation;
212
- batch = { batchId, generation, key, candidates: new Map() };
213
- this.batches.set(text, batch);
214
- }
215
360
  for (let instanceOrdinal = 0; instanceOrdinal < instanceCount; instanceOrdinal += 1) {
361
+ const text = candidateBatchText(key, slot.slot, drawItemIndex, instanceOrdinal, hasLod);
362
+ let batch = this.batches.get(text);
363
+ if (batch === undefined) {
364
+ const reused = this.freeBatchIds.pop();
365
+ const batchId = reused ?? this.generationByBatchId.length;
366
+ const generation =
367
+ reused === undefined ? 0 : (this.generationByBatchId[batchId] ?? -1) + 1;
368
+ this.generationByBatchId[batchId] = generation;
369
+ batch = { batchId, generation, key, candidates: new Map() };
370
+ this.batches.set(text, batch);
371
+ }
216
372
  const candidateKey = `${slot.slot}:${drawItemIndex}:${instanceOrdinal}`;
373
+ const draw = slot.snapshot.gpuDrivenDraws?.[drawItemIndex];
374
+ const prepared = draw?.prepared;
217
375
  batch.candidates.set(candidateKey, {
218
376
  primitiveIndex: slot.slot,
219
377
  generation: slot.generation,
220
378
  drawItemIndex,
221
379
  instanceOrdinal,
380
+ ...(draw?.projectedHeight === undefined ? {} : { projectedHeight: draw.projectedHeight }),
381
+ ...(slot.snapshot.lods === undefined
382
+ ? {}
383
+ : {
384
+ lodCoverages: [1, ...slot.snapshot.lods.map((lod) => lod.screenCoverage)],
385
+ ...(slot.snapshot.gpuDrivenDraws?.[drawItemIndex]?.lodRanges === undefined
386
+ ? {}
387
+ : {
388
+ lodRanges: slot.snapshot.gpuDrivenDraws[drawItemIndex]
389
+ ?.lodRanges as readonly {
390
+ readonly first: number;
391
+ readonly count: number;
392
+ readonly baseVertex: number;
393
+ }[],
394
+ }),
395
+ ...(slot.snapshot.lodHysteresis === undefined
396
+ ? {}
397
+ : { lodHysteresis: slot.snapshot.lodHysteresis }),
398
+ }),
399
+ ...(prepared === undefined ? {} : { prepared }),
222
400
  });
223
401
  memberships.push({ batchText: text, candidateKey });
224
402
  }