@forgeax/engine-render 0.1.21 → 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 (426) hide show
  1. package/README.md +150 -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.map +1 -1
  5. package/dist/assembly/factory.d.ts +14 -6
  6. package/dist/assembly/factory.d.ts.map +1 -1
  7. package/dist/assembly/host-contract.d.ts +10 -0
  8. package/dist/assembly/host-contract.d.ts.map +1 -1
  9. package/dist/authoring.mjs +1 -1
  10. package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
  11. package/dist/chunk-4L3P6DT5.mjs.map +1 -0
  12. package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
  13. package/dist/chunk-4NPUASZR.mjs.map +1 -0
  14. package/dist/chunk-DIYW3M5D.mjs +5085 -0
  15. package/dist/chunk-DIYW3M5D.mjs.map +1 -0
  16. package/dist/chunk-FBC4R44J.mjs +1003 -0
  17. package/dist/chunk-FBC4R44J.mjs.map +1 -0
  18. package/dist/{chunk-FS2KNUQH.mjs → chunk-FF7K3FSO.mjs} +253 -489
  19. package/dist/chunk-FF7K3FSO.mjs.map +1 -0
  20. package/dist/chunk-HZUPSYW7.mjs +1104 -0
  21. package/dist/chunk-HZUPSYW7.mjs.map +1 -0
  22. package/dist/{chunk-6OPXDQ2E.mjs → chunk-QODA5UKU.mjs} +33 -294
  23. package/dist/chunk-QODA5UKU.mjs.map +1 -0
  24. package/dist/chunk-YALPS44A.mjs +296 -0
  25. package/dist/chunk-YALPS44A.mjs.map +1 -0
  26. package/dist/{chunk-5YADUCA7.mjs → chunk-ZUWKAZ2I.mjs} +585 -991
  27. package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
  28. package/dist/cluster-binner.d.ts +5 -0
  29. package/dist/cluster-binner.d.ts.map +1 -1
  30. package/dist/components/index.d.ts +3 -0
  31. package/dist/components/index.d.ts.map +1 -1
  32. package/dist/components/light-helpers.d.ts +4 -0
  33. package/dist/components/light-helpers.d.ts.map +1 -1
  34. package/dist/components/light-probe.d.ts +9 -0
  35. package/dist/components/light-probe.d.ts.map +1 -0
  36. package/dist/components/rect-area-light.d.ts +12 -0
  37. package/dist/components/rect-area-light.d.ts.map +1 -0
  38. package/dist/components/scene-instance.d.ts +4 -0
  39. package/dist/components/scene-instance.d.ts.map +1 -1
  40. package/dist/components/spot-light.d.ts +3 -0
  41. package/dist/components/spot-light.d.ts.map +1 -1
  42. package/dist/construct-renderer.mjs +10437 -10044
  43. package/dist/construct-renderer.mjs.map +1 -1
  44. package/dist/device/gpu-residency.d.ts +8 -1
  45. package/dist/device/gpu-residency.d.ts.map +1 -1
  46. package/dist/device/lifecycle-transaction.d.ts +2 -0
  47. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  48. package/dist/environment/lifecycle.d.ts.map +1 -1
  49. package/dist/errors/gpu-driven.d.ts +26 -0
  50. package/dist/errors/gpu-driven.d.ts.map +1 -0
  51. package/dist/errors/index.d.ts +1 -0
  52. package/dist/errors/index.d.ts.map +1 -1
  53. package/dist/errors/recover.d.ts +9 -1
  54. package/dist/errors/recover.d.ts.map +1 -1
  55. package/dist/errors/render.d.ts +26 -1
  56. package/dist/errors/render.d.ts.map +1 -1
  57. package/dist/extract/gpu-driven.d.ts +50 -0
  58. package/dist/extract/gpu-driven.d.ts.map +1 -0
  59. package/dist/extract/visibility.d.ts.map +1 -1
  60. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  61. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  62. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  63. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  64. package/dist/gpu-driven/production-raster.d.ts +98 -2
  65. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  66. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  67. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  68. package/dist/gpu-scene-schema.d.ts +14 -0
  69. package/dist/gpu-scene-schema.d.ts.map +1 -1
  70. package/dist/gpu-scene.d.ts +8 -1
  71. package/dist/gpu-scene.d.ts.map +1 -1
  72. package/dist/gpu-usage.d.ts +0 -1
  73. package/dist/gpu-usage.d.ts.map +1 -1
  74. package/dist/hdrp-buffers.d.ts +3 -3
  75. package/dist/hdrp-buffers.d.ts.map +1 -1
  76. package/dist/index.d.ts +12 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.mjs +90 -5
  79. package/dist/index.mjs.map +1 -1
  80. package/dist/inspection-types.d.ts +163 -1
  81. package/dist/inspection-types.d.ts.map +1 -1
  82. package/dist/internal.d.ts +9 -0
  83. package/dist/internal.d.ts.map +1 -1
  84. package/dist/internal.mjs +23 -2
  85. package/dist/internal.mjs.map +1 -1
  86. package/dist/light-buffer-layout.d.ts +58 -149
  87. package/dist/light-buffer-layout.d.ts.map +1 -1
  88. package/dist/materials.d.ts.map +1 -1
  89. package/dist/pbr-pipeline.d.ts +26 -12
  90. package/dist/pbr-pipeline.d.ts.map +1 -1
  91. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  92. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
  93. package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
  94. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
  95. package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
  96. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
  97. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
  98. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  99. package/dist/pipeline-builder.d.ts +2 -0
  100. package/dist/pipeline-builder.d.ts.map +1 -1
  101. package/dist/pipeline-spec.d.ts +1 -1
  102. package/dist/pipeline-spec.d.ts.map +1 -1
  103. package/dist/plugin.d.ts.map +1 -1
  104. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  105. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  106. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  107. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  108. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  109. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  110. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  111. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  112. package/dist/record/frame-lighting.d.ts +8 -6
  113. package/dist/record/frame-lighting.d.ts.map +1 -1
  114. package/dist/record/frame-snapshot.d.ts +50 -10
  115. package/dist/record/frame-snapshot.d.ts.map +1 -1
  116. package/dist/record/frame.d.ts +7 -3
  117. package/dist/record/frame.d.ts.map +1 -1
  118. package/dist/record/gpu-timing.d.ts +19 -7
  119. package/dist/record/gpu-timing.d.ts.map +1 -1
  120. package/dist/record/helpers.d.ts +0 -20
  121. package/dist/record/helpers.d.ts.map +1 -1
  122. package/dist/record/main-pass-geometry.d.ts +6 -1
  123. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  124. package/dist/record/main-pass-material.d.ts +3 -1
  125. package/dist/record/main-pass-material.d.ts.map +1 -1
  126. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  127. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  128. package/dist/record/render-context.d.ts +57 -6
  129. package/dist/record/render-context.d.ts.map +1 -1
  130. package/dist/record/shadow-pass.d.ts.map +1 -1
  131. package/dist/record/typed-frame-graph.d.ts +49 -2
  132. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  133. package/dist/record/view-ubo.d.ts +1 -2
  134. package/dist/record/view-ubo.d.ts.map +1 -1
  135. package/dist/reflection/inspection.d.ts +10 -0
  136. package/dist/reflection/inspection.d.ts.map +1 -0
  137. package/dist/reflection/projection.d.ts +49 -0
  138. package/dist/reflection/projection.d.ts.map +1 -1
  139. package/dist/reflection/record-owner.d.ts +60 -0
  140. package/dist/reflection/record-owner.d.ts.map +1 -0
  141. package/dist/render-contract.d.ts +14 -8
  142. package/dist/render-contract.d.ts.map +1 -1
  143. package/dist/render-pipeline.d.ts +7 -0
  144. package/dist/render-pipeline.d.ts.map +1 -1
  145. package/dist/render-system-extract.d.ts +104 -16
  146. package/dist/render-system-extract.d.ts.map +1 -1
  147. package/dist/render-system.d.ts +12 -1
  148. package/dist/render-system.d.ts.map +1 -1
  149. package/dist/scene/probe-blend-record.d.ts +22 -0
  150. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  151. package/dist/scene/probe-blend.d.ts +142 -0
  152. package/dist/scene/probe-blend.d.ts.map +1 -0
  153. package/dist/scene/render-scene-types.d.ts +0 -6
  154. package/dist/scene/render-scene-types.d.ts.map +1 -1
  155. package/dist/scene/render-scene.d.ts +103 -9
  156. package/dist/scene/render-scene.d.ts.map +1 -1
  157. package/dist/scene/visibility/bounds.d.ts +20 -0
  158. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  159. package/dist/scene/visibility/budget.d.ts +16 -0
  160. package/dist/scene/visibility/budget.d.ts.map +1 -0
  161. package/dist/scene/visibility/facet.d.ts +73 -0
  162. package/dist/scene/visibility/facet.d.ts.map +1 -0
  163. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  164. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  165. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  166. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  167. package/dist/scene/visibility/inspection.d.ts +132 -0
  168. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  169. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  170. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  171. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  172. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  173. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  174. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  175. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  176. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  177. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  178. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  179. package/dist/scene/visibility/types.d.ts +34 -0
  180. package/dist/scene/visibility/types.d.ts.map +1 -0
  181. package/dist/ssr/admission.d.ts +122 -0
  182. package/dist/ssr/admission.d.ts.map +1 -0
  183. package/dist/ssr/composition.d.ts +32 -0
  184. package/dist/ssr/composition.d.ts.map +1 -0
  185. package/dist/ssr/errors.d.ts +39 -0
  186. package/dist/ssr/errors.d.ts.map +1 -0
  187. package/dist/ssr/identity.d.ts +13 -0
  188. package/dist/ssr/identity.d.ts.map +1 -0
  189. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  190. package/dist/temporal/frame-coordinator.d.ts +13 -0
  191. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  192. package/dist/temporal/frame.d.ts +2 -0
  193. package/dist/temporal/frame.d.ts.map +1 -1
  194. package/dist/temporal/index.mjs +6 -5
  195. package/dist/temporal/view.d.ts +7 -0
  196. package/dist/temporal/view.d.ts.map +1 -1
  197. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  198. package/dist/typed-render-graph-primitives.d.ts +4 -0
  199. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  200. package/dist/volume/passes.d.ts +8 -4
  201. package/dist/volume/passes.d.ts.map +1 -1
  202. package/package.json +20 -20
  203. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
  204. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  205. package/src/__tests__/device-feature-admission.unit.test.ts +13 -1
  206. package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
  207. package/src/__tests__/environment-generation.integration.test.ts +16 -0
  208. package/src/__tests__/evidence-identity.ts +13 -0
  209. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  210. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  211. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  212. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  213. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  214. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  215. package/src/__tests__/factory-contract.integration.test.ts +33 -0
  216. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  217. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  218. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
  219. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
  220. package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
  221. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  222. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  223. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
  224. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  225. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  226. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  227. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  228. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  229. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  230. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  231. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  232. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  233. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  234. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  235. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  236. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  237. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  238. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  239. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  240. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  241. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  242. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  243. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  244. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  245. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  246. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  247. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  248. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  249. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  250. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  251. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  252. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  253. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  254. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  255. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  256. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  257. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  258. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  259. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  260. package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
  261. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  262. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  263. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  264. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  265. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  266. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  267. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  268. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  269. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  270. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  271. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  272. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  273. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  274. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  275. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  276. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  277. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  278. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  279. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  280. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  281. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  282. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  283. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  284. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  285. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  286. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  287. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  288. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  289. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  290. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  291. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  292. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  293. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  294. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  295. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  296. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
  297. package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
  298. package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
  299. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  300. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  301. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  302. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  303. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  304. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
  305. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
  306. package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
  307. package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
  308. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  309. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  310. package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
  311. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  312. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  313. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  314. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  315. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  316. package/src/assembly/backend-contract.ts +13 -0
  317. package/src/assembly/device-feature-admission.ts +16 -9
  318. package/src/assembly/factory.ts +547 -93
  319. package/src/assembly/host-contract.ts +10 -0
  320. package/src/cluster-binner.ts +31 -0
  321. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  322. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  323. package/src/components/index.ts +3 -0
  324. package/src/components/light-helpers.ts +52 -1
  325. package/src/components/light-probe.ts +13 -0
  326. package/src/components/rect-area-light.ts +15 -0
  327. package/src/components/scene-instance.ts +6 -0
  328. package/src/components/spot-light.ts +3 -0
  329. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  330. package/src/device/gpu-residency.ts +140 -9
  331. package/src/device/lifecycle-transaction.ts +12 -0
  332. package/src/environment/lifecycle.ts +8 -1
  333. package/src/errors/gpu-driven.ts +92 -0
  334. package/src/errors/index.ts +1 -0
  335. package/src/errors/recover.ts +32 -0
  336. package/src/errors/render.ts +55 -1
  337. package/src/extract/camera.ts +3 -3
  338. package/src/extract/gpu-driven.ts +108 -0
  339. package/src/extract/visibility.ts +0 -23
  340. package/src/gpu-driven/batch-topology.ts +206 -28
  341. package/src/gpu-driven/prepared-draw.ts +208 -0
  342. package/src/gpu-driven/production-raster.ts +953 -41
  343. package/src/gpu-driven/view-gpu.ts +411 -29
  344. package/src/gpu-scene-schema.ts +13 -0
  345. package/src/gpu-scene.ts +134 -65
  346. package/src/gpu-usage.ts +0 -1
  347. package/src/hdrp-buffers.ts +6 -6
  348. package/src/index.ts +74 -0
  349. package/src/inspection-types.ts +220 -1
  350. package/src/internal.ts +38 -0
  351. package/src/light-buffer-layout.ts +154 -324
  352. package/src/materials.ts +16 -2
  353. package/src/pbr-pipeline.ts +151 -45
  354. package/src/pipeline/standard-forward-lane.ts +47 -0
  355. package/src/pipeline/standard-lighting/graph.ts +2 -1
  356. package/src/pipeline/standard-lighting/light-frame.ts +3 -2
  357. package/src/pipeline/standard-lighting/topology.ts +1 -1
  358. package/src/pipeline/standard-lighting/transport.ts +2 -1
  359. package/src/pipeline/standard-pipeline.ts +2 -0
  360. package/src/pipeline-builder.ts +5 -2
  361. package/src/pipeline-spec.ts +12 -1
  362. package/src/plugin.ts +4 -0
  363. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  364. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  365. package/src/prepare/extended-lighting/resources.ts +389 -0
  366. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  367. package/src/prepare/extended-lighting/state.ts +92 -0
  368. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  369. package/src/record/__tests__/temporal-payload.unit.test.ts +23 -1
  370. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
  371. package/src/record/frame-lighting.ts +252 -53
  372. package/src/record/frame-snapshot.ts +57 -12
  373. package/src/record/frame.ts +410 -93
  374. package/src/record/gpu-timing.ts +108 -44
  375. package/src/record/helpers.ts +0 -58
  376. package/src/record/main-pass-geometry.ts +204 -39
  377. package/src/record/main-pass-material.ts +8 -1
  378. package/src/record/main-pass-sprite-draws.ts +52 -85
  379. package/src/record/mesh-ssbo.ts +47 -36
  380. package/src/record/render-context.ts +65 -4
  381. package/src/record/shadow-pass.ts +66 -40
  382. package/src/record/typed-frame-graph.ts +225 -83
  383. package/src/record/view-ubo.ts +14 -24
  384. package/src/reflection/inspection.ts +67 -0
  385. package/src/reflection/projection.ts +196 -1
  386. package/src/reflection/record-owner.ts +1283 -0
  387. package/src/render-contract.ts +17 -5
  388. package/src/render-pipeline.ts +5 -0
  389. package/src/render-system-extract.ts +493 -134
  390. package/src/render-system.ts +811 -615
  391. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  392. package/src/scene/probe-blend-record.ts +54 -0
  393. package/src/scene/probe-blend.ts +813 -0
  394. package/src/scene/render-scene-types.ts +0 -7
  395. package/src/scene/render-scene.ts +848 -183
  396. package/src/scene/visibility/bounds.ts +57 -0
  397. package/src/scene/visibility/budget.ts +40 -0
  398. package/src/scene/visibility/facet.ts +400 -0
  399. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  400. package/src/scene/visibility/gpu-lod.ts +119 -0
  401. package/src/scene/visibility/inspection.ts +313 -0
  402. package/src/scene/visibility/lod-selector.ts +84 -0
  403. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  404. package/src/scene/visibility/occlusion-pass.ts +165 -0
  405. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  406. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  407. package/src/scene/visibility/types.ts +50 -0
  408. package/src/ssr/admission.ts +451 -0
  409. package/src/ssr/composition.ts +73 -0
  410. package/src/ssr/errors.ts +76 -0
  411. package/src/ssr/identity.ts +12 -0
  412. package/src/systems/skin-palette-allocator.ts +1 -1
  413. package/src/temporal/frame-coordinator.ts +26 -0
  414. package/src/temporal/frame.ts +14 -1
  415. package/src/temporal/view.ts +11 -0
  416. package/src/tilemap-chunk-extract-system.ts +4 -4
  417. package/src/typed-render-graph-primitives.ts +48 -0
  418. package/src/volume/passes.ts +46 -49
  419. package/dist/.tsbuildinfo +0 -1
  420. package/dist/chunk-5YADUCA7.mjs.map +0 -1
  421. package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
  422. package/dist/chunk-BXBQQKFQ.mjs +0 -67
  423. package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
  424. package/dist/chunk-FS2KNUQH.mjs.map +0 -1
  425. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  426. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -0,0 +1,478 @@
1
+ import { createVisibilityBudget, type VisibilityBudget } from './budget';
2
+
3
+ export type InvalidationReason =
4
+ | 'cut'
5
+ | 'teleport'
6
+ | 'resize'
7
+ | 'projection'
8
+ | 'primitive'
9
+ | 'bounds'
10
+ | 'topology'
11
+ | 'world-reorder'
12
+ | 'detach'
13
+ | 'failed-submit'
14
+ | 'device-loss'
15
+ | 'transparent';
16
+
17
+ export interface ConfidenceState {
18
+ readonly status: 'visible' | 'hidden';
19
+ readonly zeroStreak: number;
20
+ readonly successfulSubmits: number;
21
+ readonly queryable: boolean;
22
+ readonly dirty: boolean;
23
+ readonly lastResultGeneration: number | undefined;
24
+ }
25
+
26
+ export type ConfidenceEvent =
27
+ | { readonly type: 'result'; readonly samples: number; readonly submissionGeneration: number }
28
+ | { readonly type: 'submit'; readonly submissionGeneration: number }
29
+ | { readonly type: 'failure'; readonly submissionGeneration: number }
30
+ | { readonly type: 'invalidate'; readonly reason: InvalidationReason };
31
+
32
+ export function createConfidenceState(): ConfidenceState {
33
+ return {
34
+ status: 'visible',
35
+ zeroStreak: 0,
36
+ successfulSubmits: 0,
37
+ queryable: true,
38
+ dirty: true,
39
+ lastResultGeneration: undefined,
40
+ };
41
+ }
42
+
43
+ export function applyConfidenceEvent(
44
+ state: ConfidenceState,
45
+ event: ConfidenceEvent,
46
+ budget: VisibilityBudget = createVisibilityBudget(),
47
+ ): ConfidenceState {
48
+ if (event.type === 'result') {
49
+ if (!Number.isFinite(event.samples) || event.samples < 0) {
50
+ return { ...state, status: 'visible', dirty: true, queryable: true };
51
+ }
52
+ if (event.samples > 0) {
53
+ return {
54
+ ...state,
55
+ status: 'visible',
56
+ zeroStreak: 0,
57
+ successfulSubmits: 0,
58
+ queryable: true,
59
+ dirty: false,
60
+ lastResultGeneration: event.submissionGeneration,
61
+ };
62
+ }
63
+ const zeroStreak = state.zeroStreak + 1;
64
+ const hidden = zeroStreak >= 2;
65
+ return {
66
+ ...state,
67
+ status: hidden ? 'hidden' : 'visible',
68
+ zeroStreak,
69
+ successfulSubmits: 0,
70
+ queryable: true,
71
+ // A first zero is only provisional evidence. Keep it dirty so the
72
+ // producer naturally submits the second query; suppression requires two
73
+ // real zero results rather than two hand-applied state transitions.
74
+ dirty: !hidden,
75
+ lastResultGeneration: event.submissionGeneration,
76
+ };
77
+ }
78
+ if (event.type === 'submit') {
79
+ const successfulSubmits = state.successfulSubmits + 1;
80
+ if (successfulSubmits >= budget.expirySubmits) {
81
+ return {
82
+ ...state,
83
+ status: 'visible',
84
+ zeroStreak: 0,
85
+ successfulSubmits: 0,
86
+ queryable: true,
87
+ dirty: true,
88
+ };
89
+ }
90
+ return { ...state, successfulSubmits, dirty: state.dirty };
91
+ }
92
+ if (event.type === 'failure') {
93
+ return {
94
+ ...state,
95
+ status: 'visible',
96
+ zeroStreak: 0,
97
+ successfulSubmits: 0,
98
+ queryable: true,
99
+ dirty: true,
100
+ };
101
+ }
102
+ return {
103
+ ...state,
104
+ status: 'visible',
105
+ zeroStreak: 0,
106
+ successfulSubmits: 0,
107
+ queryable: event.reason !== 'transparent' && event.reason !== 'detach',
108
+ dirty: true,
109
+ lastResultGeneration: undefined,
110
+ };
111
+ }
112
+
113
+ export function shouldIssueRetest(
114
+ state: ConfidenceState,
115
+ budget: VisibilityBudget = createVisibilityBudget(),
116
+ ): boolean {
117
+ return (
118
+ state.status === 'hidden' && state.queryable && state.successfulSubmits >= budget.retestSubmits
119
+ );
120
+ }
121
+
122
+ function shouldQueryState(state: ConfidenceState, budget: VisibilityBudget): boolean {
123
+ return (state.dirty && state.successfulSubmits === 0) || shouldIssueRetest(state, budget);
124
+ }
125
+
126
+ export class OcclusionConfidenceScheduler {
127
+ private readonly states = new Map<string, ConfidenceState>();
128
+ private readonly wakeByGeneration = new Map<number, Set<string>>();
129
+ private readonly expireByGeneration = new Map<number, Set<string>>();
130
+ private readonly wakeHeap: number[] = [];
131
+ private readonly expireHeap: number[] = [];
132
+ private readonly wakeHeapMembers = new Set<number>();
133
+ private readonly expireHeapMembers = new Set<number>();
134
+ private readonly retestKeys = new Set<string>();
135
+ private readonly scheduleByKey = new Map<
136
+ string,
137
+ { readonly wake: number; readonly expire: number }
138
+ >();
139
+
140
+ constructor(private readonly budget: VisibilityBudget = createVisibilityBudget()) {}
141
+
142
+ ensure(key: string): ConfidenceState {
143
+ const existing = this.states.get(key);
144
+ if (existing !== undefined) return existing;
145
+ const state = createConfidenceState();
146
+ this.states.set(key, state);
147
+ return state;
148
+ }
149
+
150
+ apply(key: string, event: ConfidenceEvent): ConfidenceState {
151
+ this.unschedule(key);
152
+ const next = applyConfidenceEvent(this.ensure(key), event, this.budget);
153
+ this.states.set(key, next);
154
+ this.refreshRetestKey(key, next);
155
+ const anchor =
156
+ event.type === 'result' || event.type === 'submit' || event.type === 'failure'
157
+ ? event.submissionGeneration
158
+ : next.lastResultGeneration;
159
+ this.schedule(key, next, anchor);
160
+ return next;
161
+ }
162
+
163
+ /** Apply an in-flight query submit without scheduling a same-generation wake. */
164
+ applyInFlightSubmit(
165
+ key: string,
166
+ event: Extract<ConfidenceEvent, { readonly type: 'submit' }>,
167
+ ): ConfidenceState {
168
+ this.unschedule(key);
169
+ const next = applyConfidenceEvent(this.ensure(key), event, this.budget);
170
+ this.states.set(key, next);
171
+ this.refreshRetestKey(key, next);
172
+ this.scheduleExpiryOnly(key, next, event.submissionGeneration);
173
+ return next;
174
+ }
175
+
176
+ drainRetests(limit: number): string[] {
177
+ if (!Number.isSafeInteger(limit) || limit <= 0) return [];
178
+ const result: string[] = [];
179
+ for (const key of this.retestKeys) {
180
+ if (result.length >= limit) break;
181
+ result.push(key);
182
+ }
183
+ return result;
184
+ }
185
+
186
+ get(key: string): ConfidenceState | undefined {
187
+ return this.states.get(key);
188
+ }
189
+
190
+ remove(key: string): void {
191
+ this.unschedule(key);
192
+ this.retestKeys.delete(key);
193
+ this.states.delete(key);
194
+ }
195
+
196
+ /** Advance hidden evidence on a successful frame even when no query was issued for it. */
197
+ advanceSuccessfulSubmits(
198
+ submissionGeneration: number,
199
+ excludedKeys?: ReadonlySet<string>,
200
+ ): {
201
+ readonly changed: boolean;
202
+ readonly drawChangedKeys: readonly string[];
203
+ readonly queryChangedKeys: readonly string[];
204
+ } {
205
+ let changed = false;
206
+ const drawChangedKeys: string[] = [];
207
+ const queryChangedKeys: string[] = [];
208
+ // Expiry is consumed first. If a caller advances several generations in
209
+ // one tick, an old expiry must win over a stale wake bucket; otherwise the
210
+ // wake path could postpone visibility recovery indefinitely.
211
+ const expireKeys = this.takeDue(
212
+ this.expireByGeneration,
213
+ this.expireHeap,
214
+ this.expireHeapMembers,
215
+ submissionGeneration,
216
+ );
217
+ for (const key of expireKeys) {
218
+ const state = this.states.get(key);
219
+ if (state === undefined || state.status !== 'hidden' || !state.queryable) continue;
220
+ if (excludedKeys?.has(key)) {
221
+ this.unschedule(key);
222
+ this.schedule(key, state, submissionGeneration);
223
+ continue;
224
+ }
225
+ const beforeQuery = shouldQueryState(state, this.budget);
226
+ this.unschedule(key);
227
+ const expiring =
228
+ state.successfulSubmits >= this.budget.expirySubmits - 1
229
+ ? state
230
+ : { ...state, successfulSubmits: this.budget.expirySubmits - 1 };
231
+ const expired = applyConfidenceEvent(
232
+ expiring,
233
+ {
234
+ type: 'submit',
235
+ submissionGeneration,
236
+ },
237
+ this.budget,
238
+ );
239
+ this.states.set(key, expired);
240
+ this.refreshRetestKey(key, expired);
241
+ changed = true;
242
+ if (state.status !== expired.status || state.queryable !== expired.queryable) {
243
+ drawChangedKeys.push(key);
244
+ }
245
+ if (beforeQuery !== shouldQueryState(expired, this.budget)) queryChangedKeys.push(key);
246
+ }
247
+ const wakeKeys = this.takeDue(
248
+ this.wakeByGeneration,
249
+ this.wakeHeap,
250
+ this.wakeHeapMembers,
251
+ submissionGeneration,
252
+ );
253
+ for (const key of wakeKeys) {
254
+ const state = this.states.get(key);
255
+ if (state === undefined || state.status !== 'hidden' || !state.queryable) continue;
256
+ if (excludedKeys?.has(key)) {
257
+ this.unschedule(key);
258
+ this.schedule(key, state, submissionGeneration);
259
+ continue;
260
+ }
261
+ const beforeQuery = shouldQueryState(state, this.budget);
262
+ this.unschedule(key);
263
+ const next: ConfidenceState =
264
+ state.successfulSubmits >= this.budget.retestSubmits
265
+ ? state
266
+ : { ...state, successfulSubmits: this.budget.retestSubmits };
267
+ this.states.set(key, next);
268
+ this.refreshRetestKey(key, next);
269
+ changed = changed || next !== state;
270
+ if (state.status !== next.status || state.queryable !== next.queryable) {
271
+ drawChangedKeys.push(key);
272
+ }
273
+ if (beforeQuery !== shouldQueryState(next, this.budget)) queryChangedKeys.push(key);
274
+ this.scheduleExpiry(
275
+ key,
276
+ submissionGeneration + Math.max(1, this.budget.expirySubmits - next.successfulSubmits),
277
+ );
278
+ }
279
+ return { changed, drawChangedKeys, queryChangedKeys };
280
+ }
281
+
282
+ clear(): void {
283
+ this.states.clear();
284
+ this.wakeByGeneration.clear();
285
+ this.expireByGeneration.clear();
286
+ this.scheduleByKey.clear();
287
+ this.retestKeys.clear();
288
+ this.wakeHeap.length = 0;
289
+ this.expireHeap.length = 0;
290
+ this.wakeHeapMembers.clear();
291
+ this.expireHeapMembers.clear();
292
+ }
293
+
294
+ private schedule(key: string, state: ConfidenceState, anchorGeneration?: number): void {
295
+ if (state.status !== 'hidden' || !state.queryable || state.lastResultGeneration === undefined) {
296
+ return;
297
+ }
298
+ const anchor = anchorGeneration ?? state.lastResultGeneration;
299
+ const wake = anchor + Math.max(0, this.budget.retestSubmits - state.successfulSubmits);
300
+ const expire = anchor + Math.max(1, this.budget.expirySubmits - state.successfulSubmits);
301
+ this.scheduleExpiry(key, expire);
302
+ const entry = this.scheduleByKey.get(key);
303
+ if (entry !== undefined && entry.wake === wake) return;
304
+ this.addDue(this.wakeByGeneration, this.wakeHeap, this.wakeHeapMembers, wake, key);
305
+ this.scheduleByKey.set(key, {
306
+ wake,
307
+ expire: entry?.expire ?? expire,
308
+ });
309
+ }
310
+
311
+ private scheduleExpiry(key: string, expire: number): void {
312
+ this.addDue(this.expireByGeneration, this.expireHeap, this.expireHeapMembers, expire, key);
313
+ const entry = this.scheduleByKey.get(key);
314
+ this.scheduleByKey.set(key, {
315
+ wake: entry?.wake ?? Number.POSITIVE_INFINITY,
316
+ expire,
317
+ });
318
+ }
319
+
320
+ private scheduleExpiryOnly(key: string, state: ConfidenceState, anchorGeneration: number): void {
321
+ if (state.status !== 'hidden' || !state.queryable || state.lastResultGeneration === undefined) {
322
+ return;
323
+ }
324
+ const expire =
325
+ anchorGeneration + Math.max(1, this.budget.expirySubmits - state.successfulSubmits);
326
+ this.addDue(this.expireByGeneration, this.expireHeap, this.expireHeapMembers, expire, key);
327
+ this.scheduleByKey.set(key, { wake: Number.POSITIVE_INFINITY, expire });
328
+ }
329
+
330
+ private unschedule(key: string): void {
331
+ const entry = this.scheduleByKey.get(key);
332
+ if (entry === undefined) return;
333
+ if (Number.isFinite(entry.wake)) {
334
+ removeDue(this.wakeByGeneration, this.wakeHeapMembers, entry.wake, key);
335
+ }
336
+ if (Number.isFinite(entry.expire)) {
337
+ removeDue(this.expireByGeneration, this.expireHeapMembers, entry.expire, key);
338
+ }
339
+ this.scheduleByKey.delete(key);
340
+ }
341
+
342
+ private takeDue(
343
+ source: Map<number, Set<string>>,
344
+ heap: number[],
345
+ heapMembers: Set<number>,
346
+ generation: number,
347
+ ): string[] {
348
+ const result: string[] = [];
349
+ while (heap[0] !== undefined && (heap[0] as number) <= generation) {
350
+ const due = popMin(heap);
351
+ heapMembers.delete(due);
352
+ const keys = source.get(due);
353
+ if (keys === undefined) continue;
354
+ source.delete(due);
355
+ result.push(...keys);
356
+ }
357
+ return result;
358
+ }
359
+
360
+ private addDue(
361
+ source: Map<number, Set<string>>,
362
+ heap: number[],
363
+ heapMembers: Set<number>,
364
+ generation: number,
365
+ key: string,
366
+ ): void {
367
+ const set = source.get(generation) ?? new Set<string>();
368
+ if (!source.has(generation)) {
369
+ source.set(generation, set);
370
+ if (!heapMembers.has(generation)) {
371
+ heapMembers.add(generation);
372
+ pushMin(heap, generation);
373
+ }
374
+ }
375
+ set.add(key);
376
+ }
377
+
378
+ private refreshRetestKey(key: string, state: ConfidenceState): void {
379
+ if (shouldIssueRetest(state, this.budget)) this.retestKeys.add(key);
380
+ else this.retestKeys.delete(key);
381
+ }
382
+ }
383
+
384
+ function removeDue(
385
+ source: Map<number, Set<string>>,
386
+ heapMembers: Set<number>,
387
+ generation: number,
388
+ key: string,
389
+ ): void {
390
+ const keys = source.get(generation);
391
+ if (keys === undefined) return;
392
+ keys.delete(key);
393
+ // The heap intentionally has no arbitrary-delete operation. Removing the
394
+ // membership marker lets a later requeue of this same generation push a
395
+ // fresh heap entry; the old heap entry is harmlessly skipped by takeDue.
396
+ if (keys.size === 0) {
397
+ source.delete(generation);
398
+ heapMembers.delete(generation);
399
+ }
400
+ }
401
+
402
+ function pushMin(heap: number[], value: number): void {
403
+ heap.push(value);
404
+ let index = heap.length - 1;
405
+ while (index > 0) {
406
+ const parent = Math.floor((index - 1) / 2);
407
+ if ((heap[parent] as number) <= value) break;
408
+ heap[index] = heap[parent] as number;
409
+ index = parent;
410
+ }
411
+ heap[index] = value;
412
+ }
413
+
414
+ function popMin(heap: number[]): number {
415
+ const first = heap[0] as number;
416
+ const last = heap.pop() as number | undefined;
417
+ if (last !== undefined && heap.length > 0) {
418
+ let index = 0;
419
+ while (true) {
420
+ const left = index * 2 + 1;
421
+ if (left >= heap.length) break;
422
+ const right = left + 1;
423
+ const child =
424
+ right < heap.length && (heap[right] as number) < (heap[left] as number) ? right : left;
425
+ if ((heap[child] as number) >= last) break;
426
+ heap[index] = heap[child] as number;
427
+ index = child;
428
+ }
429
+ heap[index] = last;
430
+ }
431
+ return first;
432
+ }
433
+
434
+ export interface VisibilityDecisionInput {
435
+ readonly authorVisible: boolean;
436
+ readonly validBounds: boolean;
437
+ readonly frustumVisible: boolean;
438
+ readonly lodReady: boolean;
439
+ readonly occlusion: Pick<ConfidenceState, 'status' | 'queryable'>;
440
+ readonly lane?: 'cpu' | 'gpu';
441
+ }
442
+
443
+ export interface VisibilityDecision {
444
+ readonly draw: boolean;
445
+ readonly stage: 'author' | 'bounds' | 'frustum' | 'lod' | 'occlusion';
446
+ }
447
+
448
+ export function decideVisibility(input: VisibilityDecisionInput): VisibilityDecision {
449
+ if (!input.authorVisible) return { draw: false, stage: 'author' };
450
+ // Bounds are query input, not draw authority. Unknown or invalid bounds
451
+ // skip frustum/query rejection and remain conservatively visible.
452
+ if (!input.validBounds) return { draw: true, stage: 'bounds' };
453
+ if (!input.frustumVisible) return { draw: false, stage: 'frustum' };
454
+ if (!input.lodReady) return { draw: true, stage: 'lod' };
455
+ if (input.occlusion.status === 'hidden' && input.occlusion.queryable) {
456
+ return { draw: false, stage: 'occlusion' };
457
+ }
458
+ return { draw: true, stage: 'occlusion' };
459
+ }
460
+
461
+ export interface RecoveredVisibilityState extends ConfidenceState {
462
+ readonly retryCount: 1;
463
+ }
464
+
465
+ export function recoverVisibilityAfterDeviceLoss(
466
+ _state: ConfidenceState,
467
+ _deviceGeneration: number,
468
+ ): RecoveredVisibilityState {
469
+ return {
470
+ status: 'visible',
471
+ zeroStreak: 0,
472
+ successfulSubmits: 0,
473
+ queryable: true,
474
+ dirty: true,
475
+ lastResultGeneration: undefined,
476
+ retryCount: 1,
477
+ };
478
+ }
@@ -0,0 +1,165 @@
1
+ export interface OcclusionBounds {
2
+ readonly min: readonly [number, number, number];
3
+ readonly max: readonly [number, number, number];
4
+ }
5
+
6
+ export interface OcclusionPassIdentity {
7
+ readonly primitiveSlot: number;
8
+ readonly slotGeneration: number;
9
+ }
10
+
11
+ export interface OcclusionPassPlan {
12
+ readonly after: string;
13
+ readonly depth: { readonly resource: string; readonly usage: 'depth-read-only' };
14
+ readonly color: { readonly resource: string; readonly usage: 'not-written' };
15
+ readonly proxyBounds: OcclusionBounds;
16
+ readonly queryIndex: number;
17
+ readonly identity?: OcclusionPassIdentity;
18
+ readonly resolve: { readonly queryIndex: number; readonly bytes: number };
19
+ readonly copy: { readonly asynchronous: true; readonly bytes: number };
20
+ readonly writesColor: false;
21
+ readonly writesDepth: false;
22
+ readonly submitCount: 1;
23
+ readonly mapSameFrame: false;
24
+ }
25
+
26
+ export interface OcclusionPassInput {
27
+ readonly opaquePass: string;
28
+ readonly depthResource: string;
29
+ readonly colorResource: string;
30
+ readonly bounds: OcclusionBounds | undefined;
31
+ readonly queryIndex: number;
32
+ readonly identity?: OcclusionPassIdentity;
33
+ readonly transparent?: boolean;
34
+ readonly deformed?: boolean;
35
+ }
36
+
37
+ export interface OcclusionQueryResources {
38
+ readonly pageIndex: number;
39
+ readonly querySet: QuerySet;
40
+ readonly resolveBuffer: Buffer;
41
+ readonly stagingBuffer: Buffer;
42
+ }
43
+
44
+ export function createOcclusionQueryResources(
45
+ device: RhiDevice,
46
+ pageIndex: number,
47
+ ): Result<OcclusionQueryResources, RhiError> {
48
+ const querySet = device.createQuerySet({
49
+ label: `occlusion-page-${pageIndex}`,
50
+ type: 'occlusion',
51
+ count: OCCLUSION_QUERY_PAGE_INDEX_LIMIT,
52
+ });
53
+ if (!querySet.ok) return querySet;
54
+ const resolveBuffer = device.createBuffer({
55
+ label: `occlusion-page-${pageIndex}-resolve`,
56
+ size: OCCLUSION_QUERY_PAGE_BYTES,
57
+ usage: GPU_BUFFER_USAGE_QUERY_RESOLVE | GPU_BUFFER_USAGE_COPY_SRC,
58
+ });
59
+ if (!resolveBuffer.ok) {
60
+ device.destroyQuerySet(querySet.value);
61
+ return resolveBuffer;
62
+ }
63
+ const stagingBuffer = device.createBuffer({
64
+ label: `occlusion-page-${pageIndex}-staging`,
65
+ size: OCCLUSION_QUERY_PAGE_BYTES,
66
+ usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST,
67
+ });
68
+ if (!stagingBuffer.ok) {
69
+ device.destroyBuffer(resolveBuffer.value);
70
+ device.destroyQuerySet(querySet.value);
71
+ return stagingBuffer;
72
+ }
73
+ return ok({
74
+ pageIndex,
75
+ querySet: querySet.value,
76
+ resolveBuffer: resolveBuffer.value,
77
+ stagingBuffer: stagingBuffer.value,
78
+ });
79
+ }
80
+
81
+ /** Records resolve and copy only; caller owns one shared encoder and submit. */
82
+ export function recordOcclusionResolve(
83
+ encoder: RhiCommandEncoder,
84
+ resources: OcclusionQueryResources,
85
+ firstQuery: number,
86
+ queryCount: number,
87
+ ): Result<void, RhiError> {
88
+ if (
89
+ !Number.isSafeInteger(firstQuery) ||
90
+ !Number.isSafeInteger(queryCount) ||
91
+ firstQuery < 0 ||
92
+ queryCount <= 0 ||
93
+ firstQuery + queryCount > OCCLUSION_QUERY_PAGE_INDEX_LIMIT
94
+ ) {
95
+ return err(
96
+ new RhiErrorClass({
97
+ code: 'webgpu-runtime-error',
98
+ expected: 'query range stays within one 4096-index page',
99
+ hint: 'allocate a bounded page before recording occlusion resolve',
100
+ }),
101
+ );
102
+ }
103
+ const resolved = encoder.resolveQuerySet(
104
+ resources.querySet,
105
+ firstQuery,
106
+ queryCount,
107
+ resources.resolveBuffer,
108
+ 0,
109
+ );
110
+ if (!resolved.ok) return resolved;
111
+ encoder.copyBufferToBuffer(resources.resolveBuffer, resources.stagingBuffer, queryCount * 8);
112
+ return ok(undefined);
113
+ }
114
+
115
+ function finiteBounds(bounds: OcclusionBounds | undefined): bounds is OcclusionBounds {
116
+ if (bounds === undefined) return false;
117
+ return [...bounds.min, ...bounds.max].every((value) => Number.isFinite(value));
118
+ }
119
+
120
+ /** Creates only the transport plan; confidence remains owned by visibility facet/M4. */
121
+ export function buildOcclusionPassPlan(input: OcclusionPassInput): OcclusionPassPlan | undefined {
122
+ if (
123
+ input.transparent === true ||
124
+ input.deformed === true ||
125
+ !finiteBounds(input.bounds) ||
126
+ !Number.isSafeInteger(input.queryIndex) ||
127
+ input.queryIndex < 0
128
+ ) {
129
+ return undefined;
130
+ }
131
+ return Object.freeze({
132
+ after: input.opaquePass,
133
+ depth: { resource: input.depthResource, usage: 'depth-read-only' as const },
134
+ color: { resource: input.colorResource, usage: 'not-written' as const },
135
+ proxyBounds: input.bounds,
136
+ queryIndex: input.queryIndex,
137
+ ...(input.identity === undefined ? {} : { identity: Object.freeze({ ...input.identity }) }),
138
+ resolve: { queryIndex: input.queryIndex, bytes: 8 },
139
+ copy: { asynchronous: true as const, bytes: 8 },
140
+ writesColor: false as const,
141
+ writesDepth: false as const,
142
+ submitCount: 1 as const,
143
+ mapSameFrame: false as const,
144
+ });
145
+ }
146
+
147
+ import type {
148
+ Buffer,
149
+ QuerySet,
150
+ Result,
151
+ RhiCommandEncoder,
152
+ RhiDevice,
153
+ RhiError,
154
+ } from '@forgeax/engine-rhi';
155
+ import { err, ok, RhiError as RhiErrorClass } from '@forgeax/engine-rhi';
156
+ import {
157
+ GPU_BUFFER_USAGE_COPY_DST,
158
+ GPU_BUFFER_USAGE_COPY_SRC,
159
+ GPU_BUFFER_USAGE_MAP_READ,
160
+ GPU_BUFFER_USAGE_QUERY_RESOLVE,
161
+ } from '../../gpu-usage';
162
+ import {
163
+ OCCLUSION_QUERY_PAGE_BYTES,
164
+ OCCLUSION_QUERY_PAGE_INDEX_LIMIT,
165
+ } from './occlusion-query-pool';