@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
@@ -20,17 +20,41 @@ import {
20
20
  GPU_BUFFER_USAGE_COPY_DST,
21
21
  GPU_BUFFER_USAGE_COPY_SRC,
22
22
  GPU_BUFFER_USAGE_INDIRECT,
23
+ GPU_BUFFER_USAGE_MAP_READ,
23
24
  GPU_BUFFER_USAGE_STORAGE,
24
25
  GPU_BUFFER_USAGE_UNIFORM,
25
26
  } from '../gpu-usage';
26
27
  import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
27
- import type { SubmissionPlan } from './batch-topology';
28
+ import type { GpuDrivenCandidate, SubmissionPlan } from './batch-topology';
28
29
 
29
30
  const COMPUTE_STAGE = 0x4;
30
31
  const WORKGROUP_SIZE = 64;
31
- const CANDIDATE_STRIDE = 28;
32
- const BATCH_STRIDE = 28;
32
+ const LOD_ROW_STRIDE = GPU_SCENE_LAYOUTS.lod.stride;
33
+ const LOD_ROW_CAPACITY = 8;
34
+ const CANDIDATE_STRIDE = 48 + LOD_ROW_CAPACITY * LOD_ROW_STRIDE;
35
+ const BATCH_STRIDE = 32;
33
36
  const VIEW_BYTES = 112;
37
+ // visible + overflow + one bucket per selected LOD + selected/root index work.
38
+ // The two work counters let inspection report geometry reduction from the
39
+ // actual indirect draw ranges instead of treating submitted-instance count as
40
+ // a proxy for vertex/index work.
41
+ const COUNTER_WORDS = 2 + LOD_ROW_CAPACITY + 2;
42
+ const GEOMETRY_WORK_COUNTER_OFFSET = 2 + LOD_ROW_CAPACITY;
43
+ const ROOT_GEOMETRY_WORK_COUNTER_OFFSET = GEOMETRY_WORK_COUNTER_OFFSET + 1;
44
+ const COUNTER_STRIDE = COUNTER_WORDS * 4;
45
+ type GpuDrivenAdmissionCandidate = GpuDrivenCandidate & {
46
+ readonly submitAdmission?: boolean;
47
+ };
48
+
49
+ export type GpuLodLane = 'gpu' | 'cpu';
50
+
51
+ export function selectGpuLodLane(caps: {
52
+ readonly compute: boolean;
53
+ readonly storageBuffer: boolean;
54
+ readonly indirectDrawing: boolean;
55
+ }): GpuLodLane {
56
+ return caps.compute && caps.storageBuffer && caps.indirectDrawing ? 'gpu' : 'cpu';
57
+ }
34
58
 
35
59
  export const GPU_DRIVEN_VIEW_WGSL = /* wgsl */ `
36
60
  struct PrimitiveRecord {
@@ -51,6 +75,17 @@ struct TransformRecord {
51
75
  previousWorld: mat4x4<f32>,
52
76
  };
53
77
 
78
+ struct LodRecord {
79
+ generation: u32,
80
+ level: u32,
81
+ firstIndex: u32,
82
+ indexCount: u32,
83
+ baseVertex: i32,
84
+ screenCoverage: f32,
85
+ hysteresis: f32,
86
+ ready: u32,
87
+ };
88
+
54
89
  struct CandidateRecord {
55
90
  primitiveIndex: u32,
56
91
  generation: u32,
@@ -59,6 +94,15 @@ struct CandidateRecord {
59
94
  batchIndex: u32,
60
95
  visibleBase: u32,
61
96
  visibleCapacity: u32,
97
+ // Admission is separate from selector membership: suppressed candidates
98
+ // still contribute to the all-candidate LOD histogram, but never enter the
99
+ // compacted visible stream.
100
+ submitAdmission: u32,
101
+ projectedHeight: f32,
102
+ previousLevel: u32,
103
+ historyValid: u32,
104
+ lodCount: u32,
105
+ lodRows: array<LodRecord, 8>,
62
106
  };
63
107
 
64
108
  struct InstanceRecord {
@@ -76,6 +120,7 @@ struct BatchRecord {
76
120
  baseVertex: i32,
77
121
  indirectIndex: u32,
78
122
  indexed: u32,
123
+ candidateBase: u32,
79
124
  };
80
125
 
81
126
  struct ViewConstants {
@@ -86,6 +131,34 @@ struct ViewConstants {
86
131
  pad1: u32,
87
132
  };
88
133
 
134
+ fn selectLodLevel(
135
+ projectedHeight: f32,
136
+ previousLevel: u32,
137
+ historyValid: bool,
138
+ rows: array<LodRecord, 8>,
139
+ levelCount: u32,
140
+ ) -> u32 {
141
+ // Row zero is the implicit root and has no transition threshold. Lower
142
+ // levels carry the absolute boundary that introduces the preceding row.
143
+ // This mirrors the CPU selector: height >= rows[1] selects root, height
144
+ // between rows[2] and rows[1] selects level 1, and so on.
145
+ var selected = levelCount - 1u;
146
+ for (var level = 1u; level < levelCount; level += 1u) {
147
+ if (projectedHeight >= rows[level].screenCoverage) {
148
+ selected = level - 1u;
149
+ break;
150
+ }
151
+ }
152
+ if (historyValid && previousLevel < levelCount && selected != previousLevel) {
153
+ var boundary = rows[previousLevel + 1u].screenCoverage;
154
+ if (selected < previousLevel) { boundary = rows[previousLevel].screenCoverage; }
155
+ let band = boundary * rows[previousLevel].hysteresis;
156
+ if (abs(projectedHeight - boundary) <= band) { selected = previousLevel; }
157
+ }
158
+ if (selected >= levelCount || rows[selected].ready == 0u) { return 0u; }
159
+ return selected;
160
+ }
161
+
89
162
  @group(0) @binding(0) var<storage, read> primitives: array<PrimitiveRecord>;
90
163
  @group(0) @binding(1) var<storage, read> instances: array<InstanceRecord>;
91
164
  @group(0) @binding(2) var<storage, read> transforms: array<TransformRecord>;
@@ -96,11 +169,18 @@ struct ViewConstants {
96
169
  @group(0) @binding(7) var<storage, read_write> visibleIndices: array<vec2<u32>>;
97
170
  @group(0) @binding(8) var<storage, read_write> indirectArgs: array<u32>;
98
171
 
99
- fn counterIndex(batchIndex: u32) -> u32 { return batchIndex * 2u; }
100
- fn overflowIndex(batchIndex: u32) -> u32 { return batchIndex * 2u + 1u; }
172
+ fn counterIndex(batchIndex: u32) -> u32 { return batchIndex * ${COUNTER_WORDS}u; }
173
+ fn overflowIndex(batchIndex: u32) -> u32 { return counterIndex(batchIndex) + 1u; }
174
+ fn lodCounterIndex(batchIndex: u32, level: u32) -> u32 {
175
+ return counterIndex(batchIndex) + 2u + level;
176
+ }
101
177
 
102
178
  fn isVisible(primitive: PrimitiveRecord, world: mat4x4<f32>) -> bool {
103
- if ((primitive.flags & 7u) != 7u) { return false; }
179
+ // GPU Scene bounds are optional. A producer that has not published local
180
+ // bounds still belongs in the GPU lane; keep it conservatively visible
181
+ // instead of silently dropping a valid draw.
182
+ if ((primitive.flags & 5u) != 5u) { return false; }
183
+ if ((primitive.flags & 2u) == 0u) { return true; }
104
184
  let localCenter = (primitive.localBoundsMin.xyz + primitive.localBoundsMax.xyz) * 0.5;
105
185
  let localExtent = (primitive.localBoundsMax.xyz - primitive.localBoundsMin.xyz) * 0.5;
106
186
  let worldCenter = (world * vec4<f32>(localCenter, 1.0)).xyz;
@@ -122,6 +202,11 @@ fn resetView(@builtin(global_invocation_id) id: vec3<u32>) {
122
202
  if (batchIndex >= view.batchCount) { return; }
123
203
  atomicStore(&counters[counterIndex(batchIndex)], 0u);
124
204
  atomicStore(&counters[overflowIndex(batchIndex)], 0u);
205
+ for (var level = 0u; level < ${LOD_ROW_CAPACITY}u; level += 1u) {
206
+ atomicStore(&counters[lodCounterIndex(batchIndex, level)], 0u);
207
+ }
208
+ atomicStore(&counters[counterIndex(batchIndex) + ${GEOMETRY_WORK_COUNTER_OFFSET}u], 0u);
209
+ atomicStore(&counters[counterIndex(batchIndex) + ${ROOT_GEOMETRY_WORK_COUNTER_OFFSET}u], 0u);
125
210
  }
126
211
 
127
212
  @compute @workgroup_size(${WORKGROUP_SIZE})
@@ -135,7 +220,28 @@ fn cullView(@builtin(global_invocation_id) id: vec3<u32>) {
135
220
  let instanceIndex = primitive.instanceStart + candidate.instanceOrdinal;
136
221
  let instance = instances[instanceIndex];
137
222
  let world = transforms[primitive.transformIndex].currentWorld * transforms[instance.transformIndex].currentWorld;
223
+ let selectedLod = selectLodLevel(
224
+ candidate.projectedHeight,
225
+ candidate.previousLevel,
226
+ candidate.historyValid != 0u,
227
+ candidate.lodRows,
228
+ candidate.lodCount,
229
+ );
230
+ if (candidate.lodRows[selectedLod].ready == 0u) { return; }
231
+ // Selector counters describe the complete LOD population, including
232
+ // candidates outside the frustum and candidates suppressed by the CPU
233
+ // visibility facet. Submission counters below remain compacted draw facts.
234
+ atomicAdd(&counters[lodCounterIndex(candidate.batchIndex, selectedLod)], 1u);
235
+ if (candidate.submitAdmission == 0u) { return; }
138
236
  if (!isVisible(primitive, world)) { return; }
237
+ atomicAdd(
238
+ &counters[counterIndex(candidate.batchIndex) + ${GEOMETRY_WORK_COUNTER_OFFSET}u],
239
+ candidate.lodRows[selectedLod].indexCount,
240
+ );
241
+ atomicAdd(
242
+ &counters[counterIndex(candidate.batchIndex) + ${ROOT_GEOMETRY_WORK_COUNTER_OFFSET}u],
243
+ candidate.lodRows[0u].indexCount,
244
+ );
139
245
  let localVisible = atomicAdd(&counters[counterIndex(candidate.batchIndex)], 1u);
140
246
  if (localVisible >= candidate.visibleCapacity) {
141
247
  atomicStore(&counters[overflowIndex(candidate.batchIndex)], 1u);
@@ -157,10 +263,13 @@ fn finalizeView(@builtin(global_invocation_id) id: vec3<u32>) {
157
263
  batch.visibleCapacity,
158
264
  );
159
265
  let args = batch.indirectIndex * 5u;
160
- indirectArgs[args] = batch.count;
266
+ let candidate = candidates[batch.candidateBase];
267
+ let selectedLod = selectLodLevel(candidate.projectedHeight, candidate.previousLevel, candidate.historyValid != 0u, candidate.lodRows, candidate.lodCount);
268
+ let lod = candidate.lodRows[selectedLod];
269
+ indirectArgs[args] = select(batch.count, lod.indexCount, candidate.lodCount > 0u);
161
270
  indirectArgs[args + 1u] = visibleCount;
162
- indirectArgs[args + 2u] = batch.first;
163
- indirectArgs[args + 3u] = select(0u, bitcast<u32>(batch.baseVertex), batch.indexed != 0u);
271
+ indirectArgs[args + 2u] = select(batch.first, lod.firstIndex, candidate.lodCount > 0u && batch.indexed != 0u);
272
+ indirectArgs[args + 3u] = select(0u, bitcast<u32>(lod.baseVertex), candidate.lodCount > 0u && batch.indexed != 0u);
164
273
  indirectArgs[args + 4u] = 0u;
165
274
  }
166
275
  `;
@@ -172,6 +281,7 @@ interface ViewBuffers {
172
281
  readonly counters: Buffer;
173
282
  readonly visible: Buffer;
174
283
  readonly indirect: Buffer;
284
+ readonly lodReadback: Buffer;
175
285
  }
176
286
 
177
287
  type MutableViewBuffers = { -readonly [Name in keyof ViewBuffers]: ViewBuffers[Name] };
@@ -192,6 +302,8 @@ export interface GpuDrivenViewInspection {
192
302
  readonly batchCount: number;
193
303
  readonly visibleCapacity: number;
194
304
  readonly candidateCapacity: number;
305
+ /** Allocated visible-index capacity; distinct from logical candidates. */
306
+ readonly visibleBufferCapacity: number;
195
307
  readonly batchCapacity: number;
196
308
  readonly indirectCapacity: number;
197
309
  readonly updateCount: number;
@@ -204,10 +316,69 @@ export interface GpuDrivenViewInspection {
204
316
  readonly resourceGeneration: number;
205
317
  }
206
318
 
319
+ export interface GpuDrivenViewBufferCapacities {
320
+ readonly candidate: number;
321
+ readonly visible: number;
322
+ readonly batch: number;
323
+ readonly indirect: number;
324
+ }
325
+
326
+ export interface GpuDrivenLodSelectionInspection {
327
+ readonly candidateCount: number;
328
+ readonly batchCount: number;
329
+ readonly indirectDrawCount: number;
330
+ readonly visible: number;
331
+ readonly occluded: number;
332
+ readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
333
+ readonly batches: readonly {
334
+ readonly batchId: number;
335
+ readonly candidateCount: number;
336
+ readonly visible: number;
337
+ readonly occluded: number;
338
+ readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
339
+ }[];
340
+ readonly worldSelections?: readonly {
341
+ readonly worldKey: number;
342
+ readonly primitiveSlot: number;
343
+ readonly slotGeneration: number;
344
+ readonly candidateCount: number;
345
+ readonly visible: number;
346
+ readonly occluded: number;
347
+ readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
348
+ }[];
349
+ readonly geometryWork: number;
350
+ readonly rootGeometryWork: number;
351
+ }
352
+
207
353
  function nextCapacity(required: number): number {
208
354
  return 2 ** Math.ceil(Math.log2(Math.max(1, required)));
209
355
  }
210
356
 
357
+ /** Selector batches with no admitted candidates never produce a raster draw. */
358
+ function admittedRasterBatchCount(plan: SubmissionPlan): number {
359
+ return plan.batches.reduce((count, batch) => count + (batch.visibleCapacity > 0 ? 1 : 0), 0);
360
+ }
361
+
362
+ /**
363
+ * Derive each GPU-driven allocation from its own logical capacity domain.
364
+ * Candidate records are dense; visible indices use the sparse, 64-entry
365
+ * aligned ranges assigned by BatchTopology. Keeping these values separate is
366
+ * what makes a large LOD candidate set bounded instead of multiplying the
367
+ * candidate record and inline LOD-row buffers by the visible address space.
368
+ */
369
+ export function deriveGpuDrivenViewBufferCapacities(
370
+ plan: Pick<SubmissionPlan, 'candidateCount' | 'visibleCapacity' | 'batches'>,
371
+ ): GpuDrivenViewBufferCapacities {
372
+ return Object.freeze({
373
+ candidate: nextCapacity(plan.candidateCount),
374
+ visible: nextCapacity(plan.visibleCapacity),
375
+ batch: nextCapacity(plan.batches.length),
376
+ indirect: nextCapacity(
377
+ plan.batches.reduce((maximum, batch) => Math.max(maximum, batch.batchId + 1), 0),
378
+ ),
379
+ });
380
+ }
381
+
211
382
  function bufferBinding(buffer: Buffer): {
212
383
  readonly kind: 'buffer';
213
384
  readonly value: { buffer: Buffer };
@@ -220,6 +391,7 @@ export class GpuDrivenView {
220
391
  private supersededBuffers: ViewBuffers[] = [];
221
392
  private bindGroup: BindGroup | undefined;
222
393
  private candidateCapacity = 0;
394
+ private visibleBufferCapacity = 0;
223
395
  private batchCapacity = 0;
224
396
  private indirectCapacity = 0;
225
397
  private plan: SubmissionPlan | undefined;
@@ -236,6 +408,8 @@ export class GpuDrivenView {
236
408
  private bindGroupCreates = 0;
237
409
  private bufferRebuilds = 0;
238
410
  private resourceGeneration = 0;
411
+ private telemetryPending = false;
412
+ private lodSelection: GpuDrivenLodSelectionInspection | undefined;
239
413
 
240
414
  private constructor(
241
415
  private readonly device: RhiDevice,
@@ -314,22 +488,34 @@ export class GpuDrivenView {
314
488
  this.batchUploadBytes = 0;
315
489
  this.viewConstantsUploadBytes = 0;
316
490
  this.bindGroupCreates = 0;
491
+ // The readback copy is part of the accepted graph and therefore executes
492
+ // every submitted frame, not only when that graph is (re)compiled. Keep
493
+ // the observation latch armed across per-frame view updates; clearing it
494
+ // here would make a cached graph silently discard all later LOD samples.
495
+ this.telemetryPending = true;
496
+ this.lodSelection = undefined;
317
497
  const topologyChanged = this.plan !== plan;
318
498
  let buffersRebuilt = false;
319
499
  if (topologyChanged || this.buffers === undefined) {
320
- const requiredCandidates = Math.max(1, plan.candidateCount, plan.visibleCapacity);
321
- const requiredBatches = Math.max(1, plan.batches.length);
322
- const requiredIndirect = Math.max(
323
- 1,
324
- plan.batches.reduce((maximum, batch) => Math.max(maximum, batch.batchId + 1), 0),
325
- );
500
+ // Candidate records and compacted visible indices are different
501
+ // capacity domains. LOD singleton batches align each visible segment
502
+ // to 64 entries, so plan.visibleCapacity can be much larger than the
503
+ // number of logical candidates. Never let that sparse address space
504
+ // inflate candidate/LOD-row storage allocations.
505
+ const capacities = deriveGpuDrivenViewBufferCapacities(plan);
326
506
  if (
327
507
  this.buffers === undefined ||
328
- requiredCandidates > this.candidateCapacity ||
329
- requiredBatches > this.batchCapacity ||
330
- requiredIndirect > this.indirectCapacity
508
+ capacities.candidate > this.candidateCapacity ||
509
+ capacities.visible > this.visibleBufferCapacity ||
510
+ capacities.batch > this.batchCapacity ||
511
+ capacities.indirect > this.indirectCapacity
331
512
  ) {
332
- const rebuilt = this.rebuildBuffers(requiredCandidates, requiredBatches, requiredIndirect);
513
+ const rebuilt = this.rebuildBuffers(
514
+ capacities.candidate,
515
+ capacities.visible,
516
+ capacities.batch,
517
+ capacities.indirect,
518
+ );
333
519
  if (!rebuilt.ok) return rebuilt;
334
520
  buffersRebuilt = true;
335
521
  }
@@ -353,7 +539,9 @@ export class GpuDrivenView {
353
539
  batches.setInt32(batchOffset + 16, batch.key.baseVertex, true);
354
540
  batches.setUint32(batchOffset + 20, batch.batchId, true);
355
541
  batches.setUint32(batchOffset + 24, batch.key.drawKind === 'indexed' ? 1 : 0, true);
542
+ batches.setUint32(batchOffset + 28, candidateIndex, true);
356
543
  for (const candidate of batch.candidates) {
544
+ const admissionCandidate = candidate as GpuDrivenAdmissionCandidate;
357
545
  const candidateOffset = candidateIndex * CANDIDATE_STRIDE;
358
546
  candidates.setUint32(candidateOffset, candidate.primitiveIndex, true);
359
547
  candidates.setUint32(candidateOffset + 4, candidate.generation, true);
@@ -362,6 +550,40 @@ export class GpuDrivenView {
362
550
  candidates.setUint32(candidateOffset + 16, batchIndex, true);
363
551
  candidates.setUint32(candidateOffset + 20, batch.visibleBase, true);
364
552
  candidates.setUint32(candidateOffset + 24, batch.visibleCapacity, true);
553
+ const coverages = candidate.lodCoverages ?? [1];
554
+ const ranges = [
555
+ { first: batch.key.first, count: batch.key.count, baseVertex: batch.key.baseVertex },
556
+ ...(candidate.lodRanges ?? []),
557
+ ];
558
+ const lodCount = Math.min(LOD_ROW_CAPACITY, Math.max(1, coverages.length, ranges.length));
559
+ candidates.setUint32(
560
+ candidateOffset + 28,
561
+ admissionCandidate.submitAdmission === false ? 0 : 1,
562
+ true,
563
+ );
564
+ candidates.setFloat32(
565
+ candidateOffset + 32,
566
+ candidate.projectedHeight ?? Number.NaN,
567
+ true,
568
+ );
569
+ candidates.setUint32(candidateOffset + 36, 0, true);
570
+ candidates.setUint32(candidateOffset + 40, 0, true);
571
+ candidates.setUint32(candidateOffset + 44, lodCount, true);
572
+ for (let level = 0; level < lodCount; level += 1) {
573
+ const rowOffset = candidateOffset + 48 + level * LOD_ROW_STRIDE;
574
+ candidates.setUint32(rowOffset, candidate.generation, true);
575
+ candidates.setUint32(rowOffset + 4, level, true);
576
+ candidates.setUint32(rowOffset + 8, ranges[level]?.first ?? batch.key.first, true);
577
+ candidates.setUint32(rowOffset + 12, ranges[level]?.count ?? batch.key.count, true);
578
+ candidates.setInt32(
579
+ rowOffset + 16,
580
+ ranges[level]?.baseVertex ?? batch.key.baseVertex,
581
+ true,
582
+ );
583
+ candidates.setFloat32(rowOffset + 20, coverages[level] ?? 0, true);
584
+ candidates.setFloat32(rowOffset + 24, candidate.lodHysteresis ?? 0.08, true);
585
+ candidates.setUint32(rowOffset + 28, 1, true);
586
+ }
365
587
  candidateIndex += 1;
366
588
  }
367
589
  }
@@ -434,6 +656,7 @@ export class GpuDrivenView {
434
656
  batchCount: this.plan?.batches.length ?? 0,
435
657
  visibleCapacity: this.plan?.visibleCapacity ?? 0,
436
658
  candidateCapacity: this.candidateCapacity,
659
+ visibleBufferCapacity: this.visibleBufferCapacity,
437
660
  batchCapacity: this.batchCapacity,
438
661
  indirectCapacity: this.indirectCapacity,
439
662
  updateCount: this.updateCount,
@@ -463,6 +686,88 @@ export class GpuDrivenView {
463
686
  return 4;
464
687
  }
465
688
 
689
+ /** Read selected levels written by the GPU cull pass for the last submit. */
690
+ async readLodSelection(): Promise<GpuDrivenLodSelectionInspection | undefined> {
691
+ const buffers = this.buffers;
692
+ const plan = this.plan;
693
+ if (buffers === undefined || plan === undefined || !this.telemetryPending) {
694
+ return this.lodSelection;
695
+ }
696
+ this.telemetryPending = false;
697
+ const mapped = await buffers.lodReadback.mapAsync(GPU_BUFFER_USAGE_MAP_READ);
698
+ if (!mapped.ok) return undefined;
699
+ const range = mapped.value.getMappedRange();
700
+ if (!range.ok) {
701
+ mapped.value.unmap();
702
+ return undefined;
703
+ }
704
+ try {
705
+ const values = new DataView(range.value);
706
+ let visible = 0;
707
+ let geometryWork = 0;
708
+ let rootGeometryWork = 0;
709
+ const histogram = new Map<number, number>();
710
+ for (let batchIndex = 0; batchIndex < plan.batches.length; batchIndex += 1) {
711
+ const offset = batchIndex * COUNTER_STRIDE;
712
+ visible += values.getUint32(offset, true);
713
+ geometryWork += values.getUint32(offset + GEOMETRY_WORK_COUNTER_OFFSET * 4, true);
714
+ rootGeometryWork += values.getUint32(offset + ROOT_GEOMETRY_WORK_COUNTER_OFFSET * 4, true);
715
+ for (let level = 0; level < LOD_ROW_CAPACITY; level += 1) {
716
+ const count = values.getUint32(offset + (2 + level) * 4, true);
717
+ if (count > 0) histogram.set(level, (histogram.get(level) ?? 0) + count);
718
+ }
719
+ }
720
+ const candidateCount = plan.candidateCount;
721
+ this.lodSelection = Object.freeze({
722
+ candidateCount,
723
+ batchCount: plan.batches.length,
724
+ // Selector-only batches remain in the plan/readback but the production
725
+ // raster skips them. Report the actual nonzero-admission command count
726
+ // so a benchmark cannot mistake selector batches for raster draws.
727
+ indirectDrawCount: admittedRasterBatchCount(plan),
728
+ visible,
729
+ // A LOD histogram counts candidates that reached the selector, while
730
+ // `visible` is the actual compacted submission count. Occlusion is the
731
+ // difference between those candidate/visibility totals; using the
732
+ // histogram here would mislabel a visible candidate dropped only by an
733
+ // indirect-capacity overflow as occluded.
734
+ occluded: Math.max(0, candidateCount - visible),
735
+ lodHistogram: Object.freeze(
736
+ [...histogram.entries()]
737
+ .sort(([left], [right]) => left - right)
738
+ .map(([level, count]) => Object.freeze({ level, count })),
739
+ ),
740
+ batches: Object.freeze(
741
+ plan.batches.map((batch, batchIndex) => {
742
+ const offset = batchIndex * COUNTER_STRIDE;
743
+ const batchHistogram = new Map<number, number>();
744
+ for (let level = 0; level < LOD_ROW_CAPACITY; level += 1) {
745
+ const count = values.getUint32(offset + (2 + level) * 4, true);
746
+ if (count > 0) batchHistogram.set(level, count);
747
+ }
748
+ const batchVisible = values.getUint32(offset, true);
749
+ return Object.freeze({
750
+ batchId: batch.batchId,
751
+ candidateCount: batch.candidates.length,
752
+ visible: batchVisible,
753
+ occluded: Math.max(0, batch.candidates.length - batchVisible),
754
+ lodHistogram: Object.freeze(
755
+ [...batchHistogram.entries()]
756
+ .sort(([left], [right]) => left - right)
757
+ .map(([level, count]) => Object.freeze({ level, count })),
758
+ ),
759
+ });
760
+ }),
761
+ ),
762
+ geometryWork,
763
+ rootGeometryWork,
764
+ });
765
+ return this.lodSelection;
766
+ } finally {
767
+ mapped.value.unmap();
768
+ }
769
+ }
770
+
466
771
  addPasses<FrameCtx extends RenderGraphFrame>(
467
772
  builder: RenderGraphBuilder<FrameCtx>,
468
773
  ): Result<GpuDrivenViewGraphResources, RenderGraphError> {
@@ -538,14 +843,21 @@ export class GpuDrivenView {
538
843
  const counters = importBuffer(
539
844
  'gpu-driven.counters',
540
845
  buffers.counters,
541
- this.batchCapacity * 8,
846
+ this.batchCapacity * COUNTER_STRIDE,
542
847
  GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC,
543
848
  );
544
849
  if (!counters.ok) return counters;
850
+ const lodReadback = importBuffer(
851
+ 'gpu-driven.lod-selection-readback',
852
+ buffers.lodReadback,
853
+ this.batchCapacity * COUNTER_STRIDE,
854
+ GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST,
855
+ );
856
+ if (!lodReadback.ok) return lodReadback;
545
857
  const visible = importBuffer(
546
858
  'gpu-driven.visible',
547
859
  buffers.visible,
548
- this.candidateCapacity * 8,
860
+ this.visibleBufferCapacity * 8,
549
861
  GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC,
550
862
  );
551
863
  if (!visible.ok) return visible;
@@ -599,6 +911,21 @@ export class GpuDrivenView {
599
911
  },
600
912
  });
601
913
  if (!finalize.ok) return finalize;
914
+ const readback = builder.addCopyPass('gpu-driven.lod-selection-readback', {
915
+ accesses: [
916
+ { resource: counters.value, usage: 'copy-src' },
917
+ { resource: lodReadback.value, usage: 'copy-dst' },
918
+ ],
919
+ encode: ({ encoder, resources }) => {
920
+ encoder.copyBufferToBuffer(
921
+ resources.buffer(counters.value).unwrap(),
922
+ resources.buffer(lodReadback.value).unwrap(),
923
+ this.batchCapacity * COUNTER_STRIDE,
924
+ );
925
+ },
926
+ });
927
+ if (!readback.ok) return readback;
928
+ this.telemetryPending = true;
602
929
  return ok({
603
930
  visible: visible.value,
604
931
  primitive: primitive.value,
@@ -619,6 +946,7 @@ export class GpuDrivenView {
619
946
  this.buffers = undefined;
620
947
  this.bindGroup = undefined;
621
948
  this.plan = undefined;
949
+ this.visibleBufferCapacity = 0;
622
950
  this.scenePrimitive = undefined;
623
951
  this.sceneInstance = undefined;
624
952
  this.sceneTransform = undefined;
@@ -643,10 +971,12 @@ export class GpuDrivenView {
643
971
 
644
972
  private rebuildBuffers(
645
973
  candidateCapacity: number,
974
+ visibleCapacity: number,
646
975
  batchCapacity: number,
647
976
  indirectCapacity: number,
648
977
  ): Result<void, RhiError> {
649
978
  const nextCandidate = nextCapacity(candidateCapacity);
979
+ const nextVisible = nextCapacity(visibleCapacity);
650
980
  const nextBatch = nextCapacity(batchCapacity);
651
981
  const nextIndirect = nextCapacity(indirectCapacity);
652
982
  const created: Partial<MutableViewBuffers> = {};
@@ -666,14 +996,64 @@ export class GpuDrivenView {
666
996
  return ok(undefined);
667
997
  };
668
998
  const storage = GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC;
669
- for (const result of [
670
- allocate('candidates', nextCandidate * CANDIDATE_STRIDE, storage | GPU_BUFFER_USAGE_COPY_DST),
671
- allocate('batches', nextBatch * BATCH_STRIDE, storage | GPU_BUFFER_USAGE_COPY_DST),
672
- allocate('view', VIEW_BYTES, GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST),
673
- allocate('counters', nextBatch * 8, storage),
674
- allocate('visible', nextCandidate * 8, storage),
675
- allocate('indirect', nextIndirect * 20, storage | GPU_BUFFER_USAGE_INDIRECT),
676
- ]) {
999
+ const requests: readonly {
1000
+ readonly name: keyof ViewBuffers;
1001
+ readonly size: number;
1002
+ readonly usage: number;
1003
+ readonly storage: boolean;
1004
+ }[] = [
1005
+ {
1006
+ name: 'candidates',
1007
+ size: nextCandidate * CANDIDATE_STRIDE,
1008
+ usage: storage | GPU_BUFFER_USAGE_COPY_DST,
1009
+ storage: true,
1010
+ },
1011
+ {
1012
+ name: 'batches',
1013
+ size: nextBatch * BATCH_STRIDE,
1014
+ usage: storage | GPU_BUFFER_USAGE_COPY_DST,
1015
+ storage: true,
1016
+ },
1017
+ {
1018
+ name: 'view',
1019
+ size: VIEW_BYTES,
1020
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
1021
+ storage: false,
1022
+ },
1023
+ { name: 'counters', size: nextBatch * COUNTER_STRIDE, usage: storage, storage: true },
1024
+ { name: 'visible', size: nextVisible * 8, usage: storage, storage: true },
1025
+ {
1026
+ name: 'indirect',
1027
+ size: nextIndirect * 20,
1028
+ usage: storage | GPU_BUFFER_USAGE_INDIRECT,
1029
+ storage: true,
1030
+ },
1031
+ {
1032
+ name: 'lodReadback',
1033
+ size: nextBatch * COUNTER_STRIDE,
1034
+ usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST,
1035
+ storage: false,
1036
+ },
1037
+ ];
1038
+ const maxBufferSize = Number(this.device.limits.maxBufferSize);
1039
+ const maxStorageBufferBindingSize = Number(this.device.limits.maxStorageBufferBindingSize);
1040
+ for (const request of requests) {
1041
+ const limit = request.storage ? maxStorageBufferBindingSize : maxBufferSize;
1042
+ if (!Number.isFinite(limit) || limit <= 0 || request.size <= limit) continue;
1043
+ return err(
1044
+ new RhiError({
1045
+ code: 'limit-exceeded',
1046
+ expected: `GPU-driven ${request.name} buffer (${request.size} B) fits within ${request.storage ? 'device.limits.maxStorageBufferBindingSize' : 'device.limits.maxBufferSize'} (${limit} B)`,
1047
+ hint: 'reduce the visible/candidate workload or split the GPU-driven buffer plan before retrying',
1048
+ detail: {
1049
+ maxStorageBufferBindingSize: limit,
1050
+ requestedBytes: request.size,
1051
+ },
1052
+ }),
1053
+ );
1054
+ }
1055
+ for (const request of requests) {
1056
+ const result = allocate(request.name, request.size, request.usage);
677
1057
  if (!result.ok) {
678
1058
  for (const buffer of Object.values(created)) this.device.destroyBuffer(buffer);
679
1059
  return result;
@@ -682,6 +1062,7 @@ export class GpuDrivenView {
682
1062
  if (this.buffers !== undefined) this.supersededBuffers.push(this.buffers);
683
1063
  this.buffers = created as ViewBuffers;
684
1064
  this.candidateCapacity = nextCandidate;
1065
+ this.visibleBufferCapacity = nextVisible;
685
1066
  this.batchCapacity = nextBatch;
686
1067
  this.indirectCapacity = nextIndirect;
687
1068
  this.bufferRebuilds += 1;
@@ -701,3 +1082,4 @@ export class GpuDrivenView {
701
1082
  void this.device.queue.onSubmittedWorkDone().then(release, release);
702
1083
  }
703
1084
  }
1085
+ export { decideVisibility } from '../scene/visibility/occlusion-confidence';
@@ -63,6 +63,19 @@ export function gpuSceneWgsl(layout: GpuSceneTableLayout): string {
63
63
  }
64
64
 
65
65
  export const GPU_SCENE_SCHEMAS = Object.freeze({
66
+ lod: {
67
+ name: 'GpuSceneLod',
68
+ fields: [
69
+ { name: 'generation', type: 'u32' },
70
+ { name: 'level', type: 'u32' },
71
+ { name: 'firstIndex', type: 'u32' },
72
+ { name: 'indexCount', type: 'u32' },
73
+ { name: 'baseVertex', type: 'i32' },
74
+ { name: 'screenCoverage', type: 'f32' },
75
+ { name: 'hysteresis', type: 'f32' },
76
+ { name: 'ready', type: 'u32' },
77
+ ],
78
+ },
66
79
  primitive: {
67
80
  name: 'GpuScenePrimitive',
68
81
  fields: [