@forgeax/engine-render 0.1.20 → 0.1.23

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 (645) hide show
  1. package/README.md +365 -29
  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/device-feature-admission.d.ts +18 -0
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  7. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +30 -19
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/host-contract.d.ts +19 -0
  11. package/dist/assembly/host-contract.d.ts.map +1 -1
  12. package/dist/assembly/render-target-host.d.ts +46 -0
  13. package/dist/assembly/render-target-host.d.ts.map +1 -0
  14. package/dist/assets/render-asset-port.d.ts.map +1 -1
  15. package/dist/authoring.mjs +4 -1
  16. package/dist/authoring.mjs.map +1 -1
  17. package/dist/capture/cube-views.d.ts +21 -0
  18. package/dist/capture/cube-views.d.ts.map +1 -0
  19. package/dist/capture/scheduler.d.ts +54 -0
  20. package/dist/capture/scheduler.d.ts.map +1 -0
  21. package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
  22. package/dist/chunk-4L3P6DT5.mjs.map +1 -0
  23. package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
  24. package/dist/chunk-4NPUASZR.mjs.map +1 -0
  25. package/dist/chunk-DIYW3M5D.mjs +5085 -0
  26. package/dist/chunk-DIYW3M5D.mjs.map +1 -0
  27. package/dist/chunk-FBC4R44J.mjs +1003 -0
  28. package/dist/chunk-FBC4R44J.mjs.map +1 -0
  29. package/dist/{chunk-CDTWTHZR.mjs → chunk-FF7K3FSO.mjs} +474 -168
  30. package/dist/chunk-FF7K3FSO.mjs.map +1 -0
  31. package/dist/chunk-HZUPSYW7.mjs +1104 -0
  32. package/dist/chunk-HZUPSYW7.mjs.map +1 -0
  33. package/dist/{chunk-TBQJOK2L.mjs → chunk-QODA5UKU.mjs} +321 -269
  34. package/dist/chunk-QODA5UKU.mjs.map +1 -0
  35. package/dist/chunk-YALPS44A.mjs +296 -0
  36. package/dist/chunk-YALPS44A.mjs.map +1 -0
  37. package/dist/{chunk-4XINDLAY.mjs → chunk-ZUWKAZ2I.mjs} +1963 -2178
  38. package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
  39. package/dist/cluster-binner.d.ts +5 -0
  40. package/dist/cluster-binner.d.ts.map +1 -1
  41. package/dist/components/camera.d.ts +1 -0
  42. package/dist/components/camera.d.ts.map +1 -1
  43. package/dist/components/cube-camera.d.ts +20 -0
  44. package/dist/components/cube-camera.d.ts.map +1 -0
  45. package/dist/components/directional-light.d.ts +11 -2
  46. package/dist/components/directional-light.d.ts.map +1 -1
  47. package/dist/components/directional-shadow-filter.d.ts +25 -0
  48. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  49. package/dist/components/index.d.ts +5 -0
  50. package/dist/components/index.d.ts.map +1 -1
  51. package/dist/components/light-helpers.d.ts +4 -0
  52. package/dist/components/light-helpers.d.ts.map +1 -1
  53. package/dist/components/light-probe.d.ts +9 -0
  54. package/dist/components/light-probe.d.ts.map +1 -0
  55. package/dist/components/rect-area-light.d.ts +12 -0
  56. package/dist/components/rect-area-light.d.ts.map +1 -0
  57. package/dist/components/reflection-probe.d.ts +17 -0
  58. package/dist/components/reflection-probe.d.ts.map +1 -0
  59. package/dist/components/scene-instance.d.ts +4 -0
  60. package/dist/components/scene-instance.d.ts.map +1 -1
  61. package/dist/components/spot-light.d.ts +3 -0
  62. package/dist/components/spot-light.d.ts.map +1 -1
  63. package/dist/construct-renderer.mjs +14836 -9620
  64. package/dist/construct-renderer.mjs.map +1 -1
  65. package/dist/device/gpu-residency.d.ts +8 -1
  66. package/dist/device/gpu-residency.d.ts.map +1 -1
  67. package/dist/device/lifecycle-transaction.d.ts +2 -0
  68. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  69. package/dist/environment/frame.d.ts.map +1 -1
  70. package/dist/environment/lifecycle.d.ts +5 -0
  71. package/dist/environment/lifecycle.d.ts.map +1 -1
  72. package/dist/errors/gpu-driven.d.ts +26 -0
  73. package/dist/errors/gpu-driven.d.ts.map +1 -0
  74. package/dist/errors/index.d.ts +1 -0
  75. package/dist/errors/index.d.ts.map +1 -1
  76. package/dist/errors/recover.d.ts +9 -1
  77. package/dist/errors/recover.d.ts.map +1 -1
  78. package/dist/errors/render.d.ts +166 -93
  79. package/dist/errors/render.d.ts.map +1 -1
  80. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  81. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  82. package/dist/extract/environment.d.ts +14 -0
  83. package/dist/extract/environment.d.ts.map +1 -1
  84. package/dist/extract/gpu-driven.d.ts +50 -0
  85. package/dist/extract/gpu-driven.d.ts.map +1 -0
  86. package/dist/extract/visibility.d.ts.map +1 -1
  87. package/dist/features/fullscreen.d.ts.map +1 -1
  88. package/dist/features/targets.d.ts.map +1 -1
  89. package/dist/features/types.d.ts +10 -0
  90. package/dist/features/types.d.ts.map +1 -1
  91. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  92. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  93. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  94. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  95. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  96. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  97. package/dist/gpu-driven/production-raster.d.ts +97 -2
  98. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  99. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  100. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  101. package/dist/gpu-scene-schema.d.ts +14 -0
  102. package/dist/gpu-scene-schema.d.ts.map +1 -1
  103. package/dist/gpu-scene.d.ts +8 -1
  104. package/dist/gpu-scene.d.ts.map +1 -1
  105. package/dist/gpu-usage.d.ts +0 -1
  106. package/dist/gpu-usage.d.ts.map +1 -1
  107. package/dist/hdrp-buffers.d.ts +21 -19
  108. package/dist/hdrp-buffers.d.ts.map +1 -1
  109. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  110. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  111. package/dist/ibl/kernel-cache.d.ts +36 -0
  112. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  113. package/dist/index.d.ts +39 -3
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.mjs +91 -6
  116. package/dist/index.mjs.map +1 -1
  117. package/dist/inspection-types.d.ts +214 -1
  118. package/dist/inspection-types.d.ts.map +1 -1
  119. package/dist/instances-derived-bounds.d.ts +50 -0
  120. package/dist/instances-derived-bounds.d.ts.map +1 -0
  121. package/dist/internal.d.ts +9 -0
  122. package/dist/internal.d.ts.map +1 -1
  123. package/dist/internal.mjs +23 -2
  124. package/dist/internal.mjs.map +1 -1
  125. package/dist/light-buffer-layout.d.ts +58 -140
  126. package/dist/light-buffer-layout.d.ts.map +1 -1
  127. package/dist/materials.d.ts.map +1 -1
  128. package/dist/mesh-material-bindings.d.ts +54 -0
  129. package/dist/mesh-material-bindings.d.ts.map +1 -1
  130. package/dist/pbr-pipeline.d.ts +38 -14
  131. package/dist/pbr-pipeline.d.ts.map +1 -1
  132. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  133. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  134. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  135. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  136. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  137. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  138. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  139. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  140. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  141. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  142. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  143. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  144. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  145. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  146. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  147. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  148. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  149. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  150. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  151. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  152. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  153. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  154. package/dist/pipeline/standard-post.d.ts.map +1 -1
  155. package/dist/pipeline/standard-profile.d.ts +5 -26
  156. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  157. package/dist/pipeline-builder.d.ts +2 -0
  158. package/dist/pipeline-builder.d.ts.map +1 -1
  159. package/dist/pipeline-spec.d.ts +40 -7
  160. package/dist/pipeline-spec.d.ts.map +1 -1
  161. package/dist/plugin.d.ts.map +1 -1
  162. package/dist/point-shadow-inspection.d.ts +21 -0
  163. package/dist/point-shadow-inspection.d.ts.map +1 -0
  164. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  165. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  166. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  167. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  168. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  169. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  170. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  171. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  172. package/dist/record/frame-lighting.d.ts +33 -36
  173. package/dist/record/frame-lighting.d.ts.map +1 -1
  174. package/dist/record/frame-snapshot.d.ts +78 -24
  175. package/dist/record/frame-snapshot.d.ts.map +1 -1
  176. package/dist/record/frame-targets.d.ts +5 -0
  177. package/dist/record/frame-targets.d.ts.map +1 -1
  178. package/dist/record/frame.d.ts +16 -6
  179. package/dist/record/frame.d.ts.map +1 -1
  180. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  181. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  182. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  183. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  184. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  185. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  186. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  187. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  188. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  189. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  190. package/dist/record/gpu-timing.d.ts +20 -3
  191. package/dist/record/gpu-timing.d.ts.map +1 -1
  192. package/dist/record/helpers.d.ts +0 -20
  193. package/dist/record/helpers.d.ts.map +1 -1
  194. package/dist/record/main-pass-geometry.d.ts +6 -1
  195. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  196. package/dist/record/main-pass-material.d.ts +25 -2
  197. package/dist/record/main-pass-material.d.ts.map +1 -1
  198. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  199. package/dist/record/main-pass.d.ts +2 -0
  200. package/dist/record/main-pass.d.ts.map +1 -1
  201. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  202. package/dist/record/render-context.d.ts +98 -6
  203. package/dist/record/render-context.d.ts.map +1 -1
  204. package/dist/record/shadow-pass.d.ts.map +1 -1
  205. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  206. package/dist/record/typed-frame-graph.d.ts +119 -4
  207. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  208. package/dist/record/view-ubo.d.ts +7 -5
  209. package/dist/record/view-ubo.d.ts.map +1 -1
  210. package/dist/reflection/filter.d.ts +22 -0
  211. package/dist/reflection/filter.d.ts.map +1 -0
  212. package/dist/reflection/gpu-table.d.ts +25 -0
  213. package/dist/reflection/gpu-table.d.ts.map +1 -0
  214. package/dist/reflection/inspection.d.ts +10 -0
  215. package/dist/reflection/inspection.d.ts.map +1 -0
  216. package/dist/reflection/projection.d.ts +140 -0
  217. package/dist/reflection/projection.d.ts.map +1 -0
  218. package/dist/reflection/record-owner.d.ts +60 -0
  219. package/dist/reflection/record-owner.d.ts.map +1 -0
  220. package/dist/render-contract.d.ts +62 -14
  221. package/dist/render-contract.d.ts.map +1 -1
  222. package/dist/render-graph-primitives.d.ts +1 -0
  223. package/dist/render-graph-primitives.d.ts.map +1 -1
  224. package/dist/render-pipeline.d.ts +49 -2
  225. package/dist/render-pipeline.d.ts.map +1 -1
  226. package/dist/render-system-extract.d.ts +160 -31
  227. package/dist/render-system-extract.d.ts.map +1 -1
  228. package/dist/render-system.d.ts +28 -2
  229. package/dist/render-system.d.ts.map +1 -1
  230. package/dist/renderer.d.ts +43 -0
  231. package/dist/renderer.d.ts.map +1 -0
  232. package/dist/scene/probe-blend-record.d.ts +22 -0
  233. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  234. package/dist/scene/probe-blend.d.ts +142 -0
  235. package/dist/scene/probe-blend.d.ts.map +1 -0
  236. package/dist/scene/render-scene-types.d.ts +0 -6
  237. package/dist/scene/render-scene-types.d.ts.map +1 -1
  238. package/dist/scene/render-scene.d.ts +117 -9
  239. package/dist/scene/render-scene.d.ts.map +1 -1
  240. package/dist/scene/visibility/bounds.d.ts +20 -0
  241. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  242. package/dist/scene/visibility/budget.d.ts +16 -0
  243. package/dist/scene/visibility/budget.d.ts.map +1 -0
  244. package/dist/scene/visibility/facet.d.ts +73 -0
  245. package/dist/scene/visibility/facet.d.ts.map +1 -0
  246. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  247. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  248. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  249. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  250. package/dist/scene/visibility/inspection.d.ts +132 -0
  251. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  252. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  253. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  254. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  255. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  256. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  257. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  258. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  259. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  260. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  261. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  262. package/dist/scene/visibility/types.d.ts +34 -0
  263. package/dist/scene/visibility/types.d.ts.map +1 -0
  264. package/dist/shadow-atlas.d.ts +3 -3
  265. package/dist/shadow-atlas.d.ts.map +1 -1
  266. package/dist/ssao-buffers.d.ts +3 -0
  267. package/dist/ssao-buffers.d.ts.map +1 -1
  268. package/dist/ssr/admission.d.ts +122 -0
  269. package/dist/ssr/admission.d.ts.map +1 -0
  270. package/dist/ssr/composition.d.ts +32 -0
  271. package/dist/ssr/composition.d.ts.map +1 -0
  272. package/dist/ssr/errors.d.ts +39 -0
  273. package/dist/ssr/errors.d.ts.map +1 -0
  274. package/dist/ssr/identity.d.ts +13 -0
  275. package/dist/ssr/identity.d.ts.map +1 -0
  276. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  277. package/dist/targets/contracts.d.ts +66 -0
  278. package/dist/targets/contracts.d.ts.map +1 -0
  279. package/dist/targets/graph-projection.d.ts +29 -0
  280. package/dist/targets/graph-projection.d.ts.map +1 -0
  281. package/dist/targets/material-source.d.ts +34 -0
  282. package/dist/targets/material-source.d.ts.map +1 -0
  283. package/dist/targets/owner.d.ts +32 -0
  284. package/dist/targets/owner.d.ts.map +1 -0
  285. package/dist/targets/physical.d.ts +18 -0
  286. package/dist/targets/physical.d.ts.map +1 -0
  287. package/dist/targets/readback.d.ts +39 -0
  288. package/dist/targets/readback.d.ts.map +1 -0
  289. package/dist/temporal/frame-coordinator.d.ts +13 -0
  290. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  291. package/dist/temporal/frame.d.ts +2 -0
  292. package/dist/temporal/frame.d.ts.map +1 -1
  293. package/dist/temporal/index.mjs +6 -5
  294. package/dist/temporal/view.d.ts +7 -0
  295. package/dist/temporal/view.d.ts.map +1 -1
  296. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  297. package/dist/typed-render-graph-primitives.d.ts +5 -1
  298. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  299. package/dist/typed-shadow-passes.d.ts +1 -1
  300. package/dist/typed-shadow-passes.d.ts.map +1 -1
  301. package/dist/volume/passes.d.ts +9 -5
  302. package/dist/volume/passes.d.ts.map +1 -1
  303. package/package.json +20 -20
  304. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  305. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  306. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  307. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  308. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +12 -2
  309. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  310. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  311. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  312. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  313. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  314. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  315. package/src/__tests__/device-feature-admission.unit.test.ts +85 -0
  316. package/src/__tests__/direct-light-snapshot.unit.test.ts +62 -6
  317. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  318. package/src/__tests__/environment-generation.integration.test.ts +16 -0
  319. package/src/__tests__/evidence-identity.ts +13 -0
  320. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  321. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  322. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  323. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  324. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  325. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  326. package/src/__tests__/factory-contract.integration.test.ts +288 -29
  327. package/src/__tests__/fog-component.unit.test.ts +67 -0
  328. package/src/__tests__/fog.integration.test.ts +86 -0
  329. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  330. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  331. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  332. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -8
  333. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +18 -1
  334. package/src/__tests__/gpu-driven-production.integration.test.ts +579 -17
  335. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  336. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  337. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +101 -6
  338. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  339. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  340. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  341. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  342. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  343. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  344. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  345. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  346. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  347. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  348. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  349. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  350. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  351. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  352. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  353. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  354. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  355. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  356. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  357. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  358. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  359. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  360. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  361. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  362. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  363. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  364. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  365. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  366. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  367. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  368. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  369. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  370. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  371. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  372. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  373. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  374. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  375. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  376. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  377. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  378. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  379. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  380. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  381. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  382. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  383. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  384. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  385. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  386. package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
  387. package/src/__tests__/material-observation.integration.test.ts +101 -0
  388. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  389. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  390. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  391. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  392. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  393. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  394. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  395. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  396. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  397. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  398. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  399. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  400. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  401. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  402. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  403. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  404. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  405. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  406. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  407. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  408. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  409. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  410. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  411. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  412. package/src/__tests__/public-surface.test-d.ts +10 -0
  413. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  414. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  415. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  416. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  417. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  418. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  419. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  420. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  421. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  422. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  423. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  424. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  425. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  426. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  427. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  428. package/src/__tests__/render-error-code-owner.test-d.ts +19 -3
  429. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  430. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  431. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  432. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  433. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  434. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  435. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  436. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  437. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  438. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  439. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  440. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  441. package/src/__tests__/render-target-public-schema.json +107 -0
  442. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  443. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  444. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  445. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  446. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +124 -0
  447. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  448. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  449. package/src/__tests__/skin-motion-regression.unit.test.ts +154 -0
  450. package/src/__tests__/skinned-shadow-caster.test.ts +13 -4
  451. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  452. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  453. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  454. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  455. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  456. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  457. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  458. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +299 -0
  459. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +15 -5
  460. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  461. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  462. package/src/__tests__/standard-lighting-write.unit.test.ts +441 -0
  463. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  464. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  465. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  466. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  467. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
  468. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  469. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  470. package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
  471. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  472. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  473. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  474. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  475. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  476. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  477. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  478. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  479. package/src/assembly/backend-contract.ts +13 -0
  480. package/src/assembly/device-feature-admission.ts +80 -0
  481. package/src/assembly/directional-shadow-inspection.ts +122 -0
  482. package/src/assembly/factory.ts +1411 -366
  483. package/src/assembly/host-contract.ts +35 -0
  484. package/src/assembly/render-target-host.ts +847 -0
  485. package/src/assets/asset-decoders.ts +1 -1
  486. package/src/assets/render-asset-port.ts +7 -1
  487. package/src/capture/cube-views.ts +69 -0
  488. package/src/capture/scheduler.ts +265 -0
  489. package/src/cluster-binner.ts +31 -0
  490. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  491. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  492. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  493. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  494. package/src/components/camera.ts +4 -0
  495. package/src/components/cube-camera.ts +52 -0
  496. package/src/components/directional-light.ts +22 -2
  497. package/src/components/directional-shadow-filter.ts +57 -0
  498. package/src/components/index.ts +5 -0
  499. package/src/components/light-helpers.ts +133 -8
  500. package/src/components/light-probe.ts +13 -0
  501. package/src/components/rect-area-light.ts +15 -0
  502. package/src/components/reflection-probe.ts +49 -0
  503. package/src/components/scene-instance.ts +6 -0
  504. package/src/components/spot-light.ts +3 -0
  505. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  506. package/src/device/gpu-residency.ts +140 -9
  507. package/src/device/lifecycle-transaction.ts +12 -0
  508. package/src/environment/frame.ts +6 -24
  509. package/src/environment/lifecycle.ts +20 -1
  510. package/src/errors/gpu-driven.ts +92 -0
  511. package/src/errors/index.ts +1 -0
  512. package/src/errors/recover.ts +32 -0
  513. package/src/errors/render.ts +341 -143
  514. package/src/extract/camera.ts +3 -3
  515. package/src/extract/directional-shadow-projection.ts +61 -0
  516. package/src/extract/environment.ts +57 -0
  517. package/src/extract/gpu-driven.ts +108 -0
  518. package/src/extract/visibility.ts +2 -1
  519. package/src/features/fullscreen.ts +3 -0
  520. package/src/features/targets.ts +4 -1
  521. package/src/features/types.ts +10 -0
  522. package/src/fullscreen-post-process-pass.ts +43 -2
  523. package/src/gpu-driven/batch-topology.ts +207 -28
  524. package/src/gpu-driven/prepared-draw.ts +208 -0
  525. package/src/gpu-driven/production-raster.ts +953 -43
  526. package/src/gpu-driven/view-gpu.ts +411 -29
  527. package/src/gpu-scene-schema.ts +13 -0
  528. package/src/gpu-scene.ts +140 -64
  529. package/src/gpu-usage.ts +0 -1
  530. package/src/hdrp-buffers.ts +169 -63
  531. package/src/ibl/IblPipelineCache.ts +11 -0
  532. package/src/ibl/kernel-cache.ts +88 -0
  533. package/src/index.ts +222 -0
  534. package/src/inspection-types.ts +276 -1
  535. package/src/instances-derived-bounds.ts +171 -0
  536. package/src/internal.ts +38 -0
  537. package/src/light-buffer-layout.ts +155 -310
  538. package/src/materials.ts +16 -2
  539. package/src/mesh-material-bindings.ts +132 -0
  540. package/src/pbr-pipeline.ts +255 -60
  541. package/src/pipeline/standard-forward-lane.ts +93 -16
  542. package/src/pipeline/standard-lighting/errors.ts +6 -0
  543. package/src/pipeline/standard-lighting/graph.ts +158 -0
  544. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  545. package/src/pipeline/standard-lighting/layout.ts +31 -0
  546. package/src/pipeline/standard-lighting/light-frame.ts +35 -0
  547. package/src/pipeline/standard-lighting/membership.ts +43 -0
  548. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  549. package/src/pipeline/standard-lighting/topology.ts +120 -0
  550. package/src/pipeline/standard-lighting/transport.ts +78 -0
  551. package/src/pipeline/standard-pipeline.ts +124 -193
  552. package/src/pipeline/standard-post.ts +26 -7
  553. package/src/pipeline/standard-profile.ts +17 -47
  554. package/src/pipeline-builder.ts +5 -2
  555. package/src/pipeline-spec.ts +95 -21
  556. package/src/plugin.ts +4 -0
  557. package/src/point-shadow-inspection.ts +55 -0
  558. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  559. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  560. package/src/prepare/extended-lighting/resources.ts +389 -0
  561. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  562. package/src/prepare/extended-lighting/state.ts +92 -0
  563. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  564. package/src/record/__tests__/temporal-payload.unit.test.ts +64 -1
  565. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  566. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
  567. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  568. package/src/record/frame-lighting.ts +591 -452
  569. package/src/record/frame-snapshot.ts +92 -39
  570. package/src/record/frame-targets.ts +17 -0
  571. package/src/record/frame.ts +662 -158
  572. package/src/record/gpu-pass-timing/contract.ts +180 -0
  573. package/src/record/gpu-pass-timing/errors.ts +34 -0
  574. package/src/record/gpu-pass-timing/index.ts +37 -0
  575. package/src/record/gpu-pass-timing/parser.ts +97 -0
  576. package/src/record/gpu-pass-timing/session.ts +938 -0
  577. package/src/record/gpu-timing.ts +112 -39
  578. package/src/record/helpers.ts +0 -58
  579. package/src/record/main-pass-geometry.ts +324 -119
  580. package/src/record/main-pass-material.ts +103 -10
  581. package/src/record/main-pass-sprite-draws.ts +99 -103
  582. package/src/record/main-pass.ts +71 -15
  583. package/src/record/mesh-ssbo.ts +47 -36
  584. package/src/record/render-context.ts +136 -3
  585. package/src/record/shadow-pass.ts +83 -68
  586. package/src/record/skybox-post-pass.ts +20 -4
  587. package/src/record/typed-frame-graph.ts +1034 -43
  588. package/src/record/view-ubo.ts +72 -47
  589. package/src/reflection/filter.ts +81 -0
  590. package/src/reflection/gpu-table.ts +44 -0
  591. package/src/reflection/inspection.ts +67 -0
  592. package/src/reflection/projection.ts +476 -0
  593. package/src/reflection/record-owner.ts +1283 -0
  594. package/src/render-contract.ts +93 -10
  595. package/src/render-graph-primitives.ts +9 -4
  596. package/src/render-pipeline.ts +122 -2
  597. package/src/render-system-extract.ts +1041 -225
  598. package/src/render-system.ts +1310 -85
  599. package/src/renderer.ts +222 -0
  600. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  601. package/src/scene/probe-blend-record.ts +54 -0
  602. package/src/scene/probe-blend.ts +813 -0
  603. package/src/scene/render-scene-types.ts +0 -7
  604. package/src/scene/render-scene.ts +942 -171
  605. package/src/scene/visibility/bounds.ts +57 -0
  606. package/src/scene/visibility/budget.ts +40 -0
  607. package/src/scene/visibility/facet.ts +400 -0
  608. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  609. package/src/scene/visibility/gpu-lod.ts +119 -0
  610. package/src/scene/visibility/inspection.ts +313 -0
  611. package/src/scene/visibility/lod-selector.ts +84 -0
  612. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  613. package/src/scene/visibility/occlusion-pass.ts +165 -0
  614. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  615. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  616. package/src/scene/visibility/types.ts +50 -0
  617. package/src/shadow-atlas.ts +5 -5
  618. package/src/ssao-buffers.ts +9 -2
  619. package/src/ssr/admission.ts +451 -0
  620. package/src/ssr/composition.ts +73 -0
  621. package/src/ssr/errors.ts +76 -0
  622. package/src/ssr/identity.ts +12 -0
  623. package/src/systems/skin-palette-allocator.ts +1 -1
  624. package/src/targets/contracts.ts +182 -0
  625. package/src/targets/graph-projection.ts +169 -0
  626. package/src/targets/material-source.ts +143 -0
  627. package/src/targets/owner.ts +214 -0
  628. package/src/targets/physical.ts +18 -0
  629. package/src/targets/readback.ts +153 -0
  630. package/src/temporal/frame-coordinator.ts +26 -0
  631. package/src/temporal/frame.ts +14 -1
  632. package/src/temporal/view.ts +11 -0
  633. package/src/tilemap-chunk-extract-system.ts +4 -4
  634. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
  635. package/src/typed-render-graph-primitives.ts +53 -3
  636. package/src/typed-shadow-passes.ts +46 -40
  637. package/src/volume/passes.ts +61 -58
  638. package/dist/.tsbuildinfo +0 -1
  639. package/dist/chunk-4XINDLAY.mjs.map +0 -1
  640. package/dist/chunk-CDTWTHZR.mjs.map +0 -1
  641. package/dist/chunk-M2MP4ZOU.mjs +0 -73
  642. package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
  643. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  644. package/dist/chunk-TBQJOK2L.mjs.map +0 -1
  645. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -0,0 +1,140 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createVisibilityBudget } from '../scene/visibility/budget';
3
+ import {
4
+ primitiveKey,
5
+ primitiveKeyId,
6
+ VisibilityFacetStore,
7
+ viewKey,
8
+ viewKeyId,
9
+ } from '../scene/visibility/facet';
10
+
11
+ const view = (cameraEntity: number) =>
12
+ viewKey({
13
+ attachmentId: 'budget-scene',
14
+ cameraEntity,
15
+ viewRole: 'main',
16
+ viewGeneration: 1,
17
+ });
18
+
19
+ const primitive = (slot: number) =>
20
+ primitiveKey({
21
+ attachmentId: 'budget-scene',
22
+ worldGeneration: 1,
23
+ primitiveSlot: slot,
24
+ slotGeneration: 1,
25
+ });
26
+
27
+ describe('visibility budget integration', () => {
28
+ it('shares one derived window across views and LOD levels', () => {
29
+ const budget = createVisibilityBudget(2048);
30
+ const facets = new VisibilityFacetStore(budget);
31
+ const views = [view(1), view(2)];
32
+ for (const currentView of views) {
33
+ facets.activateView(currentView);
34
+ for (let level = 0; level < 8; level += 1) {
35
+ const currentPrimitive = primitive(currentView.cameraEntity * 10 + level);
36
+ facets.setCandidate(currentView, currentPrimitive, { level, confidence: 1 });
37
+ facets.applyConfidence(currentView, currentPrimitive, {
38
+ type: 'result',
39
+ samples: 0,
40
+ submissionGeneration: 1,
41
+ });
42
+ facets.applyConfidence(currentView, currentPrimitive, {
43
+ type: 'result',
44
+ samples: 0,
45
+ submissionGeneration: 2,
46
+ });
47
+ }
48
+ }
49
+
50
+ expect(facets.visibilityBudget()).toBe(budget);
51
+ expect(facets.visibilityBudget()).toMatchObject({
52
+ effectiveQueryBudget: 2048,
53
+ settleSubmits: 98,
54
+ retestSubmits: 44,
55
+ expirySubmits: 88,
56
+ });
57
+
58
+ facets.advanceSuccessfulSubmits(2 + budget.retestSubmits);
59
+ for (const currentView of views) {
60
+ expect(facets.shouldQuery(currentView, primitive(currentView.cameraEntity * 10))).toBe(true);
61
+ }
62
+ });
63
+
64
+ it('keeps invalidation visible and requeues the same identity without extending evidence', () => {
65
+ const budget = createVisibilityBudget(4096);
66
+ const facets = new VisibilityFacetStore(budget);
67
+ const currentView = view(3);
68
+ const currentPrimitive = primitive(3);
69
+ facets.activateView(currentView);
70
+ facets.setCandidate(currentView, currentPrimitive, { level: 2, confidence: 1 });
71
+ facets.applyConfidence(currentView, currentPrimitive, {
72
+ type: 'result',
73
+ samples: 0,
74
+ submissionGeneration: 1,
75
+ });
76
+ facets.applyConfidence(currentView, currentPrimitive, {
77
+ type: 'result',
78
+ samples: 0,
79
+ submissionGeneration: 2,
80
+ });
81
+ facets.advanceSuccessfulSubmits(2 + budget.retestSubmits, [
82
+ { view: currentView, primitive: currentPrimitive },
83
+ ]);
84
+ expect(facets.getConfidence(currentView, currentPrimitive).status).toBe('hidden');
85
+
86
+ facets.applyConfidence(currentView, currentPrimitive, {
87
+ type: 'invalidate',
88
+ reason: 'world-reorder',
89
+ });
90
+ expect(facets.getConfidence(currentView, currentPrimitive)).toMatchObject({
91
+ status: 'visible',
92
+ dirty: true,
93
+ queryable: true,
94
+ });
95
+ expect(facets.dequeueQueryCandidates(currentView, 1)).toHaveLength(1);
96
+ });
97
+
98
+ it('uses expiry-only scheduling for a due hidden row while its query is in flight', () => {
99
+ const budget = createVisibilityBudget();
100
+ const facets = new VisibilityFacetStore(budget);
101
+ const currentView = view(4);
102
+ const currentPrimitive = primitive(4);
103
+ facets.activateView(currentView);
104
+ facets.setCandidate(currentView, currentPrimitive, { level: 0, confidence: 1 });
105
+ facets.applyConfidence(currentView, currentPrimitive, {
106
+ type: 'result',
107
+ samples: 0,
108
+ submissionGeneration: 1,
109
+ });
110
+ facets.applyConfidence(currentView, currentPrimitive, {
111
+ type: 'result',
112
+ samples: 0,
113
+ submissionGeneration: 2,
114
+ });
115
+ const dueGeneration = 2 + budget.retestSubmits;
116
+ facets.advanceSuccessfulSubmits(dueGeneration);
117
+ expect(facets.dequeueQueryCandidates(currentView, 1)).toHaveLength(1);
118
+
119
+ facets.applyConfidence(currentView, currentPrimitive, {
120
+ type: 'submit',
121
+ submissionGeneration: dueGeneration,
122
+ });
123
+ expect(facets.getConfidence(currentView, currentPrimitive).successfulSubmits).toBe(
124
+ budget.retestSubmits + 1,
125
+ );
126
+ const scheduler = facets as unknown as {
127
+ confidence: { wakeByGeneration: Map<number, Set<string>> };
128
+ };
129
+ const key = `${viewKeyId(currentView)}|${primitiveKeyId(currentPrimitive)}`;
130
+ expect([...scheduler.confidence.wakeByGeneration.values()].flat()).not.toContain(key);
131
+
132
+ facets.advanceSuccessfulSubmits(
133
+ dueGeneration + budget.expirySubmits - budget.retestSubmits - 1,
134
+ );
135
+ expect(facets.getConfidence(currentView, currentPrimitive)).toMatchObject({
136
+ status: 'visible',
137
+ dirty: true,
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ createVisibilityBudget,
4
+ DEFAULT_CONFIGURED_QUERY_BUDGET,
5
+ MAX_QUERY_BUDGET,
6
+ } from '../scene/visibility/budget';
7
+
8
+ describe('visibility budget derivation', () => {
9
+ it.each([
10
+ [
11
+ 2048,
12
+ {
13
+ effectiveQueryBudget: 2048,
14
+ oneSweepCandidates: 49,
15
+ oneSweepOccluded: 44,
16
+ settleSubmits: 98,
17
+ retestSubmits: 44,
18
+ expirySubmits: 88,
19
+ },
20
+ ],
21
+ [
22
+ 4096,
23
+ {
24
+ effectiveQueryBudget: 4096,
25
+ oneSweepCandidates: 25,
26
+ oneSweepOccluded: 22,
27
+ settleSubmits: 50,
28
+ retestSubmits: 22,
29
+ expirySubmits: 44,
30
+ },
31
+ ],
32
+ ])('derives the approved windows for Q=%d', (configuredQueryBudget, expected) => {
33
+ expect(createVisibilityBudget(configuredQueryBudget)).toMatchObject({
34
+ configuredQueryBudget,
35
+ ...expected,
36
+ });
37
+ });
38
+
39
+ it('uses the default and clamps larger configured budgets without changing page policy', () => {
40
+ expect(createVisibilityBudget()).toMatchObject({
41
+ configuredQueryBudget: DEFAULT_CONFIGURED_QUERY_BUDGET,
42
+ effectiveQueryBudget: DEFAULT_CONFIGURED_QUERY_BUDGET,
43
+ });
44
+ expect(createVisibilityBudget(MAX_QUERY_BUDGET + 1).effectiveQueryBudget).toBe(
45
+ MAX_QUERY_BUDGET,
46
+ );
47
+ });
48
+
49
+ it.each([
50
+ 0,
51
+ -1,
52
+ 1.5,
53
+ Number.NaN,
54
+ Number.POSITIVE_INFINITY,
55
+ ])('rejects invalid configured budget %s', (configuredQueryBudget) => {
56
+ expect(() => createVisibilityBudget(configuredQueryBudget)).toThrow(
57
+ 'configuredQueryBudget must be a positive safe integer',
58
+ );
59
+ });
60
+
61
+ it.each([1, 2, 3, 4, 8])('keeps derived windows independent of %d LOD levels', (levels) => {
62
+ const budget = createVisibilityBudget(2048);
63
+ expect({ levels, ...budget }).toMatchObject({
64
+ levels,
65
+ settleSubmits: 98,
66
+ retestSubmits: 44,
67
+ expirySubmits: 88,
68
+ });
69
+ });
70
+ });
@@ -1,5 +1,10 @@
1
1
  import { World } from '@forgeax/engine-ecs';
2
- import { ChildOf, registerPropagateTransforms, Transform } from '@forgeax/engine-scene';
2
+ import {
3
+ ChildOf,
4
+ GlobalTransform,
5
+ registerPropagateTransforms,
6
+ Transform,
7
+ } from '@forgeax/engine-scene';
3
8
  import { describe, expect, it, vi } from 'vitest';
4
9
  import { Fog } from '../components/fog';
5
10
  import { Visibility, VisibilityStateValue } from '../components/visibility';
@@ -67,13 +72,13 @@ describe('visibility extract orchestration', () => {
67
72
  const snapshot = frame.visibilitySnapshots[0];
68
73
  expect(snapshot?.get(parent)?.effective).toBe('visible');
69
74
  expect(snapshot?.get(child)?.effective).toBe('visible');
70
- expect(world.get(child, Transform).unwrap().world[12]).toBeCloseTo(3);
75
+ expect(world.get(child, GlobalTransform).unwrap().world[12]).toBeCloseTo(3);
71
76
  });
72
77
 
73
- it('keeps renderer extraction read-only over derived Transform.world', () => {
78
+ it('keeps renderer extraction read-only over derived GlobalTransform.world', () => {
74
79
  const { world, parent, child } = makeWorld();
75
80
  world.update(0).unwrap();
76
- const before = [...world.get(child, Transform).unwrap().world];
81
+ const before = [...world.get(child, GlobalTransform).unwrap().world];
77
82
  const beforeInspection = world.inspect();
78
83
  const beforeShape = {
79
84
  entityCount: beforeInspection.entityCount,
@@ -87,7 +92,7 @@ describe('visibility extract orchestration', () => {
87
92
  world.set(parent, Transform, { pos: [9, 0, 0] }).unwrap();
88
93
  extractFrames([world], 0);
89
94
 
90
- expect([...world.get(child, Transform).unwrap().world]).toEqual(before);
95
+ expect([...world.get(child, GlobalTransform).unwrap().world]).toEqual(before);
91
96
  const afterInspection = world.inspect();
92
97
  expect({
93
98
  entityCount: afterInspection.entityCount,
@@ -98,7 +103,7 @@ describe('visibility extract orchestration', () => {
98
103
  resourceKeys: afterInspection.resourceKeys,
99
104
  }).toEqual(beforeShape);
100
105
  world.update(0).unwrap();
101
- expect(world.get(child, Transform).unwrap().world[12]).toBeCloseTo(11);
106
+ expect(world.get(child, GlobalTransform).unwrap().world[12]).toBeCloseTo(11);
102
107
  });
103
108
 
104
109
  it('keeps direct prepared-kernel output aligned with extractFrames output', () => {
@@ -82,4 +82,19 @@ describe('visibility resolver truth table', () => {
82
82
  expect(result.get(parent)?.intent).toBeUndefined();
83
83
  expect(result.get(child)?.effective).toBe('visible');
84
84
  });
85
+
86
+ it('invalidates the cached snapshot when visibility intent changes', () => {
87
+ const world = new World();
88
+ const parent = spawn(world, 'visible');
89
+ const child = spawn(world, 'inherited', parent);
90
+
91
+ const visible = resolveVisibility(world);
92
+ expect(visible.effective(child)).toBe('visible');
93
+
94
+ world.set(parent, Visibility, { state: VisibilityStateValue.hidden }).unwrap();
95
+
96
+ const hidden = resolveVisibility(world);
97
+ expect(hidden).not.toBe(visible);
98
+ expect(hidden.effective(child)).toBe('hidden');
99
+ });
85
100
  });
@@ -13,8 +13,10 @@ const passes = readFileSync(new URL('../volume/passes.ts', import.meta.url), 'ut
13
13
 
14
14
  describe('volumetric fog Point and Spot accumulation', () => {
15
15
  it('accumulates Point and Spot in one fixed inject stage', () => {
16
- expect(inject).toContain('pointLightsBuffer');
17
- expect(inject).toContain('spotLightsBuffer');
16
+ expect(inject).toContain('light_data');
17
+ expect(inject).toContain('cluster_uniform');
18
+ expect(inject).not.toContain('pointLightsBuffer');
19
+ expect(inject).not.toContain('spotLightsBuffer');
18
20
  expect(inject).toContain('evalVolumePoint');
19
21
  expect(inject).toContain('evalVolumeSpot');
20
22
  expect(inject).toContain('shadow_visibility');
@@ -37,7 +39,8 @@ describe('volumetric fog Point and Spot accumulation', () => {
37
39
  });
38
40
 
39
41
  it('retains falsifier hooks for off lights, occluders, and shadow visibility', () => {
40
- expect(inject).toContain('count');
42
+ expect(inject).toContain('selected_cluster_light_slot');
43
+ expect(inject).toContain('cluster_uniform.grid.w');
41
44
  expect(inject).toContain('shadowAtlasTile');
42
45
  expect(inject).toContain('return 0.0');
43
46
  expect(integrate).toContain('evalSpotAttenuation');
@@ -2,12 +2,7 @@ import type { RhiDevice, Texture, TextureView } from '@forgeax/engine-rhi';
2
2
  import { createShaderModule, rhi } from '@forgeax/engine-rhi-webgpu';
3
3
  import { buildEngineShaderManifest } from '@forgeax/engine-vite-plugin-shader';
4
4
  import { describe, expect, it } from 'vitest';
5
- import {
6
- LIGHT_ARRAY_HEADER_BYTES,
7
- LIGHT_ARRAY_MAX_SLOTS,
8
- POINT_LIGHT_STD430_BYTES,
9
- SPOT_LIGHT_STD430_BYTES,
10
- } from '../light-buffer-layout';
5
+ import { BYTES_PER_DIRECT_LIGHT_SLOT } from '../light-buffer-layout';
11
6
 
12
7
  const MAP_READ = 0x0001;
13
8
  const BUFFER_COPY_DST = 0x0008;
@@ -21,6 +16,9 @@ const SIZE = 8;
21
16
  const PACKED_LAYERS = Math.ceil(SIZE / 4);
22
17
  const BYTES_PER_ROW = 256;
23
18
  const BYTES_PER_PIXEL = 8;
19
+ const MAX_CLUSTER_LIGHTS = 256;
20
+ const CLUSTER_LIGHT_DATA_BYTES = MAX_CLUSTER_LIGHTS * BYTES_PER_DIRECT_LIGHT_SLOT;
21
+ const CLUSTER_UNIFORM_BYTES = 32;
24
22
  const engineManifest = await buildEngineShaderManifest();
25
23
 
26
24
  interface StageValues {
@@ -190,15 +188,15 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
190
188
  size: 128,
191
189
  usage: 0x0040 | BUFFER_COPY_DST,
192
190
  });
193
- const spotLightsBuffer = device.createBuffer({
194
- label: 'stage-readback.spot-lights',
195
- size: LIGHT_ARRAY_HEADER_BYTES + LIGHT_ARRAY_MAX_SLOTS * SPOT_LIGHT_STD430_BYTES,
191
+ const lightDataBuffer = device.createBuffer({
192
+ label: 'stage-readback.cluster-light-data',
193
+ size: CLUSTER_LIGHT_DATA_BYTES,
196
194
  usage: STORAGE_BUFFER | BUFFER_COPY_DST,
197
195
  });
198
- const pointLightsBuffer = device.createBuffer({
199
- label: 'stage-readback.point-lights',
200
- size: LIGHT_ARRAY_HEADER_BYTES + LIGHT_ARRAY_MAX_SLOTS * POINT_LIGHT_STD430_BYTES,
201
- usage: STORAGE_BUFFER | BUFFER_COPY_DST,
196
+ const clusterUniformBuffer = device.createBuffer({
197
+ label: 'stage-readback.cluster-uniform',
198
+ size: CLUSTER_UNIFORM_BYTES,
199
+ usage: 0x0040 | BUFFER_COPY_DST,
202
200
  });
203
201
  const injectReadback = device.createBuffer({
204
202
  label: 'stage-readback.inject',
@@ -218,8 +216,8 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
218
216
  if (
219
217
  !viewBuffer.ok ||
220
218
  !paramsBuffer.ok ||
221
- !spotLightsBuffer.ok ||
222
- !pointLightsBuffer.ok ||
219
+ !lightDataBuffer.ok ||
220
+ !clusterUniformBuffer.ok ||
223
221
  !injectReadback.ok ||
224
222
  !temporalReadback.ok ||
225
223
  !integrateReadback.ok
@@ -245,19 +243,22 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
245
243
  paramsData.set([0, -1, 0, 0], 20);
246
244
  paramsData.set([1, 1, 1, 0], 24);
247
245
  paramsData.set([2, 0, 0, 0], 28);
246
+ const lightData = new Uint8Array(CLUSTER_LIGHT_DATA_BYTES);
247
+ const clusterUniformData = new Float32Array(8);
248
+ const clusterGrid = new Uint32Array(clusterUniformData.buffer);
249
+ clusterGrid[0] = 1;
250
+ clusterGrid[1] = 1;
251
+ clusterGrid[2] = 1;
252
+ clusterGrid[3] = 1;
248
253
  const viewWrite = device.queue.writeBuffer(viewBuffer.value, 0, viewData);
249
254
  const paramsWrite = device.queue.writeBuffer(paramsBuffer.value, 0, paramsData);
250
- const spotLightsWrite = device.queue.writeBuffer(
251
- spotLightsBuffer.value,
252
- 0,
253
- new Uint32Array([0, 0, 0, 0]),
254
- );
255
- const pointLightsWrite = device.queue.writeBuffer(
256
- pointLightsBuffer.value,
255
+ const lightDataWrite = device.queue.writeBuffer(lightDataBuffer.value, 0, lightData);
256
+ const clusterUniformWrite = device.queue.writeBuffer(
257
+ clusterUniformBuffer.value,
257
258
  0,
258
- new Uint32Array([0, 0, 0, 0]),
259
+ clusterUniformData,
259
260
  );
260
- if (!viewWrite.ok || !paramsWrite.ok || !spotLightsWrite.ok || !pointLightsWrite.ok)
261
+ if (!viewWrite.ok || !paramsWrite.ok || !lightDataWrite.ok || !clusterUniformWrite.ok)
261
262
  throw new Error('Dawn volume stage readback unavailable: uniform upload failed');
262
263
  const densityData = new Uint8Array((BYTES_PER_PIXEL / 2) * SIZE * SIZE * SIZE);
263
264
  densityData.fill(255);
@@ -291,6 +292,7 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
291
292
  storageTexture: { access: 'write-only', format: 'rgba8unorm', viewDimension: '2d-array' },
292
293
  },
293
294
  { binding: 7, visibility: 4, buffer: { type: 'read-only-storage' } },
295
+ { binding: 8, visibility: 4, buffer: { type: 'uniform' } },
294
296
  ],
295
297
  });
296
298
  if (!layout.ok) throw layout.error;
@@ -354,7 +356,7 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
354
356
  storageTexture: { access: 'write-only', format: 'rgba16float', viewDimension: '2d' },
355
357
  },
356
358
  { binding: 10, visibility: 4, buffer: { type: 'read-only-storage' } },
357
- { binding: 11, visibility: 4, buffer: { type: 'read-only-storage' } },
359
+ { binding: 11, visibility: 4, buffer: { type: 'uniform' } },
358
360
  { binding: 12, visibility: 4, texture: { sampleType: 'float', viewDimension: '2d' } },
359
361
  { binding: 13, visibility: 4, sampler: { type: 'filtering' } },
360
362
  ],
@@ -393,7 +395,8 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
393
395
  { binding: 4, resource: { kind: 'sampler', value: comparisonSampler.value } },
394
396
  { binding: 5, resource: { kind: 'buffer', value: { buffer: paramsBuffer.value } } },
395
397
  { binding: 6, resource: { kind: 'textureView', value: outputView } },
396
- { binding: 7, resource: { kind: 'buffer', value: { buffer: spotLightsBuffer.value } } },
398
+ { binding: 7, resource: { kind: 'buffer', value: { buffer: lightDataBuffer.value } } },
399
+ { binding: 8, resource: { kind: 'buffer', value: { buffer: clusterUniformBuffer.value } } },
397
400
  ],
398
401
  });
399
402
  if (!bindings.ok) throw bindings.error;
@@ -420,8 +423,8 @@ async function runInjectReadback(sceneDepthClear = 1): Promise<VolumeStageReadba
420
423
  { binding: 7, resource: { kind: 'sampler', value: densitySampler.value } },
421
424
  { binding: 8, resource: { kind: 'textureView', value: historyView } },
422
425
  { binding: 9, resource: { kind: 'textureView', value: temporalView } },
423
- { binding: 10, resource: { kind: 'buffer', value: { buffer: spotLightsBuffer.value } } },
424
- { binding: 11, resource: { kind: 'buffer', value: { buffer: pointLightsBuffer.value } } },
426
+ { binding: 10, resource: { kind: 'buffer', value: { buffer: lightDataBuffer.value } } },
427
+ { binding: 11, resource: { kind: 'buffer', value: { buffer: clusterUniformBuffer.value } } },
425
428
  { binding: 12, resource: { kind: 'textureView', value: projectorView } },
426
429
  { binding: 13, resource: { kind: 'sampler', value: densitySampler.value } },
427
430
  ],
@@ -10,6 +10,19 @@ import type {
10
10
  /** Optional host hooks for recorder and device-lifecycle capabilities. */
11
11
  export interface RhiBackendInstrumentation {
12
12
  readonly resolveSurfaceDevice?: (device: RhiDevice) => Result<RhiDevice, RhiError>;
13
+ /**
14
+ * Optional deterministic submit fault seam for backend-owned integration
15
+ * fixtures. The callback runs after `finish()` and before the queue submit;
16
+ * returning an error aborts the frame transaction without changing the
17
+ * production queue contract.
18
+ */
19
+ readonly beforeSubmit?: (device: RhiDevice) => RhiError | undefined;
20
+ /**
21
+ * Optional device-loss projection for deterministic recovery fixtures. A
22
+ * backend may compose this promise with its native `device.lost` promise;
23
+ * normal production packs leave it undefined and use the native signal.
24
+ */
25
+ readonly deviceLost?: (device: RhiDevice) => RhiDevice['lost'];
13
26
  readonly onFrameBoundary?: () => void;
14
27
  readonly onDeviceLost?: () => void;
15
28
  }
@@ -0,0 +1,80 @@
1
+ import type { RhiAdapter, RhiDevice } from '@forgeax/engine-rhi';
2
+ import { EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES } from '../prepare/extended-lighting/resources';
3
+
4
+ const COMPRESSION_FEATURES: GPUFeatureName[] = [
5
+ 'texture-compression-bc',
6
+ 'texture-compression-etc2',
7
+ 'texture-compression-astc',
8
+ ];
9
+
10
+ // The Standard transmission fragment ABI needs 17 sampled textures across its
11
+ // material, IBL, clustered-light, and SSAO bindings. Ordinary Standard draws
12
+ // use the non-transmission variant and remain valid on the WebGPU minimum of
13
+ // 16. The extended-lighting topology reserves 20, so a device descriptor must
14
+ // request that larger limit when the adapter can provide it; adapter limits are
15
+ // not automatically inherited by requestDevice().
16
+ export const STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES = 17;
17
+
18
+ export interface DeviceFeatureAdmissionOptions {
19
+ readonly gpuPassTiming?: object | undefined;
20
+ readonly captureGpuTimings?: boolean | undefined;
21
+ }
22
+
23
+ export interface DeviceFeatureAdmission {
24
+ readonly requiredFeatures: GPUFeatureName[];
25
+ readonly requiredLimits?: {
26
+ readonly maxSampledTexturesPerShaderStage: number;
27
+ };
28
+ }
29
+
30
+ type AdapterFeatureProbe = Pick<RhiAdapter, 'features'> & Partial<Pick<RhiAdapter, 'limits'>>;
31
+
32
+ export function deriveDeviceFeatureAdmission(
33
+ adapter: AdapterFeatureProbe,
34
+ options?: DeviceFeatureAdmissionOptions,
35
+ ): DeviceFeatureAdmission {
36
+ const requiredFeatures = COMPRESSION_FEATURES.filter((feature) => adapter.features.has(feature));
37
+ const timestampRequested =
38
+ options?.gpuPassTiming !== undefined || options?.captureGpuTimings === true;
39
+ if (timestampRequested && adapter.features.has('timestamp-query')) {
40
+ requiredFeatures.push('timestamp-query');
41
+ }
42
+ const adapterSampledTextureLimit = adapter.limits?.maxSampledTexturesPerShaderStage ?? 0;
43
+ const requiredSampledTextureLimit =
44
+ adapterSampledTextureLimit >= EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
45
+ ? EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
46
+ : adapterSampledTextureLimit >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
47
+ ? STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
48
+ : undefined;
49
+ return {
50
+ requiredFeatures,
51
+ ...(requiredSampledTextureLimit === undefined
52
+ ? {}
53
+ : {
54
+ requiredLimits: {
55
+ maxSampledTexturesPerShaderStage: requiredSampledTextureLimit,
56
+ },
57
+ }),
58
+ };
59
+ }
60
+
61
+ export function deviceOptionsForAdapter(
62
+ adapter: AdapterFeatureProbe,
63
+ options?: DeviceFeatureAdmissionOptions,
64
+ ): DeviceFeatureAdmission | undefined {
65
+ const admission = deriveDeviceFeatureAdmission(adapter, options);
66
+ return admission.requiredFeatures.length === 0 && admission.requiredLimits === undefined
67
+ ? undefined
68
+ : admission;
69
+ }
70
+
71
+ export function isTimestampQueryAdmitted(
72
+ caps: Pick<RhiDevice['caps'], 'timestampQuery' | 'timestampPeriodNanoseconds'>,
73
+ ): boolean {
74
+ return (
75
+ caps.timestampQuery === true &&
76
+ caps.timestampPeriodNanoseconds !== null &&
77
+ Number.isFinite(caps.timestampPeriodNanoseconds) &&
78
+ caps.timestampPeriodNanoseconds > 0
79
+ );
80
+ }
@@ -0,0 +1,122 @@
1
+ import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
2
+ import type {
3
+ DirectionalShadowInspection,
4
+ DirectionalShadowInspectionError,
5
+ DirectionalShadowProfile,
6
+ } from '../inspection-types';
7
+ import type { DirectionalShadowBackendAdmission } from '../render-pipeline';
8
+
9
+ export interface DirectionalShadowInspectionSource {
10
+ readonly requested: DirectionalShadowProfile;
11
+ readonly candidate: 'accepted' | 'failed';
12
+ readonly lastKnownGood?: DirectionalShadowProfile | undefined;
13
+ readonly cascadeCount: number;
14
+ readonly mapSize: number;
15
+ readonly atlasBytes: number;
16
+ readonly writerPasses: number;
17
+ readonly shadowAngularRadius?: number | undefined;
18
+ readonly maxPenumbraTexels?: number | undefined;
19
+ readonly deviceGeneration: number;
20
+ readonly graphGeneration: number;
21
+ readonly shadowReady: boolean;
22
+ readonly error?: DirectionalShadowInspectionError | undefined;
23
+ }
24
+
25
+ export interface DirectionalShadowInspectionInput {
26
+ readonly admission: DirectionalShadowBackendAdmission;
27
+ readonly cascadeCount: number;
28
+ readonly mapSize: number;
29
+ readonly atlasBytes: number;
30
+ readonly writerPasses: number;
31
+ readonly blockerTaps: number;
32
+ readonly filterTapUpperBound: number;
33
+ readonly seamTapUpperBound: number;
34
+ readonly shadowAngularRadius?: number | undefined;
35
+ readonly maxPenumbraTexels?: number | undefined;
36
+ readonly deviceGeneration: number;
37
+ readonly graphGeneration: number;
38
+ readonly error?: DirectionalShadowInspectionError | undefined;
39
+ }
40
+
41
+ function deepFreeze<T>(value: T, seen = new WeakSet<object>()): T {
42
+ if (value === null || typeof value !== 'object') return value;
43
+ const object = value as object;
44
+ if (seen.has(object)) return value;
45
+ seen.add(object);
46
+ for (const child of Object.values(object)) deepFreeze(child, seen);
47
+ return Object.freeze(value);
48
+ }
49
+
50
+ /** Project the single bounded Directional inspection POD; no live handles leak. */
51
+ export function projectDirectionalShadowInspection(
52
+ input: DirectionalShadowInspectionInput,
53
+ ): DirectionalShadowInspection {
54
+ const projection: DirectionalShadowInspection = {
55
+ requested: input.admission.requested,
56
+ effective: input.admission.effective,
57
+ status: input.admission.status,
58
+ ...(input.admission.fallbackReason === undefined
59
+ ? {}
60
+ : { fallbackReason: input.admission.fallbackReason }),
61
+ lastKnownGood: input.admission.lastKnownGood,
62
+ pixelEvidence: input.admission.pixelEvidence,
63
+ cascadeCount: input.cascadeCount,
64
+ mapSize: input.mapSize,
65
+ atlasBytes: input.atlasBytes,
66
+ writerPasses: input.writerPasses,
67
+ blockerTaps: input.blockerTaps,
68
+ filterTapUpperBound: input.filterTapUpperBound,
69
+ seamTapUpperBound: input.seamTapUpperBound,
70
+ deviceGeneration: input.deviceGeneration,
71
+ graphGeneration: input.graphGeneration,
72
+ shadowAngularRadius: input.shadowAngularRadius,
73
+ maxPenumbraTexels: input.maxPenumbraTexels,
74
+ ...(input.error === undefined ? {} : { error: input.error }),
75
+ };
76
+ return deepFreeze(projection);
77
+ }
78
+
79
+ export function directionalShadowProfileFromQuality(
80
+ quality: DirectionalShadowQuality | undefined,
81
+ ): DirectionalShadowProfile {
82
+ if (quality === undefined) return 'off';
83
+ if (quality.kind === 'pcf') return `pcf${quality.kernel}` as 'pcf1' | 'pcf3' | 'pcf5';
84
+ return quality.preset === 'medium' ? 'pcssMedium' : 'pcssHigh';
85
+ }
86
+
87
+ export function projectDirectionalShadowInspectionSource(
88
+ input: DirectionalShadowInspectionSource,
89
+ ): DirectionalShadowInspection {
90
+ const filter =
91
+ input.requested === 'pcssMedium'
92
+ ? { blockerTaps: 8, filterTapUpperBound: 16, seamTapUpperBound: 48 }
93
+ : input.requested === 'pcssHigh'
94
+ ? { blockerTaps: 16, filterTapUpperBound: 32, seamTapUpperBound: 96 }
95
+ : input.requested === 'pcf5'
96
+ ? { blockerTaps: 0, filterTapUpperBound: 25, seamTapUpperBound: 25 }
97
+ : input.requested === 'pcf3'
98
+ ? { blockerTaps: 0, filterTapUpperBound: 9, seamTapUpperBound: 9 }
99
+ : { blockerTaps: 0, filterTapUpperBound: 1, seamTapUpperBound: 1 };
100
+ const admission: DirectionalShadowBackendAdmission = {
101
+ requested: input.requested,
102
+ effective:
103
+ input.candidate === 'accepted' ? input.requested : (input.lastKnownGood ?? input.requested),
104
+ status: input.candidate === 'accepted' ? 'accepted' : 'rejected',
105
+ ...(input.candidate === 'failed' ? { fallbackReason: 'candidate-failed' as const } : {}),
106
+ lastKnownGood: input.candidate === 'failed' && input.lastKnownGood !== undefined,
107
+ pixelEvidence: input.shadowReady ? 'available' : 'not-available',
108
+ };
109
+ return projectDirectionalShadowInspection({
110
+ admission,
111
+ cascadeCount: input.cascadeCount,
112
+ mapSize: input.mapSize,
113
+ atlasBytes: input.atlasBytes,
114
+ writerPasses: input.writerPasses,
115
+ ...filter,
116
+ shadowAngularRadius: input.shadowAngularRadius,
117
+ maxPenumbraTexels: input.maxPenumbraTexels,
118
+ deviceGeneration: input.deviceGeneration,
119
+ graphGeneration: input.graphGeneration,
120
+ ...(input.error === undefined ? {} : { error: input.error }),
121
+ });
122
+ }