@forgeax/engine-render 0.1.33 → 0.1.34

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 (518) hide show
  1. package/README.md +1010 -124
  2. package/dist/assembly/backend-contract.d.ts +2 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  5. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  6. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  7. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  8. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  9. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  10. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  11. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  12. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  13. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  14. package/dist/assembly/factory.d.ts +1 -1
  15. package/dist/assembly/factory.d.ts.map +1 -1
  16. package/dist/assembly/host-contract.d.ts +3 -3
  17. package/dist/assembly/host-contract.d.ts.map +1 -1
  18. package/dist/assembly/material/assembly.d.ts.map +1 -1
  19. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  20. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  21. package/dist/assembly/material-shader-policy.d.ts +16 -2
  22. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  23. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  25. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  26. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  27. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  28. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  29. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  30. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  31. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  32. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-inspection.d.ts +11 -0
  35. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  36. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  37. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  38. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  39. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready.d.ts +1 -1
  41. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  42. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  43. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  44. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  45. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  46. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  47. package/dist/authoring.mjs +1 -1
  48. package/dist/barrel-distortion.d.ts +47 -0
  49. package/dist/barrel-distortion.d.ts.map +1 -0
  50. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  51. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  52. package/dist/{chunk-ZDQWGQW7.mjs → chunk-HEB564WF.mjs} +4 -4
  53. package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-HEB564WF.mjs.map} +1 -1
  54. package/dist/{chunk-VOZWDM3L.mjs → chunk-MKDIT2EM.mjs} +1307 -188
  55. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  56. package/dist/{chunk-LEAXOMYJ.mjs → chunk-OCDLEI6I.mjs} +3 -3
  57. package/dist/{chunk-LEAXOMYJ.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  58. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  59. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  60. package/dist/{chunk-3RTCJTH4.mjs → chunk-SHFUFJ5N.mjs} +184 -13
  61. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  62. package/dist/{chunk-OO3DRYK7.mjs → chunk-UI5SFODX.mjs} +176 -96
  63. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  64. package/dist/{chunk-PLAXOOX5.mjs → chunk-UINHLCZA.mjs} +561 -79
  65. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  66. package/dist/{chunk-NB2S7ZRX.mjs → chunk-WEX45HKH.mjs} +127 -386
  67. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  68. package/dist/cloud/density.d.ts +55 -0
  69. package/dist/cloud/density.d.ts.map +1 -0
  70. package/dist/cloud/extract.d.ts +25 -0
  71. package/dist/cloud/extract.d.ts.map +1 -0
  72. package/dist/cloud/feature.d.ts +57 -0
  73. package/dist/cloud/feature.d.ts.map +1 -0
  74. package/dist/cloud/frame-context.d.ts +8 -0
  75. package/dist/cloud/frame-context.d.ts.map +1 -0
  76. package/dist/cloud/inspection.d.ts +59 -0
  77. package/dist/cloud/inspection.d.ts.map +1 -0
  78. package/dist/cloud/optics.d.ts +56 -0
  79. package/dist/cloud/optics.d.ts.map +1 -0
  80. package/dist/cloud/parameters.d.ts +73 -0
  81. package/dist/cloud/parameters.d.ts.map +1 -0
  82. package/dist/cloud/resources.d.ts +60 -0
  83. package/dist/cloud/resources.d.ts.map +1 -0
  84. package/dist/cloud/shadow.d.ts +42 -0
  85. package/dist/cloud/shadow.d.ts.map +1 -0
  86. package/dist/cloud/temporal.d.ts +74 -0
  87. package/dist/cloud/temporal.d.ts.map +1 -0
  88. package/dist/components/barrel-distortion.d.ts +13 -0
  89. package/dist/components/barrel-distortion.d.ts.map +1 -0
  90. package/dist/components/cloud-layer.d.ts +29 -0
  91. package/dist/components/cloud-layer.d.ts.map +1 -0
  92. package/dist/components/index.d.ts +2 -0
  93. package/dist/components/index.d.ts.map +1 -1
  94. package/dist/components/instances.d.ts +20 -6
  95. package/dist/components/instances.d.ts.map +1 -1
  96. package/dist/construct-renderer.d.ts +1 -1
  97. package/dist/construct-renderer.d.ts.map +1 -1
  98. package/dist/construct-renderer.mjs +6759 -2875
  99. package/dist/construct-renderer.mjs.map +1 -1
  100. package/dist/dynamic-geometry.d.ts +5 -1
  101. package/dist/dynamic-geometry.d.ts.map +1 -1
  102. package/dist/errors/cloud.d.ts +59 -0
  103. package/dist/errors/cloud.d.ts.map +1 -0
  104. package/dist/errors/render.d.ts +18 -4
  105. package/dist/errors/render.d.ts.map +1 -1
  106. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  107. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  108. package/dist/extract/camera.d.ts.map +1 -1
  109. package/dist/extract/contracts.d.ts +5 -1
  110. package/dist/extract/contracts.d.ts.map +1 -1
  111. package/dist/features/barrel-distortion.d.ts +29 -0
  112. package/dist/features/barrel-distortion.d.ts.map +1 -0
  113. package/dist/features/error-reporting.d.ts +6 -2
  114. package/dist/features/error-reporting.d.ts.map +1 -1
  115. package/dist/features/host.d.ts +10 -0
  116. package/dist/features/host.d.ts.map +1 -1
  117. package/dist/features/plan.d.ts +8 -0
  118. package/dist/features/plan.d.ts.map +1 -1
  119. package/dist/features/render-graph-contribution.d.ts +5 -0
  120. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  121. package/dist/features/render-graph-raster.d.ts +1 -0
  122. package/dist/features/render-graph-raster.d.ts.map +1 -1
  123. package/dist/features/targets.d.ts +2 -0
  124. package/dist/features/targets.d.ts.map +1 -1
  125. package/dist/features/types.d.ts +44 -0
  126. package/dist/features/types.d.ts.map +1 -1
  127. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  128. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  129. package/dist/gpu-dirty-ranges.d.ts +11 -0
  130. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  131. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  132. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  133. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  134. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  135. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  136. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  137. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  138. package/dist/gpu-driven/production-raster.d.ts +47 -3
  139. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  140. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  141. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  142. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  143. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  144. package/dist/gpu-driven/view-gpu.d.ts +26 -2
  145. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  146. package/dist/gpu-scene.d.ts +5 -0
  147. package/dist/gpu-scene.d.ts.map +1 -1
  148. package/dist/hdrp-buffers.d.ts +2 -2
  149. package/dist/hdrp-buffers.d.ts.map +1 -1
  150. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  151. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  152. package/dist/index.d.ts +27 -7
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.mjs +258 -8
  155. package/dist/index.mjs.map +1 -1
  156. package/dist/inspection-types.d.ts +135 -1
  157. package/dist/inspection-types.d.ts.map +1 -1
  158. package/dist/instance-buffer-cache.d.ts +4 -4
  159. package/dist/instance-buffer-cache.d.ts.map +1 -1
  160. package/dist/instances.d.ts +48 -100
  161. package/dist/instances.d.ts.map +1 -1
  162. package/dist/internal.mjs +7 -7
  163. package/dist/pbr-pipeline.d.ts +19 -1
  164. package/dist/pbr-pipeline.d.ts.map +1 -1
  165. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  166. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  167. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  168. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  169. package/dist/pipeline/standard-output/types.d.ts +4 -2
  170. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  171. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  172. package/dist/pipeline/standard-post.d.ts.map +1 -1
  173. package/dist/pipeline-builder.d.ts +1 -1
  174. package/dist/pipeline-builder.d.ts.map +1 -1
  175. package/dist/plugin.d.ts.map +1 -1
  176. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  177. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  178. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  179. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  180. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  181. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  182. package/dist/record/fold-buckets.d.ts +95 -0
  183. package/dist/record/fold-buckets.d.ts.map +1 -0
  184. package/dist/record/fold-instance-buffer.d.ts +6 -0
  185. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  186. package/dist/record/frame-lighting.d.ts +1 -0
  187. package/dist/record/frame-lighting.d.ts.map +1 -1
  188. package/dist/record/frame-snapshot.d.ts +37 -3
  189. package/dist/record/frame-snapshot.d.ts.map +1 -1
  190. package/dist/record/frame.d.ts +10 -61
  191. package/dist/record/frame.d.ts.map +1 -1
  192. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  193. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  194. package/dist/record/gpu-timing.d.ts +8 -0
  195. package/dist/record/gpu-timing.d.ts.map +1 -1
  196. package/dist/record/main-pass-geometry.d.ts +21 -5
  197. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  198. package/dist/record/main-pass-material.d.ts +7 -1
  199. package/dist/record/main-pass-material.d.ts.map +1 -1
  200. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  201. package/dist/record/main-pass.d.ts +13 -2
  202. package/dist/record/main-pass.d.ts.map +1 -1
  203. package/dist/record/motion-blur-receipt.d.ts +11 -0
  204. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  205. package/dist/record/probe-blend-buffer.d.ts +16 -0
  206. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  207. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  208. package/dist/record/render-context.d.ts +22 -3
  209. package/dist/record/render-context.d.ts.map +1 -1
  210. package/dist/record/shadow-pass.d.ts +2 -0
  211. package/dist/record/shadow-pass.d.ts.map +1 -1
  212. package/dist/record/skybox-post-pass.d.ts +1 -1
  213. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  214. package/dist/record/typed-frame-graph.d.ts +22 -2
  215. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  216. package/dist/record/view-ubo.d.ts +11 -3
  217. package/dist/record/view-ubo.d.ts.map +1 -1
  218. package/dist/record/volume-params.d.ts +9 -0
  219. package/dist/record/volume-params.d.ts.map +1 -0
  220. package/dist/recovery/render-system-candidate.d.ts +10 -0
  221. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  222. package/dist/reflection/record-owner.d.ts.map +1 -1
  223. package/dist/render-contract.d.ts +42 -4
  224. package/dist/render-contract.d.ts.map +1 -1
  225. package/dist/render-graph-primitives.d.ts.map +1 -1
  226. package/dist/render-pipeline.d.ts +103 -2
  227. package/dist/render-pipeline.d.ts.map +1 -1
  228. package/dist/render-system-extract-tail.d.ts +3 -2
  229. package/dist/render-system-extract-tail.d.ts.map +1 -1
  230. package/dist/render-system-extract.d.ts +16 -9
  231. package/dist/render-system-extract.d.ts.map +1 -1
  232. package/dist/render-system-presentation.d.ts +23 -0
  233. package/dist/render-system-presentation.d.ts.map +1 -0
  234. package/dist/render-system-producer-roots.d.ts +10 -0
  235. package/dist/render-system-producer-roots.d.ts.map +1 -0
  236. package/dist/render-system-residency.d.ts +12 -0
  237. package/dist/render-system-residency.d.ts.map +1 -0
  238. package/dist/render-system.d.ts +19 -14
  239. package/dist/render-system.d.ts.map +1 -1
  240. package/dist/scene/probe-blend.d.ts +27 -0
  241. package/dist/scene/probe-blend.d.ts.map +1 -1
  242. package/dist/scene/render-scene.d.ts +12 -5
  243. package/dist/scene/render-scene.d.ts.map +1 -1
  244. package/dist/ssao-buffers.d.ts +9 -0
  245. package/dist/ssao-buffers.d.ts.map +1 -1
  246. package/dist/surface/dynamic-input.d.ts +133 -0
  247. package/dist/surface/dynamic-input.d.ts.map +1 -0
  248. package/dist/surface/gpu-driven.d.ts +41 -0
  249. package/dist/surface/gpu-driven.d.ts.map +1 -0
  250. package/dist/surface/submission-observation.d.ts +136 -0
  251. package/dist/surface/submission-observation.d.ts.map +1 -0
  252. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  253. package/dist/temporal/gpu.d.ts +13 -1
  254. package/dist/temporal/gpu.d.ts.map +1 -1
  255. package/dist/temporal/index.mjs +5 -5
  256. package/dist/temporal/inspection.d.ts.map +1 -1
  257. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  258. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  259. package/dist/transmission/single-layer-medium.d.ts +89 -0
  260. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  261. package/dist/typed-render-graph-primitives.d.ts +14 -2
  262. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  263. package/dist/volume/component.d.ts +9 -4
  264. package/dist/volume/component.d.ts.map +1 -1
  265. package/dist/volume/extract.d.ts +1 -1
  266. package/dist/volume/extract.d.ts.map +1 -1
  267. package/dist/volume/inspection.d.ts +2 -0
  268. package/dist/volume/inspection.d.ts.map +1 -1
  269. package/dist/volume/passes.d.ts +4 -1
  270. package/dist/volume/passes.d.ts.map +1 -1
  271. package/dist/volume/resources.d.ts.map +1 -1
  272. package/package.json +22 -21
  273. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  274. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  275. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  276. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  277. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  278. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  279. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  280. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  281. package/src/__tests__/factory-contract.integration.test.ts +295 -45
  282. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  283. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  284. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  285. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  286. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  287. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  288. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  289. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  290. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
  291. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
  292. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  293. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  294. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  295. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  296. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  297. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  298. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  299. package/src/__tests__/gpu-scene.unit.test.ts +119 -9
  300. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  301. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  302. package/src/__tests__/instances-store.unit.test.ts +97 -305
  303. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  304. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  305. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  306. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  307. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  308. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  309. package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
  310. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  311. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  312. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  313. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  314. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  315. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  316. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  317. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  318. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  319. package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
  320. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  321. package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
  322. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  323. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  324. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  325. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  326. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
  327. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  328. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  329. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  330. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  331. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  332. package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
  333. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  334. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  335. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  336. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  337. package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
  338. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  339. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  340. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  341. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  342. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  343. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  344. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  345. package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
  346. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  347. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  348. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  349. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  350. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  351. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  352. package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
  353. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
  354. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  355. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  356. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  357. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
  358. package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
  359. package/src/assembly/backend-contract.ts +30 -0
  360. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  361. package/src/assembly/canvas-draw-guard.ts +11 -0
  362. package/src/assembly/cloud-shadow-entry.ts +22 -0
  363. package/src/assembly/dynamic-geometry-host.ts +65 -37
  364. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  365. package/src/assembly/factory.ts +2 -30
  366. package/src/assembly/host-contract.ts +3 -3
  367. package/src/assembly/material/assembly.ts +25 -13
  368. package/src/assembly/material/pipeline-helpers.ts +53 -12
  369. package/src/assembly/material-shader-policy.ts +55 -0
  370. package/src/assembly/material-shader-prewarm.ts +21 -10
  371. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  372. package/src/assembly/recovery/renderer-recover.ts +41 -18
  373. package/src/assembly/render-feature-post-processes.ts +54 -0
  374. package/src/assembly/renderer-facade.ts +4 -1
  375. package/src/assembly/renderer-feature-inspection.ts +8 -2
  376. package/src/assembly/renderer-frame-transaction.ts +23 -0
  377. package/src/assembly/renderer-inspection.ts +28 -0
  378. package/src/assembly/skin-palette-owner.ts +1 -1
  379. package/src/assembly/temporal-post-process-prewarm.ts +3 -3
  380. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  381. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  382. package/src/assembly/webgpu-ready.ts +115 -61
  383. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  384. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  385. package/src/assembly/webgpu-renderer.ts +214 -252
  386. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  387. package/src/barrel-distortion.ts +247 -0
  388. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  389. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  390. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  391. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  392. package/src/cloud/density.ts +600 -0
  393. package/src/cloud/extract.ts +110 -0
  394. package/src/cloud/feature.ts +1810 -0
  395. package/src/cloud/frame-context.ts +42 -0
  396. package/src/cloud/inspection.ts +130 -0
  397. package/src/cloud/optics.ts +285 -0
  398. package/src/cloud/parameters.ts +255 -0
  399. package/src/cloud/resources.ts +122 -0
  400. package/src/cloud/shadow.ts +167 -0
  401. package/src/cloud/temporal.ts +226 -0
  402. package/src/components/barrel-distortion.ts +46 -0
  403. package/src/components/cloud-layer.ts +47 -0
  404. package/src/components/index.ts +2 -0
  405. package/src/components/instances.ts +18 -45
  406. package/src/components/sprite-instances.ts +1 -1
  407. package/src/construct-renderer.ts +1 -1
  408. package/src/dynamic-geometry.ts +12 -1
  409. package/src/errors/cloud.ts +103 -0
  410. package/src/errors/render.ts +44 -7
  411. package/src/examples/single-layer-medium-surface.ts +59 -0
  412. package/src/extract/camera.ts +15 -1
  413. package/src/extract/contracts.ts +5 -0
  414. package/src/features/barrel-distortion.ts +172 -0
  415. package/src/features/error-reporting.ts +26 -6
  416. package/src/features/host.ts +115 -14
  417. package/src/features/plan.ts +8 -0
  418. package/src/features/render-graph-contribution.ts +9 -0
  419. package/src/features/render-graph-raster.ts +45 -3
  420. package/src/features/targets.ts +66 -0
  421. package/src/features/types.ts +47 -0
  422. package/src/fullscreen-post-process-pass.ts +41 -9
  423. package/src/gpu-dirty-ranges.ts +48 -0
  424. package/src/gpu-driven/batch-topology.ts +53 -0
  425. package/src/gpu-driven/material-artifacts.ts +33 -25
  426. package/src/gpu-driven/prepared-draw.ts +12 -4
  427. package/src/gpu-driven/production-raster-material.ts +1894 -0
  428. package/src/gpu-driven/production-raster.ts +825 -784
  429. package/src/gpu-driven/resource-allocation.ts +142 -0
  430. package/src/gpu-driven/shadow-ownership.ts +71 -0
  431. package/src/gpu-driven/view-gpu.ts +164 -10
  432. package/src/gpu-scene.ts +75 -84
  433. package/src/hdrp-buffers.ts +4 -3
  434. package/src/ibl/skylight-bind-group.ts +49 -7
  435. package/src/index.ts +196 -9
  436. package/src/inspection-types.ts +162 -0
  437. package/src/instance-buffer-cache.ts +4 -4
  438. package/src/instances.ts +149 -569
  439. package/src/pbr-pipeline.ts +219 -13
  440. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  441. package/src/pipeline/standard-forward-lane.ts +313 -57
  442. package/src/pipeline/standard-output/graph.ts +8 -2
  443. package/src/pipeline/standard-output/types.ts +12 -1
  444. package/src/pipeline/standard-pipeline.ts +291 -18
  445. package/src/pipeline/standard-post.ts +90 -24
  446. package/src/pipeline-builder.ts +1 -1
  447. package/src/plugin.ts +4 -0
  448. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  449. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  450. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  451. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  452. package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
  453. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  454. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  455. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  456. package/src/record/barrel-distortion-frame.ts +110 -0
  457. package/src/record/dynamic-geometry-consumption.ts +39 -9
  458. package/src/record/fold-buckets.ts +852 -0
  459. package/src/record/fold-instance-buffer.ts +51 -0
  460. package/src/record/frame-lighting.ts +36 -2
  461. package/src/record/frame-snapshot.ts +39 -3
  462. package/src/record/frame.ts +217 -1084
  463. package/src/record/gpu-draw-receipts.ts +38 -0
  464. package/src/record/gpu-timing.ts +48 -5
  465. package/src/record/main-pass-geometry.ts +251 -82
  466. package/src/record/main-pass-material.ts +66 -4
  467. package/src/record/main-pass-sprite-draws.ts +7 -65
  468. package/src/record/main-pass.ts +156 -20
  469. package/src/record/motion-blur-receipt.ts +91 -0
  470. package/src/record/probe-blend-buffer.ts +180 -0
  471. package/src/record/recovery-pipeline.ts +35 -3
  472. package/src/record/render-context.ts +39 -3
  473. package/src/record/shadow-pass.ts +30 -1
  474. package/src/record/skybox-post-pass.ts +28 -4
  475. package/src/record/typed-frame-graph.ts +770 -134
  476. package/src/record/view-ubo.ts +26 -3
  477. package/src/record/volume-params.ts +225 -0
  478. package/src/recovery/render-system-candidate.ts +235 -42
  479. package/src/reflection/record-owner.ts +7 -3
  480. package/src/render-contract.ts +48 -4
  481. package/src/render-graph-primitives.ts +7 -1
  482. package/src/render-pipeline.ts +130 -0
  483. package/src/render-system-extract-tail.ts +57 -61
  484. package/src/render-system-extract.ts +74 -76
  485. package/src/render-system-presentation.ts +56 -0
  486. package/src/render-system-producer-roots.ts +96 -0
  487. package/src/render-system-residency.ts +159 -0
  488. package/src/render-system.ts +347 -363
  489. package/src/scene/probe-blend.ts +96 -4
  490. package/src/scene/render-scene.ts +349 -280
  491. package/src/ssao-buffers.ts +95 -2
  492. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  493. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  494. package/src/surface/dynamic-input.ts +658 -0
  495. package/src/surface/gpu-driven.ts +147 -0
  496. package/src/surface/submission-observation.ts +517 -0
  497. package/src/systems/skin-palette-allocator.ts +4 -1
  498. package/src/temporal/gpu.ts +101 -17
  499. package/src/temporal/inspection.ts +7 -1
  500. package/src/temporal/standard-scene-data.ts +5 -0
  501. package/src/temporal/target-coverage-attachment.ts +4 -0
  502. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  503. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  504. package/src/transmission/single-layer-medium.ts +322 -0
  505. package/src/typed-render-graph-primitives.ts +70 -2
  506. package/src/volume/component.ts +10 -9
  507. package/src/volume/extract.ts +11 -5
  508. package/src/volume/inspection.ts +3 -0
  509. package/src/volume/passes.ts +87 -4
  510. package/src/volume/resources.ts +2 -1
  511. package/dist/chunk-3RTCJTH4.mjs.map +0 -1
  512. package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
  513. package/dist/chunk-NNQL7CB3.mjs +0 -1713
  514. package/dist/chunk-NNQL7CB3.mjs.map +0 -1
  515. package/dist/chunk-OO3DRYK7.mjs.map +0 -1
  516. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
  517. package/dist/chunk-PLAXOOX5.mjs.map +0 -1
  518. package/dist/chunk-VOZWDM3L.mjs.map +0 -1
@@ -253,7 +253,7 @@ export function resolveWebGPUVertexBufferLayouts(options: {
253
253
  : undefined,
254
254
  ),
255
255
  )
256
- : layoutKind === 'pbr-skin'
256
+ : layoutKind === 'pbr-skin' || layoutKind === 'gpu-driven-cluster-skin'
257
257
  ? toGpuVertexBufferLayouts(
258
258
  deriveVertexBufferLayout(
259
259
  meshAttributes ?? SKIN_VERTEX_ATTRIBUTE_MAP,
@@ -0,0 +1,247 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+ import {
3
+ type BarrelDistortionData,
4
+ validateBarrelDistortionParameters,
5
+ } from './components/barrel-distortion';
6
+ import { BarrelDistortionInvalidParameterError } from './errors/render';
7
+
8
+ /** One immutable mapping used by the output pass and display interaction. */
9
+ export interface BarrelDistortionMapping {
10
+ readonly width: number;
11
+ readonly height: number;
12
+ readonly aspect: number;
13
+ readonly strength: number;
14
+ readonly centerX: number;
15
+ readonly centerY: number;
16
+ readonly radiusSquared: number;
17
+ /** Unjittered camera facts from the submitted frame, when available. */
18
+ readonly camera?: BarrelDistortionCameraFrame;
19
+ }
20
+
21
+ /** Camera matrices paired with the mapping's accepted render frame. */
22
+ export interface BarrelDistortionCameraFrame {
23
+ readonly projection: 'perspective' | 'orthographic';
24
+ /** Far plane from the same submitted camera snapshot as the matrices. */
25
+ readonly far: number;
26
+ readonly viewMatrix: ArrayLike<number>;
27
+ readonly projectionMatrix: ArrayLike<number>;
28
+ }
29
+
30
+ // The equations below are evaluated in JavaScript's binary64 domain. Keep the
31
+ // public input domain strict, then absorb only a handful of binary64 ulps at
32
+ // the computed UV boundary. A real crop miss is orders of magnitude larger
33
+ // and remains rejected.
34
+ const COMPUTED_UV_BOUNDARY_EPSILON = Number.EPSILON * 16;
35
+
36
+ /**
37
+ * Detach and deep-freeze a mapping before it crosses a public or realm
38
+ * boundary. Worker structured cloning drops the source object's frozen state,
39
+ * so every receiving boundary must call this helper again.
40
+ */
41
+ export function freezeBarrelDistortionMapping(
42
+ mapping: BarrelDistortionMapping,
43
+ ): BarrelDistortionMapping {
44
+ const camera = mapping.camera;
45
+ return Object.freeze({
46
+ ...mapping,
47
+ ...(camera === undefined
48
+ ? {}
49
+ : {
50
+ camera: Object.freeze({
51
+ projection: camera.projection,
52
+ far: camera.far,
53
+ viewMatrix: Object.freeze(Array.from(camera.viewMatrix)),
54
+ projectionMatrix: Object.freeze(Array.from(camera.projectionMatrix)),
55
+ }),
56
+ }),
57
+ });
58
+ }
59
+
60
+ /** Attach the unjittered camera facts at the queue-submit publication boundary. */
61
+ export function attachBarrelDistortionCameraFrame(
62
+ mapping: BarrelDistortionMapping,
63
+ camera: BarrelDistortionCameraFrame,
64
+ ): BarrelDistortionMapping {
65
+ return freezeBarrelDistortionMapping({
66
+ ...mapping,
67
+ camera: {
68
+ projection: camera.projection,
69
+ far: camera.far,
70
+ viewMatrix: camera.viewMatrix,
71
+ projectionMatrix: camera.projectionMatrix,
72
+ },
73
+ });
74
+ }
75
+
76
+ /** Continuous output-viewport pixel coordinate. */
77
+ export interface DisplayPoint {
78
+ x: number;
79
+ y: number;
80
+ }
81
+
82
+ function invalidExtent(
83
+ field: 'width' | 'height',
84
+ value: number,
85
+ ): BarrelDistortionInvalidParameterError {
86
+ return new BarrelDistortionInvalidParameterError({
87
+ field,
88
+ value,
89
+ expected: 'finite and greater than 0',
90
+ });
91
+ }
92
+
93
+ /** Derive the output-sized mapping, including the auto-crop normalization. */
94
+ export function createBarrelDistortionMapping(
95
+ width: number,
96
+ height: number,
97
+ input: Partial<BarrelDistortionData> | undefined,
98
+ ): Result<BarrelDistortionMapping, BarrelDistortionInvalidParameterError> {
99
+ if (!Number.isFinite(width) || width <= 0) return err(invalidExtent('width', width));
100
+ if (!Number.isFinite(height) || height <= 0) return err(invalidExtent('height', height));
101
+ const parameters = validateBarrelDistortionParameters(input);
102
+ if (!parameters.ok) return parameters;
103
+ const { strength, centerX, centerY } = parameters.value;
104
+ const aspect = width / height;
105
+ const radiusSquared =
106
+ 4 *
107
+ (aspect * aspect * Math.max(centerX, 1 - centerX) ** 2 + Math.max(centerY, 1 - centerY) ** 2);
108
+ return ok(
109
+ Object.freeze({
110
+ width,
111
+ height,
112
+ aspect,
113
+ strength,
114
+ centerX,
115
+ centerY,
116
+ radiusSquared,
117
+ }),
118
+ );
119
+ }
120
+
121
+ function displayToSceneScale(mapping: BarrelDistortionMapping, ux: number, uy: number): number {
122
+ const px = 2 * mapping.aspect * (ux - mapping.centerX);
123
+ const py = 2 * (uy - mapping.centerY);
124
+ const t = (px * px + py * py) / mapping.radiusSquared;
125
+ return (1 - mapping.strength) / (1 - mapping.strength * t);
126
+ }
127
+
128
+ /**
129
+ * Accept a computed UV only when it is inside the unit interval or within a
130
+ * machine-precision boundary. Inputs are checked before the equation runs;
131
+ * this helper must never turn an out-of-domain caller input into a valid UV.
132
+ */
133
+ function normalizeComputedUv(out: DisplayPoint, x: number, y: number): boolean {
134
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
135
+ if (
136
+ x < -COMPUTED_UV_BOUNDARY_EPSILON ||
137
+ x > 1 + COMPUTED_UV_BOUNDARY_EPSILON ||
138
+ y < -COMPUTED_UV_BOUNDARY_EPSILON ||
139
+ y > 1 + COMPUTED_UV_BOUNDARY_EPSILON
140
+ ) {
141
+ return false;
142
+ }
143
+ out.x = Math.min(1, Math.max(0, x));
144
+ out.y = Math.min(1, Math.max(0, y));
145
+ return true;
146
+ }
147
+
148
+ /** Shared display UV -> source UV equation used by the GPU and CPU paths. */
149
+ export function mapDisplayUvToSceneUv(
150
+ out: DisplayPoint,
151
+ mapping: BarrelDistortionMapping,
152
+ ux: number,
153
+ uy: number,
154
+ ): boolean {
155
+ if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
156
+ return false;
157
+ if (mapping.strength === 0) {
158
+ out.x = ux;
159
+ out.y = uy;
160
+ return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
161
+ }
162
+ const scale = displayToSceneScale(mapping, ux, uy);
163
+ return normalizeComputedUv(
164
+ out,
165
+ mapping.centerX + (ux - mapping.centerX) * scale,
166
+ mapping.centerY + (uy - mapping.centerY) * scale,
167
+ );
168
+ }
169
+
170
+ /** Analytic source UV -> display UV inverse of the shared barrel model. */
171
+ export function mapSceneUvToDisplayUv(
172
+ out: DisplayPoint,
173
+ mapping: BarrelDistortionMapping,
174
+ ux: number,
175
+ uy: number,
176
+ ): boolean {
177
+ if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
178
+ return false;
179
+ if (mapping.strength === 0) {
180
+ out.x = ux;
181
+ out.y = uy;
182
+ return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
183
+ }
184
+ const px = 2 * mapping.aspect * (ux - mapping.centerX);
185
+ const py = 2 * (uy - mapping.centerY);
186
+ const sourceRadius = Math.hypot(px, py);
187
+ if (sourceRadius === 0) {
188
+ out.x = mapping.centerX;
189
+ out.y = mapping.centerY;
190
+ return true;
191
+ }
192
+ const discriminant =
193
+ (1 - mapping.strength) ** 2 +
194
+ (4 * mapping.strength * sourceRadius * sourceRadius) / mapping.radiusSquared;
195
+ const displayRadius =
196
+ (2 * sourceRadius) / (1 - mapping.strength + Math.sqrt(Math.max(0, discriminant)));
197
+ const scale = displayRadius / sourceRadius;
198
+ return normalizeComputedUv(
199
+ out,
200
+ mapping.centerX + (ux - mapping.centerX) * scale,
201
+ mapping.centerY + (uy - mapping.centerY) * scale,
202
+ );
203
+ }
204
+
205
+ function validPixel(point: DisplayPoint): boolean {
206
+ return Number.isFinite(point.x) && Number.isFinite(point.y);
207
+ }
208
+
209
+ /** Map an output display pixel to the unwarped scene pixel. */
210
+ export function mapDisplayToScene(
211
+ out: DisplayPoint,
212
+ mapping: BarrelDistortionMapping,
213
+ displayX: number,
214
+ displayY: number,
215
+ ): boolean {
216
+ if (!Number.isFinite(displayX) || !Number.isFinite(displayY)) return false;
217
+ if (displayX < 0 || displayX > mapping.width || displayY < 0 || displayY > mapping.height) {
218
+ return false;
219
+ }
220
+ const scene = { x: 0, y: 0 };
221
+ if (!mapDisplayUvToSceneUv(scene, mapping, displayX / mapping.width, displayY / mapping.height)) {
222
+ return false;
223
+ }
224
+ out.x = scene.x * mapping.width;
225
+ out.y = scene.y * mapping.height;
226
+ return validPixel(out);
227
+ }
228
+
229
+ /** Map an unwarped scene pixel to its visible output display pixel. */
230
+ export function mapSceneToDisplay(
231
+ out: DisplayPoint,
232
+ mapping: BarrelDistortionMapping,
233
+ sceneX: number,
234
+ sceneY: number,
235
+ ): boolean {
236
+ if (!Number.isFinite(sceneX) || !Number.isFinite(sceneY)) return false;
237
+ if (sceneX < 0 || sceneX > mapping.width || sceneY < 0 || sceneY > mapping.height) {
238
+ return false;
239
+ }
240
+ const display = { x: 0, y: 0 };
241
+ if (!mapSceneUvToDisplayUv(display, mapping, sceneX / mapping.width, sceneY / mapping.height)) {
242
+ return false;
243
+ }
244
+ out.x = display.x * mapping.width;
245
+ out.y = display.y * mapping.height;
246
+ return validPixel(out);
247
+ }
@@ -0,0 +1,173 @@
1
+ /// <reference types="@webgpu/types" />
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { evaluateCloudDensity } from '../density';
6
+ import { CLOUD_VIEW_FULLSCREEN_WGSL } from '../feature';
7
+ import { cloudIncidentLight } from '../optics';
8
+ import { validateCloudLayer } from '../parameters';
9
+
10
+ // Execute both production WGSL definitions. A source-text comparison cannot
11
+ // detect a hash constant mismatch or a different body/envelope interpretation.
12
+ describe('cloud density CPU/GPU parity', () => {
13
+ it('uses the same formation and density in cached-field generation and both analytic shaders', async () => {
14
+ const adapter = await navigator.gpu.requestAdapter();
15
+ expect(adapter).not.toBeNull();
16
+ if (!adapter) throw new Error('WebGPU adapter unavailable');
17
+ const device = await adapter.requestDevice();
18
+ const params = validateCloudLayer({
19
+ baseHeight: 0,
20
+ thickness: 1,
21
+ scale: 1,
22
+ seed: 42,
23
+ coverage: 0.8,
24
+ density: 1,
25
+ wind: [0, 0, 0],
26
+ });
27
+ if (!params.ok) throw params.error;
28
+ const shared = readFileSync(
29
+ new URL('../../../../shader/src/cloud.wgsl', import.meta.url),
30
+ 'utf8',
31
+ );
32
+ try {
33
+ for (const source of [CLOUD_VIEW_FULLSCREEN_WGSL, shared]) {
34
+ const helpers = source.slice(
35
+ source.indexOf('fn cloud_hash3('),
36
+ source.indexOf(source === shared ? 'fn cloud_density(' : 'fn cloud_analytic_density('),
37
+ );
38
+ const code = `
39
+ const CLOUD_VERTICAL_NOISE_CELLS: f32 = 2.0;
40
+ ${helpers}
41
+ @group(0) @binding(0) var<storage, read_write> output: array<f32>;
42
+ @compute @workgroup_size(64)
43
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
44
+ let p = vec3<f32>(f32(id.x % 13u) / 13.0, (f32(id.x % 11u) + 0.5) / 11.0, f32(id.x % 17u) / 17.0);
45
+ output[id.x] = cloud_compose_density(p.y, cloud_formation_field(p, 42u), 0.8);
46
+ }`;
47
+ const module = device.createShaderModule({ code });
48
+ const info = await module.getCompilationInfo();
49
+ expect(info.messages.filter((message) => message.type === 'error')).toEqual([]);
50
+ const pipeline = await device.createComputePipelineAsync({
51
+ layout: 'auto',
52
+ compute: { module, entryPoint: 'main' },
53
+ });
54
+ const output = device.createBuffer({
55
+ size: 512,
56
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
57
+ });
58
+ const readback = device.createBuffer({
59
+ size: 512,
60
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
61
+ });
62
+ try {
63
+ const encoder = device.createCommandEncoder();
64
+ const pass = encoder.beginComputePass();
65
+ pass.setPipeline(pipeline);
66
+ pass.setBindGroup(
67
+ 0,
68
+ device.createBindGroup({
69
+ layout: pipeline.getBindGroupLayout(0),
70
+ entries: [{ binding: 0, resource: { buffer: output } }],
71
+ }),
72
+ );
73
+ pass.dispatchWorkgroups(2);
74
+ pass.end();
75
+ encoder.copyBufferToBuffer(output, 0, readback, 0, 512);
76
+ device.queue.submit([encoder.finish()]);
77
+ await readback.mapAsync(GPUMapMode.READ);
78
+ const values = new Float32Array(readback.getMappedRange());
79
+ let maxError = 0;
80
+ for (let i = 0; i < 128; i++) {
81
+ const point = [(i % 13) / 13, ((i % 11) + 0.5) / 11, (i % 17) / 17];
82
+ maxError = Math.max(
83
+ maxError,
84
+ Math.abs(
85
+ (values[i] ?? Number.NaN) - evaluateCloudDensity(params.value, point).density,
86
+ ),
87
+ );
88
+ }
89
+ expect(maxError).toBeLessThan(0.001);
90
+ readback.unmap();
91
+ } finally {
92
+ output.destroy();
93
+ readback.destroy();
94
+ }
95
+ }
96
+ } finally {
97
+ device.destroy();
98
+ }
99
+ });
100
+ });
101
+
102
+ describe('cloud incident light CPU/GPU parity', () => {
103
+ it('uses the volume light basis and the same phase and fill in the CPU oracle', async () => {
104
+ const adapter = await navigator.gpu.requestAdapter();
105
+ if (!adapter) throw new Error('WebGPU adapter unavailable');
106
+ const device = await adapter.requestDevice();
107
+ const output = device.createBuffer({
108
+ size: 1024,
109
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
110
+ });
111
+ const readback = device.createBuffer({
112
+ size: 1024,
113
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
114
+ });
115
+ try {
116
+ const helpers = CLOUD_VIEW_FULLSCREEN_WGSL.slice(
117
+ CLOUD_VIEW_FULLSCREEN_WGSL.indexOf('fn cloud_henyey_greenstein('),
118
+ CLOUD_VIEW_FULLSCREEN_WGSL.indexOf('fn cloud_ray_jitter('),
119
+ );
120
+ const module = device.createShaderModule({
121
+ code: `
122
+ struct Light { sunRadiance: vec4<f32> };
123
+ var<private> cloud: Light = Light(vec4<f32>(1.0));
124
+ ${helpers}
125
+ @group(0) @binding(0) var<storage, read_write> output: array<vec4<f32>>;
126
+ @compute @workgroup_size(64)
127
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
128
+ let t = f32(id.x % 8u) / 7.0;
129
+ let angle = f32((id.x / 8u) % 4u) / 1.5 - 1.0;
130
+ let height = f32(id.x / 32u);
131
+ output[id.x] = vec4<f32>(cloud_incident_light(t, angle, height), 1.0);
132
+ }`,
133
+ });
134
+ const pipeline = await device.createComputePipelineAsync({
135
+ layout: 'auto',
136
+ compute: { module, entryPoint: 'main' },
137
+ });
138
+ const encoder = device.createCommandEncoder();
139
+ const pass = encoder.beginComputePass();
140
+ pass.setPipeline(pipeline);
141
+ pass.setBindGroup(
142
+ 0,
143
+ device.createBindGroup({
144
+ layout: pipeline.getBindGroupLayout(0),
145
+ entries: [{ binding: 0, resource: { buffer: output } }],
146
+ }),
147
+ );
148
+ pass.dispatchWorkgroups(1);
149
+ pass.end();
150
+ encoder.copyBufferToBuffer(output, 0, readback, 0, 1024);
151
+ device.queue.submit([encoder.finish()]);
152
+ await readback.mapAsync(GPUMapMode.READ);
153
+ const values = new Float32Array(readback.getMappedRange());
154
+ for (let i = 0; i < 64; i++) {
155
+ const expected = cloudIncidentLight(
156
+ (i % 8) / 7,
157
+ (Math.floor(i / 8) % 4) / 1.5 - 1,
158
+ Math.floor(i / 32),
159
+ );
160
+ for (const channel of [0, 1, 2] as const)
161
+ expect(values[i * 4 + channel]).toBeCloseTo(expected[channel], 4);
162
+ }
163
+ readback.unmap();
164
+ // An unoccluded sideways ray must retain direct and multiple scattering
165
+ // in the shared light units; an accidental extra 1/(4*pi) dims this badly.
166
+ expect(cloudIncidentLight(1, 0, 1)[0]).toBeGreaterThan(1);
167
+ } finally {
168
+ output.destroy();
169
+ readback.destroy();
170
+ device.destroy();
171
+ }
172
+ });
173
+ });
@@ -0,0 +1,265 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { VIEW_UNIFORM_BYTES } from '../../record/view-ubo';
3
+ import {
4
+ CLOUD_HISTORY_FULLSCREEN_WGSL,
5
+ CLOUD_VIEW_FULLSCREEN_WGSL,
6
+ CLOUD_VIEW_PARAMS_BYTES,
7
+ } from '../feature';
8
+
9
+ const WIDTH = 4;
10
+ const HEIGHT = 4;
11
+ const TEXTURE_BINDING = 0x04;
12
+ const RENDER_ATTACHMENT = 0x10;
13
+ const COPY_SRC = 0x01;
14
+ const COPY_DST = 0x02;
15
+ const MAP_READ = 0x0001;
16
+ const BUFFER_COPY_DST = 0x0008;
17
+ const STORAGE = 0x0080;
18
+ const UNIFORM = 0x0040;
19
+ const MAP_MODE_READ = 0x0001;
20
+
21
+ interface Readback {
22
+ readonly rows: readonly number[];
23
+ readonly center: number;
24
+ }
25
+
26
+ function identity(target: Float32Array, offset: number): void {
27
+ target[offset] = 1;
28
+ target[offset + 5] = 1;
29
+ target[offset + 10] = 1;
30
+ target[offset + 15] = 1;
31
+ }
32
+
33
+ function createSource(device: GPUDevice): GPUTexture {
34
+ const texture = device.createTexture({
35
+ size: [WIDTH, HEIGHT],
36
+ format: 'rgba8unorm',
37
+ usage: TEXTURE_BINDING | COPY_DST,
38
+ });
39
+ const bytes = new Uint8Array(WIDTH * HEIGHT * 4);
40
+ for (let y = 0; y < HEIGHT; y += 1) {
41
+ for (let x = 0; x < WIDTH; x += 1) {
42
+ const offset = (y * WIDTH + x) * 4;
43
+ bytes[offset] = 20 + y * 60;
44
+ bytes[offset + 1] = 7 + x * 31;
45
+ bytes[offset + 2] = 240 - y * 40;
46
+ bytes[offset + 3] = 255;
47
+ }
48
+ }
49
+ device.queue.writeTexture(
50
+ { texture },
51
+ bytes,
52
+ { bytesPerRow: WIDTH * 4 },
53
+ { width: WIDTH, height: HEIGHT, depthOrArrayLayers: 1 },
54
+ );
55
+ return texture;
56
+ }
57
+
58
+ function createDepth(device: GPUDevice): GPUTexture {
59
+ return device.createTexture({
60
+ size: [WIDTH, HEIGHT],
61
+ format: 'depth32float',
62
+ usage: TEXTURE_BINDING | RENDER_ATTACHMENT,
63
+ });
64
+ }
65
+
66
+ function createViewUniforms(): Float32Array {
67
+ const data = new Float32Array(VIEW_UNIFORM_BYTES / Float32Array.BYTES_PER_ELEMENT);
68
+ identity(data, 0);
69
+ identity(data, 44);
70
+ data[16] = 0;
71
+ data[17] = -1;
72
+ data[18] = 0;
73
+ data[20] = 1;
74
+ data[21] = 1;
75
+ data[22] = 1;
76
+ data[24] = 0;
77
+ data[25] = 0;
78
+ data[26] = 0;
79
+ return data;
80
+ }
81
+
82
+ function createCloudUniforms(density: number): Float32Array {
83
+ const data = new Float32Array(24);
84
+ data[0] = -1;
85
+ data[1] = 2;
86
+ data[2] = 1;
87
+ data[3] = density;
88
+ data[4] = 1;
89
+ data[5] = 0;
90
+ data[6] = 10;
91
+ data[7] = 7;
92
+ data[8] = 0;
93
+ data[9] = 0;
94
+ data[10] = 0;
95
+ data[12] = 0;
96
+ data[13] = -1;
97
+ data[14] = 0;
98
+ data[16] = 1;
99
+ data[17] = 1;
100
+ data[18] = 1;
101
+ const integers = new Uint32Array(data.buffer);
102
+ integers[20] = 4;
103
+ integers[21] = 8;
104
+ integers[22] = 8;
105
+ return data;
106
+ }
107
+
108
+ async function render(
109
+ device: GPUDevice,
110
+ pipeline: GPURenderPipeline,
111
+ depthValue: number,
112
+ density: number,
113
+ ): Promise<Readback> {
114
+ const source = createSource(device);
115
+ const depth = createDepth(device);
116
+ const output = device.createTexture({
117
+ size: [WIDTH, HEIGHT],
118
+ format: 'rgba8unorm',
119
+ usage: RENDER_ATTACHMENT | COPY_SRC,
120
+ });
121
+ const viewBuffer = device.createBuffer({
122
+ size: VIEW_UNIFORM_BYTES,
123
+ usage: UNIFORM | BUFFER_COPY_DST,
124
+ });
125
+ const cloudBuffer = device.createBuffer({
126
+ size: CLOUD_VIEW_PARAMS_BYTES,
127
+ usage: UNIFORM | BUFFER_COPY_DST,
128
+ });
129
+ // The production cache contains three R8 formation planes (weather, body,
130
+ // erosion), packed into 48 u32 lanes for the 4^3 fixture. Keep the Dawn
131
+ // fixture on the same storage extent so the density shader never reads
132
+ // beyond the test resource when the cloud is enabled.
133
+ const densityBuffer = device.createBuffer({ size: 192, usage: STORAGE | BUFFER_COPY_DST });
134
+ const previousRadiance = device.createTexture({
135
+ size: [WIDTH, HEIGHT],
136
+ format: 'rgba16float',
137
+ usage: TEXTURE_BINDING | RENDER_ATTACHMENT,
138
+ });
139
+ const previousTransmittance = device.createTexture({
140
+ size: [WIDTH, HEIGHT],
141
+ format: 'rgba16float',
142
+ usage: TEXTURE_BINDING | RENDER_ATTACHMENT,
143
+ });
144
+ const previousDepth = device.createTexture({
145
+ size: [WIDTH, HEIGHT],
146
+ format: 'rgba16float',
147
+ usage: TEXTURE_BINDING | RENDER_ATTACHMENT,
148
+ });
149
+ const readback = device.createBuffer({
150
+ size: 256 * HEIGHT,
151
+ usage: MAP_READ | BUFFER_COPY_DST,
152
+ });
153
+ device.queue.writeBuffer(viewBuffer, 0, createViewUniforms());
154
+ device.queue.writeBuffer(cloudBuffer, 0, createCloudUniforms(density));
155
+ device.queue.writeBuffer(
156
+ densityBuffer,
157
+ 0,
158
+ new Uint32Array(48).fill(density === 0 ? 0 : 0xffffffff),
159
+ );
160
+ const group0 = device.createBindGroup({
161
+ layout: pipeline.getBindGroupLayout(0),
162
+ entries: [{ binding: 0, resource: { buffer: viewBuffer } }],
163
+ });
164
+ const group1 = device.createBindGroup({
165
+ layout: pipeline.getBindGroupLayout(1),
166
+ entries: [
167
+ { binding: 0, resource: source.createView() },
168
+ { binding: 1, resource: device.createSampler() },
169
+ { binding: 2, resource: { buffer: cloudBuffer } },
170
+ { binding: 3, resource: depth.createView() },
171
+ { binding: 4, resource: device.createSampler() },
172
+ { binding: 5, resource: previousRadiance.createView() },
173
+ { binding: 6, resource: previousTransmittance.createView() },
174
+ { binding: 7, resource: previousDepth.createView() },
175
+ { binding: 8, resource: { buffer: densityBuffer } },
176
+ ],
177
+ });
178
+ const encoder = device.createCommandEncoder();
179
+ const depthPass = encoder.beginRenderPass({
180
+ colorAttachments: [],
181
+ depthStencilAttachment: {
182
+ view: depth.createView(),
183
+ depthClearValue: depthValue,
184
+ depthLoadOp: 'clear',
185
+ depthStoreOp: 'store',
186
+ },
187
+ });
188
+ depthPass.end();
189
+ const pass = encoder.beginRenderPass({
190
+ colorAttachments: [
191
+ {
192
+ view: output.createView(),
193
+ clearValue: [0, 0, 0, 1],
194
+ loadOp: 'clear',
195
+ storeOp: 'store',
196
+ },
197
+ ],
198
+ });
199
+ pass.setPipeline(pipeline);
200
+ pass.setBindGroup(0, group0);
201
+ pass.setBindGroup(1, group1);
202
+ pass.draw(3);
203
+ pass.end();
204
+ encoder.copyTextureToBuffer(
205
+ { texture: output },
206
+ { buffer: readback, bytesPerRow: 256, rowsPerImage: HEIGHT },
207
+ { width: WIDTH, height: HEIGHT, depthOrArrayLayers: 1 },
208
+ );
209
+ device.queue.submit([encoder.finish()]);
210
+ await device.queue.onSubmittedWorkDone();
211
+ await readback.mapAsync(MAP_MODE_READ);
212
+ const bytes = new Uint8Array(readback.getMappedRange().slice(0));
213
+ readback.unmap();
214
+ source.destroy();
215
+ depth.destroy();
216
+ output.destroy();
217
+ viewBuffer.destroy();
218
+ cloudBuffer.destroy();
219
+ densityBuffer.destroy();
220
+ previousRadiance.destroy();
221
+ previousTransmittance.destroy();
222
+ previousDepth.destroy();
223
+ readback.destroy();
224
+ return {
225
+ rows: Array.from({ length: HEIGHT }, (_, y) => bytes[y * 256] ?? 0),
226
+ center: bytes[(HEIGHT >> 1) * 256 + (WIDTH >> 1) * 4] ?? 0,
227
+ };
228
+ }
229
+
230
+ describe('CloudLayer framebuffer Y convention on Dawn', () => {
231
+ it('keeps asymmetric color rows and depth-cut rays in one top-left convention', async () => {
232
+ const adapter = await navigator.gpu.requestAdapter();
233
+ if (adapter === null) return;
234
+ const device = await adapter.requestDevice();
235
+ const module = device.createShaderModule({ code: CLOUD_VIEW_FULLSCREEN_WGSL });
236
+ const pipeline = await device.createRenderPipelineAsync({
237
+ layout: 'auto',
238
+ vertex: { module, entryPoint: 'vs_main' },
239
+ fragment: {
240
+ module,
241
+ entryPoint: 'fs_main',
242
+ targets: [{ format: 'rgba8unorm' }],
243
+ },
244
+ primitive: { topology: 'triangle-list' },
245
+ });
246
+ const historyModule = device.createShaderModule({ code: CLOUD_HISTORY_FULLSCREEN_WGSL });
247
+ await device.createRenderPipelineAsync({
248
+ layout: 'auto',
249
+ vertex: { module: historyModule, entryPoint: 'vs_main' },
250
+ fragment: {
251
+ module: historyModule,
252
+ entryPoint: 'fs_main',
253
+ targets: [{ format: 'rgba16float' }, { format: 'rgba16float' }],
254
+ },
255
+ primitive: { topology: 'triangle-list' },
256
+ });
257
+ const noCloud = await render(device, pipeline, 1, 0);
258
+ expect(noCloud.rows).toEqual([20, 80, 140, 200]);
259
+ expect(noCloud.center).toBe(140);
260
+ const near = await render(device, pipeline, 0.1, 1);
261
+ const far = await render(device, pipeline, 1, 1);
262
+ expect(Math.abs(near.center - far.center)).toBeGreaterThan(2);
263
+ device.destroy();
264
+ });
265
+ });