@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,119 @@
1
+ import { GPU_SCENE_LAYOUTS, GPU_SCENE_SCHEMAS } from '../../gpu-scene-schema';
2
+ import { type LodSelection, selectLod } from './lod-selector';
3
+
4
+ export const GPU_LOD_ROW_SCHEMA = GPU_SCENE_SCHEMAS.lod;
5
+ export const GPU_LOD_ROW_LAYOUT = GPU_SCENE_LAYOUTS.lod;
6
+
7
+ export interface LodDrawRange {
8
+ readonly firstIndex: number;
9
+ readonly indexCount: number;
10
+ readonly baseVertex: number;
11
+ }
12
+
13
+ export interface GpuLodRowsInput {
14
+ readonly generation: number;
15
+ readonly hysteresis: number;
16
+ readonly ranges: readonly LodDrawRange[];
17
+ /** Absolute projected-height thresholds, including root at index zero. */
18
+ readonly coverages: readonly number[];
19
+ readonly ready: readonly boolean[];
20
+ }
21
+
22
+ export interface GpuLodRow extends LodDrawRange {
23
+ readonly generation: number;
24
+ readonly level: number;
25
+ readonly screenCoverage: number;
26
+ readonly hysteresis: number;
27
+ readonly ready: boolean;
28
+ }
29
+
30
+ function finiteInteger(value: number, label: string): number {
31
+ if (!Number.isSafeInteger(value) || value < 0) {
32
+ throw new RangeError(`${label} must be a non-negative safe integer`);
33
+ }
34
+ return value;
35
+ }
36
+
37
+ function finiteFraction(value: number, label: string): number {
38
+ if (!Number.isFinite(value) || value < 0 || value > 1) {
39
+ throw new RangeError(`${label} must be in [0, 1]`);
40
+ }
41
+ return value;
42
+ }
43
+
44
+ /** Projects one stable row per draw range; it never contains a per-view choice. */
45
+ export function buildGpuLodRows(input: GpuLodRowsInput): readonly GpuLodRow[] {
46
+ if (!Number.isSafeInteger(input.generation) || input.generation < 0) {
47
+ throw new RangeError('generation must be a non-negative safe integer');
48
+ }
49
+ if (input.ranges.length === 0 || input.ranges.length !== input.coverages.length) {
50
+ throw new RangeError('ranges and coverages must have the same non-zero length');
51
+ }
52
+ if (input.ready.length !== input.ranges.length) {
53
+ throw new RangeError('ready must have one value for every LOD range');
54
+ }
55
+ if (!Number.isFinite(input.hysteresis) || input.hysteresis < 0 || input.hysteresis >= 1) {
56
+ throw new RangeError('hysteresis must be in [0, 1)');
57
+ }
58
+ let previousCoverage = 1;
59
+ return Object.freeze(
60
+ input.ranges.map((range, level) => {
61
+ const coverage = finiteFraction(input.coverages[level] ?? Number.NaN, `coverage[${level}]`);
62
+ if (level === 0 && coverage !== 1) throw new RangeError('root coverage must be 1');
63
+ if (level > 0 && coverage >= previousCoverage) {
64
+ throw new RangeError('LOD coverages must be strictly decreasing');
65
+ }
66
+ previousCoverage = coverage;
67
+ return Object.freeze({
68
+ generation: input.generation,
69
+ level,
70
+ firstIndex: finiteInteger(range.firstIndex, `ranges[${level}].firstIndex`),
71
+ indexCount: finiteInteger(range.indexCount, `ranges[${level}].indexCount`),
72
+ baseVertex: Number.isSafeInteger(range.baseVertex) ? range.baseVertex : 0,
73
+ screenCoverage: coverage,
74
+ hysteresis: input.hysteresis,
75
+ ready: input.ready[level] === true,
76
+ });
77
+ }),
78
+ );
79
+ }
80
+
81
+ export function encodeGpuLodRows(rows: readonly GpuLodRow[]): Uint8Array {
82
+ const bytes = new Uint8Array(GPU_LOD_ROW_LAYOUT.stride * rows.length);
83
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
84
+ for (const row of rows) {
85
+ const offset = row.level * GPU_LOD_ROW_LAYOUT.stride;
86
+ view.setUint32(offset, row.generation, true);
87
+ view.setUint32(offset + 4, row.level, true);
88
+ view.setUint32(offset + 8, row.firstIndex, true);
89
+ view.setUint32(offset + 12, row.indexCount, true);
90
+ view.setInt32(offset + 16, row.baseVertex, true);
91
+ view.setFloat32(offset + 20, row.screenCoverage, true);
92
+ view.setFloat32(offset + 24, row.hysteresis, true);
93
+ view.setUint32(offset + 28, row.ready ? 1 : 0, true);
94
+ }
95
+ return bytes;
96
+ }
97
+
98
+ export interface GpuLodSelectionInput {
99
+ readonly projectedHeight: number;
100
+ readonly previousLevel: number;
101
+ readonly historyValid: boolean;
102
+ }
103
+
104
+ /** CPU reference used by both the GPU selector contract and unsupported fallback. */
105
+ export function selectGpuLod(
106
+ rows: readonly GpuLodRow[],
107
+ input: GpuLodSelectionInput,
108
+ ): LodSelection {
109
+ const first = rows[0];
110
+ if (first === undefined) return { level: 0, confidence: 1 };
111
+ return selectLod({
112
+ levels: rows.slice(1),
113
+ projectedHeight: input.projectedHeight,
114
+ previousLevel: input.previousLevel,
115
+ hysteresis: first.hysteresis,
116
+ ready: rows.map((row) => row.ready),
117
+ historyValid: input.historyValid,
118
+ });
119
+ }
@@ -0,0 +1,313 @@
1
+ import { createVisibilityBudget } from './budget';
2
+
3
+ /** Detached, bounded LOD/occlusion facts for renderer inspection consumers. */
4
+
5
+ export const LOD_OCCLUSION_INSPECTION_SCHEMA = 'forgeax::lod-occlusion-inspection::v2' as const;
6
+ export const LOD_OCCLUSION_INSPECTION_MAX_BYTES = 16 * 1024;
7
+ const MAX_INSPECTION_SAMPLES = 64;
8
+
9
+ export type LodOcclusionInspectionError = {
10
+ readonly code: string;
11
+ readonly expected: string;
12
+ readonly hint: string;
13
+ readonly detail?: Readonly<Record<string, string | number | boolean>>;
14
+ };
15
+
16
+ export type LodOcclusionFallback =
17
+ | { readonly active: false }
18
+ | {
19
+ readonly active: true;
20
+ readonly reason: 'producer-failed' | 'page-exhausted' | 'query-failed' | 'device-loss';
21
+ readonly error: LodOcclusionInspectionError;
22
+ };
23
+
24
+ export type LodOcclusionDegradation =
25
+ | { readonly active: false }
26
+ | { readonly active: true; readonly reason: 'all-visible' | 'query-unavailable' };
27
+
28
+ export interface LodOcclusionInspectionSample {
29
+ readonly primitiveSlot: number;
30
+ readonly level: number;
31
+ readonly visible: boolean;
32
+ }
33
+
34
+ export interface LodOcclusionInspectionRow {
35
+ readonly root: { readonly guid: string; readonly sourceKey: string };
36
+ readonly view: {
37
+ readonly attachmentId: string;
38
+ readonly cameraEntity: number;
39
+ readonly viewRole: 'main' | 'shadow' | 'reflection' | 'probe';
40
+ readonly viewGeneration: number;
41
+ };
42
+ readonly slot: { readonly primitiveSlot: number; readonly slotGeneration: number };
43
+ readonly generation: number;
44
+ readonly count: {
45
+ readonly candidates: number;
46
+ readonly visible: number;
47
+ readonly occluded: number;
48
+ };
49
+ readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
50
+ /** Map latency for the most recently completed query page (bounded POD). */
51
+ readonly queryLatencyUs: {
52
+ readonly median: number;
53
+ readonly p95: number;
54
+ readonly last: number;
55
+ };
56
+ readonly pagePressure: { readonly used: number; readonly capacity: number };
57
+ readonly fallback: LodOcclusionFallback;
58
+ readonly degradation: LodOcclusionDegradation;
59
+ readonly samples: readonly LodOcclusionInspectionSample[];
60
+ }
61
+
62
+ export interface LodOcclusionInspectionSubmit {
63
+ readonly frameId: number;
64
+ readonly build: string;
65
+ readonly deviceGeneration: number;
66
+ }
67
+
68
+ /**
69
+ * Per-World attribution is only authoritative when the renderer publishes
70
+ * the GPU facts from the same submit that owns the parent inspection. The
71
+ * normal projection path remains useful diagnostics, but it must not be used
72
+ * as evidence for a World-reorder or per-World LOD claim.
73
+ */
74
+ export type LodOcclusionWorldAttribution =
75
+ | { readonly status: 'unavailable'; readonly reason: 'projection-only' }
76
+ | { readonly status: 'same-submit'; readonly submit: LodOcclusionInspectionSubmit };
77
+
78
+ export interface LodOcclusionWorldInspection {
79
+ readonly attachmentId: string;
80
+ readonly rows: readonly LodOcclusionInspectionRow[];
81
+ readonly attribution: LodOcclusionWorldAttribution;
82
+ }
83
+
84
+ export interface LodOcclusionInspectionBudget {
85
+ readonly configuredQueryBudget: number;
86
+ readonly effectiveQueryBudget: number;
87
+ readonly settleSubmits: number;
88
+ readonly retestSubmits: number;
89
+ readonly expirySubmits: number;
90
+ }
91
+
92
+ export interface LodOcclusionInspection extends LodOcclusionInspectionRow {
93
+ readonly schema: typeof LOD_OCCLUSION_INSPECTION_SCHEMA;
94
+ /** Atomic identity for the submit that produced every row below. */
95
+ readonly submit: LodOcclusionInspectionSubmit;
96
+ /** Immutable budget values used by the query and confidence owners. */
97
+ readonly budget: LodOcclusionInspectionBudget;
98
+ /** Stable attachment identity; array order is never an attribution key. */
99
+ readonly worlds: readonly LodOcclusionWorldInspection[];
100
+ }
101
+
102
+ export type LodOcclusionInspectionInput = Omit<
103
+ LodOcclusionInspection,
104
+ 'schema' | 'submit' | 'budget' | 'worlds'
105
+ > & {
106
+ readonly submit?: LodOcclusionInspectionSubmit;
107
+ readonly budget?: LodOcclusionInspectionBudget;
108
+ readonly worlds?: readonly LodOcclusionWorldInspectionInput[];
109
+ };
110
+
111
+ export type LodOcclusionWorldInspectionInput = Omit<LodOcclusionWorldInspection, 'attribution'> & {
112
+ readonly attribution?: LodOcclusionWorldAttribution;
113
+ };
114
+
115
+ export type LodOcclusionInspectionAction =
116
+ | { readonly action: 'none' }
117
+ | { readonly action: 'rebuild'; readonly sourceKey: string; readonly reason: string }
118
+ | { readonly action: 'cold-cook'; readonly sourceKey: string; readonly reason: string }
119
+ | { readonly action: 'retry'; readonly sourceKey: string; readonly reason: string };
120
+
121
+ function nonNegativeInteger(name: string, value: number): number {
122
+ if (!Number.isInteger(value) || value < 0)
123
+ throw new Error(`${name} must be a non-negative integer`);
124
+ return value;
125
+ }
126
+
127
+ function copyError(error: LodOcclusionInspectionError): LodOcclusionInspectionError {
128
+ return Object.freeze({
129
+ code: error.code,
130
+ expected: error.expected,
131
+ hint: error.hint,
132
+ ...(error.detail === undefined ? {} : { detail: Object.freeze({ ...error.detail }) }),
133
+ });
134
+ }
135
+
136
+ function copyFallback(fallback: LodOcclusionFallback): LodOcclusionFallback {
137
+ return fallback.active
138
+ ? Object.freeze({ ...fallback, error: copyError(fallback.error) })
139
+ : Object.freeze({ active: false });
140
+ }
141
+
142
+ function copySamples(
143
+ samples: readonly LodOcclusionInspectionSample[],
144
+ ): readonly LodOcclusionInspectionSample[] {
145
+ return Object.freeze(
146
+ [...samples]
147
+ .sort((left, right) => left.primitiveSlot - right.primitiveSlot)
148
+ .slice(0, MAX_INSPECTION_SAMPLES)
149
+ .map((sample) =>
150
+ Object.freeze({
151
+ primitiveSlot: nonNegativeInteger('sample.primitiveSlot', sample.primitiveSlot),
152
+ level: nonNegativeInteger('sample.level', sample.level),
153
+ visible: sample.visible,
154
+ }),
155
+ ),
156
+ );
157
+ }
158
+
159
+ function copyWorldAttribution(
160
+ attribution: LodOcclusionWorldAttribution | undefined,
161
+ ): LodOcclusionWorldAttribution {
162
+ if (attribution === undefined) {
163
+ return Object.freeze({ status: 'unavailable', reason: 'projection-only' });
164
+ }
165
+ if (attribution.status === 'same-submit') {
166
+ if (typeof attribution.submit.build !== 'string' || attribution.submit.build.length === 0) {
167
+ throw new Error('world.attribution.submit.build must be a non-empty string');
168
+ }
169
+ nonNegativeInteger('world.attribution.submit.frameId', attribution.submit.frameId);
170
+ nonNegativeInteger(
171
+ 'world.attribution.submit.deviceGeneration',
172
+ attribution.submit.deviceGeneration,
173
+ );
174
+ return Object.freeze({
175
+ status: 'same-submit',
176
+ submit: Object.freeze({ ...attribution.submit }),
177
+ });
178
+ }
179
+ if (attribution.status !== 'unavailable' || attribution.reason !== 'projection-only') {
180
+ throw new Error('world.attribution must declare same-submit or projection-only');
181
+ }
182
+ return Object.freeze({ status: 'unavailable', reason: 'projection-only' });
183
+ }
184
+
185
+ function copyRow(row: LodOcclusionInspectionRow): LodOcclusionInspectionRow {
186
+ for (const [name, value] of Object.entries({
187
+ generation: row.generation,
188
+ candidates: row.count.candidates,
189
+ visible: row.count.visible,
190
+ occluded: row.count.occluded,
191
+ median: row.queryLatencyUs.median,
192
+ p95: row.queryLatencyUs.p95,
193
+ last: row.queryLatencyUs.last,
194
+ pageUsed: row.pagePressure.used,
195
+ pageCapacity: row.pagePressure.capacity,
196
+ }))
197
+ nonNegativeInteger(`inspection.${name}`, value);
198
+ if (
199
+ row.view.cameraEntity < 0 ||
200
+ row.view.viewGeneration < 0 ||
201
+ row.slot.primitiveSlot < 0 ||
202
+ row.slot.slotGeneration < 0
203
+ ) {
204
+ throw new Error('inspection identity values must be non-negative');
205
+ }
206
+ if (row.pagePressure.used > row.pagePressure.capacity) {
207
+ throw new Error('inspection page pressure exceeds capacity');
208
+ }
209
+ return Object.freeze({
210
+ root: Object.freeze({ ...row.root }),
211
+ view: Object.freeze({ ...row.view }),
212
+ slot: Object.freeze({ ...row.slot }),
213
+ generation: row.generation,
214
+ count: Object.freeze({ ...row.count }),
215
+ lodHistogram: Object.freeze(
216
+ row.lodHistogram.map((histogram) =>
217
+ Object.freeze({
218
+ level: nonNegativeInteger('histogram.level', histogram.level),
219
+ count: nonNegativeInteger('histogram.count', histogram.count),
220
+ }),
221
+ ),
222
+ ),
223
+ queryLatencyUs: Object.freeze({ ...row.queryLatencyUs }),
224
+ pagePressure: Object.freeze({ ...row.pagePressure }),
225
+ fallback: copyFallback(row.fallback),
226
+ degradation: Object.freeze({ ...row.degradation }),
227
+ samples: copySamples(row.samples),
228
+ });
229
+ }
230
+
231
+ function copyWorlds(
232
+ worlds: readonly LodOcclusionWorldInspectionInput[] | undefined,
233
+ fallback: LodOcclusionInspectionRow,
234
+ ): readonly LodOcclusionWorldInspection[] {
235
+ const source = worlds ?? [{ attachmentId: fallback.view.attachmentId, rows: [fallback] }];
236
+ return Object.freeze(
237
+ source.map((world) =>
238
+ Object.freeze({
239
+ attachmentId: world.attachmentId,
240
+ rows: Object.freeze(world.rows.map((row) => copyRow(row))),
241
+ attribution: copyWorldAttribution(world.attribution),
242
+ }),
243
+ ),
244
+ );
245
+ }
246
+
247
+ export function inspectLodOcclusion(input: LodOcclusionInspectionInput): LodOcclusionInspection {
248
+ const row = copyRow(input);
249
+ const defaultBudget = createVisibilityBudget();
250
+ const budget = input.budget ?? {
251
+ configuredQueryBudget: defaultBudget.configuredQueryBudget,
252
+ effectiveQueryBudget: defaultBudget.effectiveQueryBudget,
253
+ settleSubmits: defaultBudget.settleSubmits,
254
+ retestSubmits: defaultBudget.retestSubmits,
255
+ expirySubmits: defaultBudget.expirySubmits,
256
+ };
257
+ for (const [name, value] of Object.entries(budget))
258
+ nonNegativeInteger(`inspection.budget.${name}`, value);
259
+ const submit = input.submit ?? {
260
+ frameId: input.generation,
261
+ build: 'unknown',
262
+ deviceGeneration: 0,
263
+ };
264
+ nonNegativeInteger('inspection.submit.frameId', submit.frameId);
265
+ nonNegativeInteger('inspection.submit.deviceGeneration', submit.deviceGeneration);
266
+ if (typeof submit.build !== 'string' || submit.build.length === 0) {
267
+ throw new Error('inspection.submit.build must be a non-empty string');
268
+ }
269
+ const worlds = copyWorlds(input.worlds, row);
270
+ for (const world of worlds) {
271
+ if (
272
+ world.attribution.status === 'same-submit' &&
273
+ (world.attribution.submit.frameId !== submit.frameId ||
274
+ world.attribution.submit.build !== submit.build ||
275
+ world.attribution.submit.deviceGeneration !== submit.deviceGeneration)
276
+ ) {
277
+ throw new Error('world attribution must reference the parent inspection submit');
278
+ }
279
+ }
280
+ const inspection: LodOcclusionInspection = Object.freeze({
281
+ schema: LOD_OCCLUSION_INSPECTION_SCHEMA,
282
+ ...row,
283
+ submit: Object.freeze({ ...submit }),
284
+ budget: Object.freeze({ ...budget }),
285
+ worlds,
286
+ });
287
+ if (JSON.stringify(inspection).length > LOD_OCCLUSION_INSPECTION_MAX_BYTES) {
288
+ throw new Error(`LOD occlusion inspection exceeds ${LOD_OCCLUSION_INSPECTION_MAX_BYTES} bytes`);
289
+ }
290
+ return inspection;
291
+ }
292
+
293
+ export function serializeLodOcclusionInspection(inspection: LodOcclusionInspection): string {
294
+ const serialized = JSON.stringify(inspection);
295
+ if (serialized.length > LOD_OCCLUSION_INSPECTION_MAX_BYTES) {
296
+ throw new Error(`LOD occlusion inspection exceeds ${LOD_OCCLUSION_INSPECTION_MAX_BYTES} bytes`);
297
+ }
298
+ return serialized;
299
+ }
300
+
301
+ export function consumeLodOcclusionInspection(
302
+ inspection: LodOcclusionInspection,
303
+ ): LodOcclusionInspectionAction {
304
+ if (!inspection.fallback.active) return { action: 'none' };
305
+ const { reason, error } = inspection.fallback;
306
+ const action =
307
+ error.code.startsWith('asset-') || reason === 'producer-failed'
308
+ ? 'rebuild'
309
+ : reason === 'query-failed' || reason === 'page-exhausted' || reason === 'device-loss'
310
+ ? 'retry'
311
+ : 'cold-cook';
312
+ return { action, sourceKey: inspection.root.sourceKey, reason };
313
+ }
@@ -0,0 +1,84 @@
1
+ import type { MeshLodLevel } from '@forgeax/engine-types';
2
+
3
+ export interface ProjectedHeightInput {
4
+ readonly radius: number;
5
+ readonly depth: number;
6
+ readonly projection: 'perspective' | 'orthographic';
7
+ readonly fov?: number;
8
+ readonly orthoHeight?: number;
9
+ }
10
+
11
+ export interface LodSelectorInput {
12
+ /** Lower-detail levels in ascending detail order; LOD0 is implicit. */
13
+ readonly levels: readonly Pick<MeshLodLevel, 'screenCoverage'>[];
14
+ readonly projectedHeight: number;
15
+ readonly previousLevel: number;
16
+ readonly hysteresis: number;
17
+ /** Index zero is the root and must remain the safe fallback. */
18
+ readonly ready: readonly boolean[];
19
+ readonly historyValid: boolean;
20
+ }
21
+
22
+ export interface LodSelection {
23
+ readonly level: number;
24
+ readonly confidence: number;
25
+ }
26
+
27
+ export function projectedHeight(input: ProjectedHeightInput): number {
28
+ if (!Number.isFinite(input.radius) || input.radius <= 0 || !Number.isFinite(input.depth)) {
29
+ return Number.NaN;
30
+ }
31
+ if (input.projection === 'perspective') {
32
+ if (!Number.isFinite(input.fov) || (input.fov ?? 0) <= 0 || input.depth <= 0) return Number.NaN;
33
+ return (2 * input.radius) / (input.depth * Math.tan((input.fov ?? 0) / 2));
34
+ }
35
+ if (!Number.isFinite(input.orthoHeight) || (input.orthoHeight ?? 0) <= 0) return Number.NaN;
36
+ return (2 * input.radius) / (input.orthoHeight ?? 0);
37
+ }
38
+
39
+ function rawLevel(levels: readonly Pick<MeshLodLevel, 'screenCoverage'>[], height: number): number {
40
+ for (let index = 0; index < levels.length; index += 1) {
41
+ const threshold = levels[index]?.screenCoverage;
42
+ if (threshold !== undefined && height >= threshold) return index;
43
+ }
44
+ return levels.length;
45
+ }
46
+
47
+ function hysteresisLevel(input: LodSelectorInput, raw: number): number {
48
+ if (!input.historyValid || input.previousLevel < 0 || input.previousLevel > input.levels.length) {
49
+ return raw;
50
+ }
51
+ const previous = input.previousLevel;
52
+ if (raw === previous) return previous;
53
+ const clampedHysteresis = Math.min(Math.max(input.hysteresis, 0), 0.99);
54
+ // A downgrade crosses the next lower-detail threshold; an upgrade crosses
55
+ // the threshold that introduced the current level. These are adjacent
56
+ // boundaries even when a frame jumps over several levels.
57
+ const thresholdIndex =
58
+ raw > previous
59
+ ? Math.min(previous, input.levels.length - 1)
60
+ : Math.min(previous, input.levels.length) - 1;
61
+ const threshold =
62
+ thresholdIndex < 0
63
+ ? input.levels[0]?.screenCoverage
64
+ : input.levels[thresholdIndex]?.screenCoverage;
65
+ if (threshold === undefined || !Number.isFinite(threshold)) return raw;
66
+ const height = input.projectedHeight;
67
+ if (raw > previous && height >= threshold * (1 - clampedHysteresis)) return previous;
68
+ if (raw < previous && height < threshold * (1 + clampedHysteresis)) return previous;
69
+ return raw;
70
+ }
71
+
72
+ export function selectLod(input: LodSelectorInput): LodSelection {
73
+ const root = 0;
74
+ if (!Number.isFinite(input.projectedHeight) || input.projectedHeight <= 0) {
75
+ return { level: root, confidence: 1 };
76
+ }
77
+ const raw = rawLevel(input.levels, input.projectedHeight);
78
+ const selected = Math.max(root, Math.min(input.levels.length, hysteresisLevel(input, raw)));
79
+ if (input.ready[selected] === true) return { level: selected, confidence: 1 };
80
+ for (let level = selected - 1; level >= root; level -= 1) {
81
+ if (input.ready[level] === true) return { level, confidence: 1 };
82
+ }
83
+ return { level: root, confidence: 1 };
84
+ }