@photivra/engine 2.20.0 → 2.22.0

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 (275) hide show
  1. package/CHANGELOG.md +295 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +7 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +18 -0
  6. package/dist/capture/c2pa-embed.d.ts +116 -0
  7. package/dist/capture/c2pa-embed.js +493 -0
  8. package/dist/capture/c2pa-embed.js.map +1 -0
  9. package/dist/capture/dng-import.d.ts +73 -8
  10. package/dist/capture/dng-import.js +315 -80
  11. package/dist/capture/dng-import.js.map +1 -1
  12. package/dist/capture/dng-lens-correction-opcodes.d.ts +2 -2
  13. package/dist/capture/dng-lens-correction-opcodes.js +2 -2
  14. package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
  15. package/dist/capture/dng-opcodes.d.ts +96 -0
  16. package/dist/capture/dng-opcodes.js +391 -0
  17. package/dist/capture/dng-opcodes.js.map +1 -0
  18. package/dist/capture/dng-profile-maps.d.ts +99 -0
  19. package/dist/capture/dng-profile-maps.js +381 -0
  20. package/dist/capture/dng-profile-maps.js.map +1 -0
  21. package/dist/capture/dng-stage-image.d.ts +23 -0
  22. package/dist/capture/dng-stage-image.js +65 -0
  23. package/dist/capture/dng-stage-image.js.map +1 -0
  24. package/dist/capture/exposure-build-up-stage.d.ts +16 -0
  25. package/dist/capture/exposure-build-up-stage.js +4 -0
  26. package/dist/capture/exposure-build-up-stage.js.map +1 -0
  27. package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
  28. package/dist/capture/jpeg-xl-tiles.js +151 -0
  29. package/dist/capture/jpeg-xl-tiles.js.map +1 -0
  30. package/dist/capture/lossy-jpeg.d.ts +17 -0
  31. package/dist/capture/lossy-jpeg.js +369 -0
  32. package/dist/capture/lossy-jpeg.js.map +1 -0
  33. package/dist/capture/native-environment-lean.d.ts +2 -1
  34. package/dist/capture/native-environment-lean.js +41 -73
  35. package/dist/capture/native-environment-lean.js.map +1 -1
  36. package/dist/capture/native-environment-tabulated.js +4 -3
  37. package/dist/capture/native-environment-tabulated.js.map +1 -1
  38. package/dist/capture/native-raw-development.d.ts +2 -1
  39. package/dist/capture/native-raw-development.js.map +1 -1
  40. package/dist/capture/native-raw.d.ts +3 -2
  41. package/dist/capture/native-raw.js +5 -4
  42. package/dist/capture/native-raw.js.map +1 -1
  43. package/dist/capture/sensor-raw-producer.d.ts +4 -1
  44. package/dist/capture/sensor-raw-producer.js +12 -3
  45. package/dist/capture/sensor-raw-producer.js.map +1 -1
  46. package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
  47. package/dist/color/neighbor-highlight-reconstruction.js +234 -0
  48. package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
  49. package/dist/color/sensor-color-development.d.ts +5 -0
  50. package/dist/color/sensor-color-development.js +7 -1
  51. package/dist/color/sensor-color-development.js.map +1 -1
  52. package/dist/color/spectral-spikiness.d.ts +11 -0
  53. package/dist/color/spectral-spikiness.js +23 -0
  54. package/dist/color/spectral-spikiness.js.map +1 -0
  55. package/dist/core/execution-error.d.ts +21 -0
  56. package/dist/core/execution-error.js +12 -0
  57. package/dist/core/execution-error.js.map +1 -0
  58. package/dist/core/version.d.ts +1 -1
  59. package/dist/core/version.js +1 -1
  60. package/dist/core/version.js.map +1 -1
  61. package/dist/equipment/generic-lens-catalogue.d.ts +3 -2
  62. package/dist/equipment/generic-lens-catalogue.js +18 -5
  63. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  64. package/dist/equipment/generic-tier-assets.d.ts +32 -0
  65. package/dist/equipment/generic-tier-assets.js +16 -11
  66. package/dist/equipment/generic-tier-assets.js.map +1 -1
  67. package/dist/equipment/sensor-profile-body.d.ts +78 -0
  68. package/dist/equipment/sensor-profile-body.js +77 -0
  69. package/dist/equipment/sensor-profile-body.js.map +1 -0
  70. package/dist/equipment/sensor-profiles.d.ts +8 -0
  71. package/dist/equipment/sensor-profiles.js +82 -0
  72. package/dist/equipment/sensor-profiles.js.map +1 -0
  73. package/dist/facade/catalogue-lens-signal.d.ts +28 -3
  74. package/dist/facade/catalogue-lens-signal.js +83 -78
  75. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  76. package/dist/facade/chart-radiance-batch.js +2 -1
  77. package/dist/facade/chart-radiance-batch.js.map +1 -1
  78. package/dist/facade/defocus-image-space-provider.d.ts +7 -0
  79. package/dist/facade/defocus-image-space-provider.js +64 -76
  80. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  81. package/dist/facade/diffraction-provider.d.ts +1 -1
  82. package/dist/facade/diffraction-provider.js +4 -4
  83. package/dist/facade/diffraction-provider.js.map +1 -1
  84. package/dist/facade/display-view-gpu-session.js +39 -5
  85. package/dist/facade/display-view-gpu-session.js.map +1 -1
  86. package/dist/facade/display-view-gpu.d.ts +19 -6
  87. package/dist/facade/display-view-gpu.js.map +1 -1
  88. package/dist/facade/display-view-lens-signal.js +25 -23
  89. package/dist/facade/display-view-lens-signal.js.map +1 -1
  90. package/dist/facade/display-view-normal.d.ts +15 -0
  91. package/dist/facade/display-view-normal.js +44 -0
  92. package/dist/facade/display-view-normal.js.map +1 -0
  93. package/dist/facade/display-view-rendering.d.ts +97 -0
  94. package/dist/facade/display-view-rendering.js +598 -0
  95. package/dist/facade/display-view-rendering.js.map +1 -0
  96. package/dist/facade/display-view.d.ts +37 -65
  97. package/dist/facade/display-view.js +152 -499
  98. package/dist/facade/display-view.js.map +1 -1
  99. package/dist/facade/expected-signal-setup.d.ts +3 -1
  100. package/dist/facade/expected-signal-setup.js +3 -3
  101. package/dist/facade/expected-signal-setup.js.map +1 -1
  102. package/dist/facade/expected-signal.d.ts +47 -14
  103. package/dist/facade/expected-signal.js +117 -112
  104. package/dist/facade/expected-signal.js.map +1 -1
  105. package/dist/facade/exposure-build-up.d.ts +70 -0
  106. package/dist/facade/exposure-build-up.js +164 -0
  107. package/dist/facade/exposure-build-up.js.map +1 -0
  108. package/dist/facade/fast-methods.d.ts +1 -1
  109. package/dist/facade/fast-methods.js +46 -9
  110. package/dist/facade/fast-methods.js.map +1 -1
  111. package/dist/facade/generic-lens-capture.d.ts +3 -3
  112. package/dist/facade/generic-lens-capture.js +3 -3
  113. package/dist/facade/generic-lens-capture.js.map +1 -1
  114. package/dist/facade/image-plane-enable.d.ts +7 -0
  115. package/dist/facade/image-plane-enable.js +17 -0
  116. package/dist/facade/image-plane-enable.js.map +1 -0
  117. package/dist/facade/image-plane-lattice-data.d.ts +6 -0
  118. package/dist/facade/image-plane-lattice-data.js +10 -0
  119. package/dist/facade/image-plane-lattice-data.js.map +1 -0
  120. package/dist/facade/image-plane-support.d.ts +33 -0
  121. package/dist/facade/image-plane-support.js +11 -0
  122. package/dist/facade/image-plane-support.js.map +1 -0
  123. package/dist/facade/image-plane.d.ts +36 -0
  124. package/dist/facade/image-plane.js +384 -0
  125. package/dist/facade/image-plane.js.map +1 -0
  126. package/dist/facade/mixed-illumination.d.ts +108 -0
  127. package/dist/facade/mixed-illumination.js +174 -0
  128. package/dist/facade/mixed-illumination.js.map +1 -0
  129. package/dist/facade/on-demand-kits.d.ts +14 -0
  130. package/dist/facade/on-demand-kits.js +36 -0
  131. package/dist/facade/on-demand-kits.js.map +1 -0
  132. package/dist/facade/opt-in-kit.d.ts +9 -0
  133. package/dist/facade/opt-in-kit.js +11 -0
  134. package/dist/facade/opt-in-kit.js.map +1 -0
  135. package/dist/facade/quality-kit.d.ts +21 -0
  136. package/dist/facade/quality-kit.js +23 -0
  137. package/dist/facade/quality-kit.js.map +1 -0
  138. package/dist/facade/quality-paths.d.ts +22 -0
  139. package/dist/facade/quality-paths.js +44 -0
  140. package/dist/facade/quality-paths.js.map +1 -0
  141. package/dist/facade/representative-light-sources.d.ts +72 -0
  142. package/dist/facade/representative-light-sources.js +124 -0
  143. package/dist/facade/representative-light-sources.js.map +1 -0
  144. package/dist/facade/scene-radiance.d.ts +145 -4
  145. package/dist/facade/scene-radiance.js +50 -13
  146. package/dist/facade/scene-radiance.js.map +1 -1
  147. package/dist/facade/simulate-capture.d.ts +68 -20
  148. package/dist/facade/simulate-capture.js +399 -146
  149. package/dist/facade/simulate-capture.js.map +1 -1
  150. package/dist/facade/simulate-display-view.d.ts +10 -12
  151. package/dist/facade/simulate-display-view.js +70 -53
  152. package/dist/facade/simulate-display-view.js.map +1 -1
  153. package/dist/gpu/chart-composite-gpu.d.ts +13 -1
  154. package/dist/gpu/chart-composite-gpu.js +48 -9
  155. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  156. package/dist/gpu/chart-radiance-gpu.js +4 -30
  157. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  158. package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
  159. package/dist/gpu/display-view-composite-material-module.js +39 -13
  160. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  161. package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
  162. package/dist/gpu/display-view-gpu-renderer.js +134 -0
  163. package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
  164. package/dist/gpu/display-view-image-material-module.d.ts +87 -0
  165. package/dist/gpu/display-view-image-material-module.js +361 -0
  166. package/dist/gpu/display-view-image-material-module.js.map +1 -0
  167. package/dist/gpu/display-view-image-quality.d.ts +87 -0
  168. package/dist/gpu/display-view-image-quality.js +625 -0
  169. package/dist/gpu/display-view-image-quality.js.map +1 -0
  170. package/dist/gpu/display-view-quality-gpu.d.ts +80 -65
  171. package/dist/gpu/display-view-quality-gpu.js +187 -445
  172. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  173. package/dist/gpu/display-view-speed-gpu.d.ts +67 -19
  174. package/dist/gpu/display-view-speed-gpu.js +181 -105
  175. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  176. package/dist/gpu/image-kernel-accelerator.js +3 -0
  177. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  178. package/dist/gpu/image-kernel-execution.d.ts +2 -13
  179. package/dist/gpu/image-kernel-execution.js +2 -10
  180. package/dist/gpu/image-kernel-execution.js.map +1 -1
  181. package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
  182. package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
  183. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  184. package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
  185. package/dist/gpu/wgsl-display-view-coupled.js +353 -0
  186. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  187. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +33 -24
  188. package/dist/gpu/wgsl-display-view-quality-kernel.js +166 -146
  189. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  190. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +19 -44
  191. package/dist/gpu/wgsl-display-view-speed-kernel.js +116 -18
  192. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  193. package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
  194. package/dist/gpu/wgsl-exact-sum.js +42 -0
  195. package/dist/gpu/wgsl-exact-sum.js.map +1 -0
  196. package/dist/index.d.ts +22 -6
  197. package/dist/index.js +16 -2
  198. package/dist/index.js.map +1 -1
  199. package/dist/motion/subject-motion.d.ts +66 -0
  200. package/dist/motion/subject-motion.js +52 -0
  201. package/dist/motion/subject-motion.js.map +1 -0
  202. package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
  203. package/dist/optics/generic-lens-lateral-color.js +77 -0
  204. package/dist/optics/generic-lens-lateral-color.js.map +1 -0
  205. package/dist/optics/generic-lens-psf.js +54 -46
  206. package/dist/optics/generic-lens-psf.js.map +1 -1
  207. package/dist/optics/lens-psf-profile.d.ts +7 -0
  208. package/dist/optics/lens-psf-profile.js +2 -1
  209. package/dist/optics/lens-psf-profile.js.map +1 -1
  210. package/dist/output/camera-channel-response.d.ts +50 -0
  211. package/dist/output/camera-channel-response.js +69 -0
  212. package/dist/output/camera-channel-response.js.map +1 -0
  213. package/dist/output/developed-lens-corrections.d.ts +19 -5
  214. package/dist/output/developed-lens-corrections.js +28 -9
  215. package/dist/output/developed-lens-corrections.js.map +1 -1
  216. package/dist/output/generic-lens-corrections.d.ts +41 -5
  217. package/dist/output/generic-lens-corrections.js +52 -8
  218. package/dist/output/generic-lens-corrections.js.map +1 -1
  219. package/dist/output/native-capture-sdr.js +5 -2
  220. package/dist/output/native-capture-sdr.js.map +1 -1
  221. package/dist/output/print-plan.d.ts +41 -5
  222. package/dist/output/print-plan.js +59 -14
  223. package/dist/output/print-plan.js.map +1 -1
  224. package/dist/output/sdr-rendering.d.ts +18 -1
  225. package/dist/output/sdr-rendering.js +39 -2
  226. package/dist/output/sdr-rendering.js.map +1 -1
  227. package/dist/sensor/binomial-sample.d.ts +3 -0
  228. package/dist/sensor/binomial-sample.js +86 -0
  229. package/dist/sensor/binomial-sample.js.map +1 -0
  230. package/dist/sensor/native-sensor-report.d.ts +10 -0
  231. package/dist/sensor/native-sensor-report.js +16 -0
  232. package/dist/sensor/native-sensor-report.js.map +1 -0
  233. package/dist/sensor/sensor-characteristics.d.ts +91 -13
  234. package/dist/sensor/sensor-characteristics.js +30 -15
  235. package/dist/sensor/sensor-characteristics.js.map +1 -1
  236. package/docs/AGENT_GUIDE.md +1 -0
  237. package/docs/API_REFERENCE.md +214 -4
  238. package/docs/QUICK_START.md +162 -8
  239. package/docs/USAGE.md +195 -22
  240. package/docs/api/capture-c2pa-embed.md +177 -0
  241. package/docs/api/capture-dng-import.md +86 -10
  242. package/docs/api/capture-dng-opcodes.md +93 -0
  243. package/docs/api/capture-dng-profile-maps.md +141 -0
  244. package/docs/api/capture-dng-stage-image.md +43 -0
  245. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  246. package/docs/api/capture-lossy-jpeg.md +41 -0
  247. package/docs/api/capture-native-raw-development.md +2 -1
  248. package/docs/api/core-execution-error.md +43 -0
  249. package/docs/api/core-version.md +1 -1
  250. package/docs/api/equipment-generic-lens-catalogue.md +10 -5
  251. package/docs/api/equipment-sensor-profile-body.md +64 -0
  252. package/docs/api/equipment-sensor-profiles.md +86 -0
  253. package/docs/api/exports.json +460 -5
  254. package/docs/api/facade-display-view-gpu.md +5 -2
  255. package/docs/api/facade-display-view.md +11 -15
  256. package/docs/api/facade-exposure-build-up.md +105 -0
  257. package/docs/api/facade-fast-methods.md +47 -10
  258. package/docs/api/facade-image-plane-enable.md +16 -0
  259. package/docs/api/facade-mixed-illumination.md +67 -0
  260. package/docs/api/facade-quality-paths.md +24 -0
  261. package/docs/api/facade-representative-light-sources.md +131 -0
  262. package/docs/api/facade-scene-radiance.md +54 -3
  263. package/docs/api/facade-simulate-capture.md +48 -11
  264. package/docs/api/facade-simulate-display-view.md +10 -12
  265. package/docs/api/gpu-image-kernel-execution.md +0 -36
  266. package/docs/api/motion-subject-motion.md +23 -0
  267. package/docs/api/optics-generic-lens-lateral-color.md +79 -0
  268. package/docs/api/output-camera-channel-response.md +55 -0
  269. package/docs/api/output-developed-lens-corrections.md +10 -1
  270. package/docs/api/output-generic-lens-corrections.md +40 -4
  271. package/docs/api/output-print-plan.md +35 -1
  272. package/docs/api/output-sdr-rendering.md +6 -1
  273. package/docs/api/sensor-sensor-characteristics.md +54 -2
  274. package/llms.txt +3 -0
  275. package/package.json +6 -5
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  /**
3
3
  * WGSL kernels of the display view's Quality path on the GPU (#479): the exact area mean with diffraction kernels, the
4
- * anti-aliasing filter's spots, the AHD candidates and the low-signal correction, in three passes over one band of display
4
+ * anti-aliasing filter's spots and the AHD candidates, in three passes over one band of display
5
5
  * rows (docs/WEBGPU.md, "Display view (Quality) on the GPU"; the float64 reference is `renderDisplayView` with
6
6
  * `createDisplayViewExpectedSignal`, facade/display-view.ts and facade/expected-signal.ts):
7
7
  *
@@ -21,9 +21,7 @@
21
21
  * camera-channel covariance), white balance and the highlight clip, the colour matrix, the tone curve and the clip to
22
22
  * [0, 1] with the skewness term, the candidates' average, the Stein covariance with the closed-form B_c, the independent
23
23
  * residual, AHD's choice variance ¼·Var(H − V), the flag for AHD's choice on the noise-free mosaic, which the reference takes and
24
- * the kernel does not (#457), and the low-signal correction interpolated trilinearly between the grid
25
- * states of a table the host uploaded (the states themselves are computed on the host, #447; the kernel records every
26
- * grid key it needs in a bitmap and flags a display pixel whose state the table lacks).
24
+ * the kernel does not (#457). 2.22.0: there is no low-signal correction (docs/DISPLAY_VIEW.md, "AHD at low signal").
27
25
  *
28
26
  * **Highlights (#533).** The site pass records whether each site is certainly below white, certainly at or above it, or
29
27
  * undecided (`clip_state`, from the site's mean and variance and their running bounds: certain only beyond twice the reference's
@@ -50,10 +48,13 @@
50
48
  * trigonometric built-in).
51
49
  */
52
50
  import { DISPLAY_VIEW_CLIP_MEAN_U, DISPLAY_VIEW_CLIP_VARIANCE_U, DISPLAY_VIEW_REINHARD_MEAN_U, DISPLAY_VIEW_REINHARD_VARIANCE_U } from "./wgsl-display-view-speed-kernel.js";
53
- import { AHD_KEY_FLOOR, AHD_KEY_LEVELS, AHD_KEY_OCTAVES, AHD_KEY_STEPS } from "../facade/display-view.js";
51
+ import { EXACT_SUM_WGSL } from "./wgsl-exact-sum.js";
52
+ import { DISPLAY_VIEW_COUPLED_STUB_WGSL } from "./wgsl-display-view-coupled-stub.js";
54
53
  export const DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE = 64;
55
54
  /** Largest footprint per axis (real pixels), as the Speed kernel. */
56
55
  export const DISPLAY_VIEW_QUALITY_MAXIMUM_PIXELS_PER_AXIS = 6;
56
+ /** Most spectral nodes the site passes integrate per site (2.21.0: the per-node partials are private arrays of this length). */
57
+ export const DISPLAY_VIEW_QUALITY_MAXIMUM_NODES = 96;
57
58
  /** Uniform parameter words (see `Params`): 56 words, 224 bytes. */
58
59
  export const DISPLAY_VIEW_QUALITY_PARAM_WORDS = 56;
59
60
  /** Words per lattice-group record: lx0, ly0, lw, lh, lbase, first tap (the blocked site pass, #492), cu (f32), cv (f32). */
@@ -89,12 +90,7 @@ export const DISPLAY_VIEW_QUALITY_FLAG = /* @__PURE__ */ Object.freeze({
89
90
  materialUncertain: 1,
90
91
  /** A site the pixel read lies outside the band's site window (a host geometry error; recompute). */
91
92
  siteOutsideWindow: 2,
92
- /** The correction needed a grid state the table does not hold (recompute, or compute the state and dispatch again). */
93
- stateMissing: 4,
94
- /** A sample's pixel variance lies within its bound of the correction threshold (recompute). */
95
- thresholdTie: 8,
96
- /** Informational: the pixel's expected value includes the low-signal correction. */
97
- corrected: 16,
93
+ // Bits 4, 8 and 16 (the low-signal correction's state, threshold and applied flags) are unused since 2.22.0.
98
94
  /**
99
95
  * #457: the reference takes AHD's choice on the noise-free mosaic (one candidate, or their average on a tie), which the kernel
100
96
  * does not compute; it renders the average. The pixel is flagged (recompute) when Σ_j a_j·½|h(H) − h(V)| of some component
@@ -104,7 +100,7 @@ export const DISPLAY_VIEW_QUALITY_FLAG = /* @__PURE__ */ Object.freeze({
104
100
  /** #533: a real pixel's saturation is undecided (the host computes the display pixel's count with the reference; its samples stay). */
105
101
  highlightUndecided: 64,
106
102
  /** The bits that require the host's recomputation. */
107
- recompute: 47
103
+ recompute: 35
108
104
  });
109
105
  /** #533: flag words per display pixel: the flags, then the 36-bit mask of saturated real pixels. */
110
106
  export const DISPLAY_VIEW_QUALITY_FLAG_WORDS = 3;
@@ -114,8 +110,6 @@ export const DISPLAY_VIEW_QUALITY_FLAG_WORDS = 3;
114
110
  * design tolerance, not a measurement: 2^-10 σ is a tenth of the 0.01 σ to which the float32 tests hold the GPU to the reference.
115
111
  */
116
112
  export const DISPLAY_VIEW_QUALITY_CHOICE_SIGMA = 2 ** -10;
117
- /** Words of the grid-key bitmap: one bit per grid key (AHD_KEY_LEVELS³ keys). */
118
- export const DISPLAY_VIEW_QUALITY_BITMAP_WORDS = Math.ceil(AHD_KEY_LEVELS ** 3 / 32);
119
113
  /**
120
114
  * A site whose uncertain lattice points (the material module's flag) could move its electrons by more than this fraction of
121
115
  * their value is flagged for host recomputation; a smaller uncertainty enters the site's bound instead (the tap weights times
@@ -133,7 +127,7 @@ const literal = (value) => {
133
127
  return /[.e]/u.test(text) ? text : `${text}.0`;
134
128
  };
135
129
  /** The parameter struct and the bound helpers shared by the three passes. */
136
- const COMMON_WGSL = /* wgsl */ `
130
+ export const DISPLAY_VIEW_QUALITY_COMMON_WGSL = /* wgsl */ `
137
131
  const E: f32 = ${E_LITERAL};
138
132
  const SLACK: f32 = 1e-14;
139
133
  const CLIP_REACH: f32 = 8.5;
@@ -153,16 +147,16 @@ count: u32, row0: u32, region_w: u32, region_x: u32,
153
147
  region_y: u32, margin: u32, native_w: u32, native_h: u32,
154
148
  columns: u32, tone: u32, reach: u32, candidates: u32,
155
149
  site_x0: i32, site_y0: i32, site_w: u32, site_h: u32,
156
- correction: u32, state_count: u32, taps: u32, groups: u32,
150
+ unused_16: u32, unused_17: u32, taps: u32, groups: u32,
157
151
  lattice_total: u32, gain_at_clip0: u32, gain_at_clip1: u32, gain_at_clip2: u32,
158
152
  k0: f32, k1: f32, k2: f32, reach2: f32,
159
153
  dark0: f32, dark1: f32, dark2: f32, clip_level: f32,
160
- c0_0: f32, c0_1: f32, c0_2: f32, threshold: f32,
154
+ c0_0: f32, c0_1: f32, c0_2: f32, unused_35: f32,
161
155
  g0: f32, g1: f32, g2: f32, scale_u: f32,
162
156
  m00: f32, m01: f32, m02: f32, m10: f32,
163
157
  m11: f32, m12: f32, m20: f32, m21: f32,
164
158
  m22: f32, scale_v: f32, site_gamma: f32, site_first: u32,
165
- site_tiny: f32, pad_f: f32, pad_g: f32, pad_h: f32,
159
+ site_tiny: f32, nodes: u32, pad_g: f32, pad_h: f32,
166
160
  };
167
161
  @group(0) @binding(0) var<uniform> params: Params;
168
162
 
@@ -344,7 +338,7 @@ return out;
344
338
  * records hold each group's lattice window (lx0, ly0, lw, lh), its first index in the lattice buffer and the point constants
345
339
  * c_u = 0.5 + f_x − W/2 and c_v = 0.5 + f_y − H/2 (exact in float32, checked by the host).
346
340
  */
347
- export const DISPLAY_VIEW_QUALITY_LATTICE_WGSL = /* wgsl */ `${COMMON_WGSL}
341
+ export const DISPLAY_VIEW_QUALITY_LATTICE_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}
348
342
  @group(0) @binding(1) var<storage, read> groups: array<u32>;
349
343
  @group(0) @binding(2) var<storage, read_write> lattice_out: array<u32>;
350
344
  @compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
@@ -371,7 +365,7 @@ lattice_out[i] = m | select(0u, 0x80000000u, material_uncertain);
371
365
  * The end of pass 2, shared by both site passes: from the site's electrons e and their absolute bound de, the readout moments
372
366
  * (mean k·e, variance k²e + c₀, third cumulant k³e), the clip at the white level and their bounds, written to site i.
373
367
  */
374
- const SITE_TAIL_WGSL = /* wgsl */ `
368
+ export const DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL = /* wgsl */ `
375
369
  fn write_site(i: u32, c: u32, e: f32, de: f32, flag: u32) {
376
370
  let kc = k_of(c);
377
371
  let mean = kc * e;
@@ -404,16 +398,38 @@ const siteBindingsWgsl = (tapRecords) => /* wgsl */ `
404
398
  @group(0) @binding(4) var<storage, read> lattice: array<u32>;
405
399
  @group(0) @binding(5) var<storage, read_write> sites: array<f32>;
406
400
  @group(0) @binding(6) var<storage, read> spread: array<f32>;
401
+ ${EXACT_SUM_WGSL}
402
+ alias Nodes = array<f32, ${DISPLAY_VIEW_QUALITY_MAXIMUM_NODES}>;
403
+ fn add_tap(p: ptr<function, Nodes>, c: u32, t: u32, m: u32) {
404
+ let N = params.nodes;
405
+ for (var n = 0u; n < N; n = n + 1u) { (*p)[n] = (*p)[n] + table[(c * params.taps + t) * N + n] * table[(3u * params.taps + m) * N + n]; }
406
+ }
407
+ fn add_nodes(p: ptr<function, Nodes>, q: ptr<function, Nodes>) { for (var n = 0u; n < params.nodes; n = n + 1u) { (*p)[n] = (*p)[n] + (*q)[n]; (*q)[n] = 0.0; } }
408
+ fn node_total(p: ptr<function, Nodes>, dark: f32) -> f32 {
409
+ var anchor = exact_exponent(dark);
410
+ for (var k = 0u; k < params.nodes; k = k + 1u) { if ((*p)[k] > 0.0) { anchor = max(anchor, exact_exponent((*p)[k])); } }
411
+ var sum: ExactSum;
412
+ sum.anchor = anchor;
413
+ for (var k = 0u; k < params.nodes; k = k + 1u) { exact_add(&sum, (*p)[k]); }
414
+ exact_add(&sum, dark);
415
+ return exact_round(&sum);
416
+ }
407
417
  `;
408
418
  /**
409
419
  * Pass 2: the expected electrons, readout moments and white clip of every native site of the band's window, with their bounds.
410
- * The site's electrons are Σ_t table[(m_t·3 + c)·T + t] + dark_c, every term non-negative and rounded once on the host, so the
411
- * sum carries (T + 3)·E relative (`params.site_gamma`, the host's value). An uncertain lattice point adds the tap's value spread
420
+ * 2.21.0 (signals integrated from the material spectrum per site): the site's electrons are Σ_t Σ_n C_c(t, n)·L_{m_t}(λ_n) +
421
+ * dark_c, from the channel's per-node kernel values and the spectrum of the material each tap reads (`table`: C at
422
+ * (c·T + t)·N + n, the spectra from 3·T·N), accumulated per node over the taps, then the node partials and the dark electrons
423
+ * summed in integer arithmetic and rounded once (EXACT_SUM_WGSL). Every term is non-negative and carries two host roundings (E/2
424
+ * each), its product (E) and at most T − 1 additions, then the final rounding (E), and the dark electrons their host rounding
425
+ * (E/2), so the sum carries at most (T + 2.5)·E, inside (T + 3)·E
426
+ * (`params.site_gamma`),
427
+ * plus `params.site_tiny`, the host's absolute bound on what a device that flushes below the float32 normal range can drop. An uncertain lattice point adds the tap's value spread
412
428
  * over the materials (`spread`, host float64) to the site's uncertainty, which enters its bounds, or flags the site when it
413
429
  * exceeds UNCERTAINTY_LIMIT of the electrons. One dispatch covers the `params.count` sites from `params.site_first` (#492: the
414
430
  * host splits a band's sites over several dispatches so that no dispatch gathers more than its tap budget).
415
431
  */
416
- export const DISPLAY_VIEW_QUALITY_SITES_WGSL = /* wgsl */ `${COMMON_WGSL}${siteBindingsWgsl(true)}${SITE_TAIL_WGSL}
432
+ export const DISPLAY_VIEW_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}${siteBindingsWgsl(true)}${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}
417
433
  @compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
418
434
  fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {
419
435
  let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;
@@ -424,44 +440,42 @@ let jj = i / params.site_w;
424
440
  let x = params.site_x0 + i32(ii);
425
441
  let y = params.site_y0 + i32(jj);
426
442
  let c = channel_of(x, y);
427
- var sum = 0.0;
428
443
  var uncertainty = 0.0;
429
444
  let T = params.taps;
445
+ var partial: Nodes;
430
446
  for (var t = 0u; t < T; t = t + 1u) {
431
447
  let tb = t * TAP;
432
448
  let g = taps[tb] * GROUP;
433
449
  let index = groups[g + 4u] + (jj + taps[tb + 2u]) * groups[g + 2u] + ii + taps[tb + 1u];
434
450
  let raw = lattice[index];
435
451
  if ((raw >> 31u) != 0u) { uncertainty = uncertainty + spread[c * T + t]; }
436
- sum = sum + table[((raw & 0x7fffffffu) * 3u + c) * T + t];
452
+ add_tap(&partial, c, t, raw & 0x7fffffffu);
437
453
  }
438
- let e = sum + dark_of(c);
454
+ let e = node_total(&partial, dark_of(c));
439
455
  let flag = select(0u, 1u, uncertainty > UNCERTAINTY_LIMIT * e);
440
- let de = params.site_gamma * e + uncertainty;
456
+ let de = params.site_gamma * e + uncertainty + params.site_tiny;
441
457
  write_site(i, c, e, de, flag);
442
458
  }
443
459
  `;
444
460
  /**
445
- * Pass 2 with the defocus disc (#492): the same site electrons from a factored table, summed in blocks.
461
+ * Pass 2 with the defocus disc (#492): the same site electrons, summed in blocks.
446
462
  *
447
- * - **Factored table.** A neutral material's value is its reflectance times the white's combined kernel (the reference forms it
448
- * so: reflectance[m]·neutral_c(t)), so the table holds one neutral row per channel and one row per spectral material, and
449
- * `materials` holds per material its row (0 for neutral, 1 + s for spectral material s) and its reflectance as float32 bits
450
- * (1 for a spectral material, an exact product). A scene of thousands of neutral materials (#491's composite: 4 143) then
451
- * needs 3·T values instead of 3·T per material.
463
+ * - **Spectral table.** The pass reads the tap-list pass's table (2.21.0): the channel's per-node kernel values C_c(t, n) and each
464
+ * material's spectrum, so the signal of each tap is integrated from its material's spectrum when the site is read.
452
465
  * - **Blocked sum.** Every group's taps form a dense cw × ch rectangle (the combined kernel's window, row-major from the group's
453
- * first tap, `groups[g + 5]`), so a site sums each row of a group into a row partial, the rows into a group partial and the
454
- * groups into the total. No tap records are read: the tap's lattice offset within its group is its (dx, dy).
466
+ * first tap, `groups[g + 5]`), so a site sums each row of a group into a row partial per node, the rows into a group partial
467
+ * and the groups into the total, then the nodes in integer arithmetic. No tap records are read: the tap's lattice offset
468
+ * within its group is its (dx, dy).
455
469
  *
456
- * Every term is non-negative and carries two host roundings (u each, u = E/2: the row value and the reflectance), one product
457
- * (E) and at most cw + ch + G additions, plus the dark electrons' addition, so the electrons carry (cw_max + ch_max + G + 3)·E
458
- * relative (`params.site_gamma`; recursive summation of the up to 4·(2·96 + 2)² = 150 544 taps of a combined radius of 96 would
459
- * carry (T + 3)·E ≈ 1.8 % instead). The uncertainty, summed the same way, is scaled by 1 + site_gamma, and `params.site_tiny`
460
- * (host float64, rounded up) bounds the terms below the float32 normal range, which a device may flush to zero.
470
+ * Every term is non-negative and carries two host roundings (u each, u = E/2: the kernel value and the spectrum), one product
471
+ * (E) and at most cw − 1 + ch − 1 + G − 1 inexact additions, then the integer sum of the node partials and the dark electrons,
472
+ * rounded once (E), and the dark electrons' host rounding (E/2), so the electrons carry at most (cw_max + ch_max + G + 0.5)·E, inside (cw_max + ch_max + G + 3)·E (`params.site_gamma`;
473
+ * recursive summation of the up to 4·(2·96 + 2)² = 150 544 taps of a combined radius of 96 would carry (T + 3)·E ≈ 1.8 %
474
+ * instead). The uncertainty, summed the same way, is scaled by 1 + site_gamma, and `params.site_tiny` (host float64, rounded up)
475
+ * bounds what a device that flushes results below the float32 normal range can drop (the tap-list pass adds it too).
461
476
  */
462
- export const DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL = /* wgsl */ `${COMMON_WGSL}${siteBindingsWgsl(false)}
463
- @group(0) @binding(7) var<storage, read> materials: array<u32>;
464
- ${SITE_TAIL_WGSL}
477
+ export const DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}${siteBindingsWgsl(false)}
478
+ ${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}
465
479
  @compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
466
480
  fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {
467
481
  let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;
@@ -473,7 +487,9 @@ let x = params.site_x0 + i32(ii);
473
487
  let y = params.site_y0 + i32(jj);
474
488
  let c = channel_of(x, y);
475
489
  let T = params.taps;
476
- var sum = 0.0;
490
+ var total: Nodes;
491
+ var group_sum: Nodes;
492
+ var row_sum: Nodes;
477
493
  var uncertainty = 0.0;
478
494
  for (var k = 0u; k < params.groups; k = k + 1u) {
479
495
  let g = k * GROUP;
@@ -482,57 +498,50 @@ let cw = lw - params.site_w + 1u;
482
498
  let ch = groups[g + 3u] - params.site_h + 1u;
483
499
  let lattice0 = groups[g + 4u] + jj * lw + ii;
484
500
  let tap0 = groups[g + 5u];
485
- var group_sum = 0.0;
486
501
  var group_uncertainty = 0.0;
487
502
  for (var dy = 0u; dy < ch; dy = dy + 1u) {
488
503
  let row = lattice0 + dy * lw;
489
504
  let row_tap = tap0 + dy * cw;
490
- var row_sum = 0.0;
491
505
  var row_uncertainty = 0.0;
492
506
  for (var dx = 0u; dx < cw; dx = dx + 1u) {
493
507
  let raw = lattice[row + dx];
494
508
  let t = row_tap + dx;
495
- let m = 2u * (raw & 0x7fffffffu);
496
509
  if ((raw >> 31u) != 0u) { row_uncertainty = row_uncertainty + spread[c * T + t]; }
497
- row_sum = row_sum + bitcast<f32>(materials[m + 1u]) * table[(materials[m] * 3u + c) * T + t];
510
+ add_tap(&row_sum, c, t, raw & 0x7fffffffu);
498
511
  }
499
- group_sum = group_sum + row_sum;
512
+ add_nodes(&group_sum, &row_sum);
500
513
  group_uncertainty = group_uncertainty + row_uncertainty;
501
514
  }
502
- sum = sum + group_sum;
515
+ add_nodes(&total, &group_sum);
503
516
  uncertainty = uncertainty + group_uncertainty;
504
517
  }
505
- let e = sum + dark_of(c);
518
+ let e = node_total(&total, dark_of(c));
506
519
  let flag = select(0u, 1u, uncertainty > UNCERTAINTY_LIMIT * e);
507
520
  let de = params.site_gamma * e + uncertainty * (1.0 + params.site_gamma) + params.site_tiny;
508
521
  write_site(i, c, e, de, flag);
509
522
  }
510
523
  `;
511
524
  /** Pass 3: the display pixels (see the module comment). */
512
- export const DISPLAY_VIEW_QUALITY_PIXELS_WGSL = /* wgsl */ `${COMMON_WGSL}
513
- const KEY_LEVELS: u32 = ${AHD_KEY_LEVELS}u;
514
- const KEY_STEPS: f32 = ${literal(AHD_KEY_STEPS)};
515
- const KEY_OCTAVES: f32 = ${literal(AHD_KEY_OCTAVES)};
516
- const KEY_FLOOR: f32 = ${literal(AHD_KEY_FLOOR)};
517
- const KEY_TOP: u32 = ${AHD_KEY_OCTAVES * AHD_KEY_STEPS - 1}u;
525
+ /*
526
+ * 2.22.0: in the luminance branch the nested loops read the colour matrix rows from one local array (`mm`) instead of calling
527
+ * `m_of` (a `switch`): SwiftShader's compiler ended the GPU process on the `switch` inside those loops (after about 44 s). The
528
+ * values are the same (docs/WEBGPU.md, "Coupled rendering steps").
529
+ */
530
+ export const DISPLAY_VIEW_QUALITY_PIXELS_BODY_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}
518
531
  const CAND_BASE: u32 = ${DISPLAY_VIEW_QUALITY_AUX.candidates}u;
519
532
  const CAND_STRIDE: u32 = ${CANDIDATE_PHASE_STRIDE}u;
520
533
  const FLAG_UNCERTAIN: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.materialUncertain}u;
521
534
  const FLAG_OUTSIDE: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.siteOutsideWindow}u;
522
- const FLAG_STATE: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.stateMissing}u;
523
- const FLAG_TIE: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.thresholdTie}u;
524
- const FLAG_CORRECTED: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.corrected}u;
525
535
  const FLAG_CHOICE: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.ahdChoice}u;
526
536
  const FLAG_HIGHLIGHT: u32 = ${DISPLAY_VIEW_QUALITY_FLAG.highlightUndecided}u;
527
537
  const CHOICE_SIGMA: f32 = ${literal(DISPLAY_VIEW_QUALITY_CHOICE_SIGMA)};
528
538
  @group(0) @binding(1) var<storage, read> aux: array<f32>;
529
539
  @group(0) @binding(2) var<storage, read> geometry: array<f32>;
530
540
  @group(0) @binding(3) var<storage, read> sites: array<f32>;
531
- @group(0) @binding(4) var<storage, read> state_keys: array<i32>;
532
- @group(0) @binding(5) var<storage, read> state_values: array<f32>;
533
- @group(0) @binding(6) var<storage, read_write> bitmap: array<atomic<u32>>;
534
- @group(0) @binding(7) var<storage, read_write> out: array<f32>;
535
- @group(0) @binding(8) var<storage, read_write> flags: array<u32>;
541
+ @group(0) @binding(4) var<storage, read_write> out: array<f32>;
542
+ @group(0) @binding(5) var<storage, read_write> flags: array<u32>;
543
+
544
+ fn cp_k(i: u32) -> f32 { return aux[${DISPLAY_VIEW_QUALITY_AUX.size}u + i]; }
536
545
 
537
546
  var<private> pixel_flags: u32;
538
547
  ${REINHARD_WGSL}
@@ -549,66 +558,6 @@ return o;
549
558
  }
550
559
 
551
560
  fn clip_bits(o: u32) -> u32 { return (bitcast<u32>(sites[o + 6u]) >> 1u) & 3u; }
552
-
553
- fn find_state(key: i32) -> u32 {
554
- var lo = 0u; var hi = params.state_count;
555
- while (lo < hi) {
556
- let mid = (lo + hi) / 2u;
557
- if (state_keys[mid] < key) { lo = mid + 1u; } else { hi = mid; }
558
- }
559
- if (lo < params.state_count && state_keys[lo] == key) { return lo; }
560
- return params.state_count;
561
- }
562
-
563
-
564
-
565
- fn flat_correction(m0: vec2<f32>, m1: vec2<f32>, m2: vec2<f32>, corr: ptr<function, array<vec2<f32>, 3>>) {
566
- var base: array<u32, 3>; var frac: array<f32, 3>; var dg: array<f32, 3>;
567
- for (var i = 0u; i < 3u; i = i + 1u) {
568
- var mi = m0; if (i == 1u) { mi = m1; } if (i == 2u) { mi = m2; }
569
- let clamped = clamp(mi.x, KEY_FLOOR, 1.0);
570
- let g = (log2(clamped) + KEY_OCTAVES) * KEY_STEPS;
571
- let b = min(u32(max(floor(g), 0.0)), KEY_TOP);
572
- base[i] = b; frac[i] = g - f32(b);
573
-
574
- var slope = 0.0; if (mi.x > KEY_FLOOR && mi.x < 1.0) { slope = 1.4426950408889634 * mi.y / clamped; }
575
- dg[i] = KEY_STEPS * (slope + 4.76837158203125e-7 + 36.0 * E) + 2.0 * E * (g + 1.0);
576
- }
577
- var values: array<f32, 24>;
578
- var missing = false;
579
- for (var corner = 0u; corner < 8u; corner = corner + 1u) {
580
- let i0 = base[0] + (corner & 1u); let i1 = base[1] + ((corner >> 1u) & 1u); let i2 = base[2] + ((corner >> 2u) & 1u);
581
- let key = i32((i0 * KEY_LEVELS + i1) * KEY_LEVELS + i2);
582
- var weight = 1.0;
583
- for (var i = 0u; i < 3u; i = i + 1u) { weight = weight * select(1.0 - frac[i], frac[i], ((corner >> i) & 1u) == 1u); }
584
- if (weight == 0.0) { for (var c = 0u; c < 3u; c = c + 1u) { values[corner * 3u + c] = 0.0; } continue; }
585
- atomicOr(&bitmap[u32(key) / 32u], 1u << (u32(key) % 32u));
586
- let at = find_state(key);
587
- if (at >= params.state_count) { missing = true; continue; }
588
- for (var c = 0u; c < 3u; c = c + 1u) { values[corner * 3u + c] = state_values[at * 3u + c]; }
589
- }
590
- if (missing) { pixel_flags = pixel_flags | FLAG_STATE; for (var c = 0u; c < 3u; c = c + 1u) { (*corr)[c] = vec2<f32>(0.0, 0.0); } return; }
591
- for (var c = 0u; c < 3u; c = c + 1u) {
592
- var sum = 0.0; var mag = 0.0;
593
- for (var corner = 0u; corner < 8u; corner = corner + 1u) {
594
- var weight = 1.0;
595
- for (var i = 0u; i < 3u; i = i + 1u) { weight = weight * select(1.0 - frac[i], frac[i], ((corner >> i) & 1u) == 1u); }
596
- sum = sum + weight * values[corner * 3u + c];
597
- mag = mag + abs(weight * values[corner * 3u + c]);
598
- }
599
-
600
- var slope_term = 0.0;
601
- for (var i = 0u; i < 3u; i = i + 1u) {
602
- var largest = 0.0;
603
- for (var corner = 0u; corner < 8u; corner = corner + 1u) {
604
- if (((corner >> i) & 1u) == 1u) { continue; }
605
- largest = max(largest, abs(values[(corner | (1u << i)) * 3u + c] - values[corner * 3u + c]));
606
- }
607
- slope_term = slope_term + dg[i] * largest;
608
- }
609
- (*corr)[c] = vec2<f32>(sum, slope_term + 12.0 * E * mag);
610
- }
611
- }
612
561
  @compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
613
562
  fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {
614
563
  let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;
@@ -625,6 +574,9 @@ let ny = bitcast<u32>(geometry[row + 1u]);
625
574
  let margin = i32(params.margin);
626
575
  let reinhard = params.tone == 1u;
627
576
  let count = params.candidates;
577
+
578
+ var cp_used = false;
579
+ var cp_a: array<vec2<f32>, 9>;
628
580
  let share = select(1.0, 0.5, count == 2u);
629
581
 
630
582
  let xb_even = geometry[col + 8u]; let xb_odd = geometry[col + 9u];
@@ -689,8 +641,6 @@ let bit = j * nx + ii;
689
641
  if (bit < 32u) { mask0 = mask0 | (1u << bit); } else { mask1 = mask1 | (1u << (bit - 32u)); }
690
642
  } else if ((clip_any & 2u) != 0u) { undecided = true; }
691
643
  var e_mean = array<vec2<f32>, 3>(vec2<f32>(0.0), vec2<f32>(0.0), vec2<f32>(0.0));
692
- var cm_average = array<vec2<f32>, 3>(vec2<f32>(0.0), vec2<f32>(0.0), vec2<f32>(0.0));
693
- var pixel_variance = vec2<f32>(0.0, 0.0);
694
644
  var bs: array<vec2<f32>, 3>; var hs: array<vec2<f32>, 3>;
695
645
  var hv: array<vec2<f32>, 3>;
696
646
 
@@ -717,17 +667,26 @@ cm[0] = b_add(cm[0], b_scale(sm, w0)); cm[1] = b_add(cm[1], b_scale(sm, w1)); cm
717
667
  C[0] = b_add(C[0], b_scale(sv, w0 * w0)); C[1] = b_add(C[1], b_scale(sv, w0 * w1)); C[2] = b_add(C[2], b_scale(sv, w0 * w2));
718
668
  C[3] = b_add(C[3], b_scale(sv, w1 * w1)); C[4] = b_add(C[4], b_scale(sv, w1 * w2)); C[5] = b_add(C[5], b_scale(sv, w2 * w2));
719
669
  }
720
- for (var c = 0u; c < 3u; c = c + 1u) { if (exact[c]) { cm[c].y = 0.0; } cm_average[c] = b_add(cm_average[c], b_scale(cm[c], share)); }
670
+ for (var c = 0u; c < 3u; c = c + 1u) { if (exact[c]) { cm[c].y = 0.0; } }
671
+
672
+
673
+ var cp_chain_run = false;
674
+ if (cp_on() && cp_reconstruct()) {
675
+ let reach = cp_reconstruct_reach(vec3<f32>(cm[0].x, cm[1].x, cm[2].x), vec3<f32>(C[0].x, C[3].x, C[5].x));
676
+ if (reach == 2u) { pixel_flags = pixel_flags | FLAG_UNCERTAIN; }
677
+ cp_chain_run = reach >= 1u;
678
+ }
679
+ let cp_unclipped = cp_on() && cp_reconstruct();
721
680
 
722
681
 
723
682
  var bm: array<vec2<f32>, 3>; var bv: array<vec2<f32>, 3>;
724
683
  for (var c = 0u; c < 3u; c = c + 1u) {
725
684
  let gc = b_rounded(g_of(c));
726
685
  var um = b_mul(gc, cm[c]);
727
- if (exact[c] && cm[c].x == 1.0 && gain_at_clip(c)) { um = vec2<f32>(params.clip_level, 0.0); }
686
+ if (exact[c] && cm[c].x == 1.0 && gain_at_clip(c) && !cp_unclipped) { um = vec2<f32>(params.clip_level, 0.0); }
728
687
  let uv = b_mul(b_mul(gc, gc), C[select(select(0u, 3u, c == 1u), 5u, c == 2u)]);
729
688
  let gap = params.clip_level - um.x;
730
- if (gap > 0.0 && gap * gap > params.reach2 * uv.x) { bm[c] = um; bv[c] = uv; bs[c] = gc; }
689
+ if (cp_unclipped || (gap > 0.0 && gap * gap > params.reach2 * uv.x)) { bm[c] = um; bv[c] = uv; bs[c] = gc; }
731
690
  else { let clipped = upper_clip(um, uv, params.clip_level); bm[c] = clipped.mean; bv[c] = clipped.variance; bs[c] = b_mul(gc, clipped.slope); }
732
691
  }
733
692
 
@@ -763,16 +722,75 @@ if (variance.x > 0.0) { skew = b_div(third, b_mul(variance, b_sqrt(variance)));
763
722
  moments = unit_clip(mean, variance, skew);
764
723
  }
765
724
  hm[comp] = moments.mean; hv[comp] = moments.variance; hs[comp] = moments.slope;
766
- h_cand[d * 3u + comp] = moments.mean;
767
- e_mean[comp] = b_add(e_mean[comp], b_scale(moments.mean, share));
768
- pixel_variance = vec2<f32>(max(pixel_variance.x, moments.variance.x), max(pixel_variance.y, moments.variance.y));
769
725
  }
770
726
 
771
727
  var an: array<vec2<f32>, 9>;
728
+ cp_used = false;
729
+ if (cp_on()) {
730
+ if (!cp_chain_run && cp_luminance()) {
731
+ var mu = vec3<f32>(0.0); var dmu = vec3<f32>(0.0); var dsv = vec3<f32>(0.0);
732
+ var srow = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));
733
+ var cc = array<f32, 9>(C[0].x, C[1].x, C[2].x, C[1].x, C[3].x, C[4].x, C[2].x, C[4].x, C[5].x);
734
+ var mm = array<vec3<f32>, 3>(vec3<f32>(params.m00, params.m01, params.m02), vec3<f32>(params.m10, params.m11, params.m12), vec3<f32>(params.m20, params.m21, params.m22));
735
+ for (var r = 0u; r < 3u; r = r + 1u) {
736
+ mu[r] = mm[r][0] * bm[0].x + mm[r][1] * bm[1].x + mm[r][2] * bm[2].x;
737
+ dmu[r] = abs(mm[r][0]) * bm[0].y + abs(mm[r][1]) * bm[1].y + abs(mm[r][2]) * bm[2].y;
738
+ dsv[r] = mm[r][0] * mm[r][0] * bv[0].y + mm[r][1] * mm[r][1] * bv[1].y + mm[r][2] * mm[r][2] * bv[2].y;
739
+ for (var q = 0u; q < 3u; q = q + 1u) {
740
+ var v = 0.0;
741
+ for (var a = 0u; a < 3u; a = a + 1u) { for (var b = 0u; b < 3u; b = b + 1u) {
742
+ var bab = bv[a].x; if (a != b) { bab = bs[a].x * bs[b].x * cc[a * 3u + b]; }
743
+ v = v + mm[r][a] * mm[q][b] * bab;
744
+ } }
745
+ srow[r][q] = v;
746
+ }
747
+ }
748
+ let t = cp_tone(mu, srow[0], srow[1], srow[2], dmu, dsv);
749
+ if (t.flag) { pixel_flags = pixel_flags | FLAG_UNCERTAIN; }
750
+ if (cp_desaturate() && t.top) { cp_chain_run = true; }
751
+ else {
752
+ let rows = array<vec3<f32>, 3>(t.a0, t.a1, t.a2);
753
+ for (var comp = 0u; comp < 3u; comp = comp + 1u) {
754
+ hm[comp] = vec2<f32>(t.hm[comp], t.bm[comp]); hv[comp] = vec2<f32>(t.hv[comp], t.bv[comp]);
772
755
  for (var c = 0u; c < 3u; c = c + 1u) {
756
+ let jmc = rows[comp].x * m_of(0u, c) + rows[comp].y * m_of(1u, c) + rows[comp].z * m_of(2u, c);
757
+ let ajm = abs(rows[comp].x * m_of(0u, c)) + abs(rows[comp].y * m_of(1u, c)) + abs(rows[comp].z * m_of(2u, c));
758
+ let value = jmc * bs[c].x;
759
+ an[comp * 3u + c] = vec2<f32>(value, ajm * bs[c].y + abs(value) * (t.ju + 4.0 * E));
760
+ }
761
+ }
762
+ cp_used = true;
763
+ }
764
+ } else if (!cp_chain_run && cp_desaturate() && !reinhard) {
765
+ for (var comp = 0u; comp < 3u; comp = comp + 1u) {
766
+ let top = hm[comp].x + CLIP_REACH * sqrt(hv[comp].x);
767
+ if (hm[comp].x > 0.0 && top >= 0.999) { cp_chain_run = true; }
768
+ if (hm[comp].x > 0.0 && hm[comp].x - 2.0 * hm[comp].y < 0.999 && abs(top - 0.999) <= 2.0 * (hm[comp].y + CLIP_REACH * min(sqrt(hv[comp].y), hv[comp].y / (2.0 * sqrt(hv[comp].x)))) + 64.0 * E) { pixel_flags = pixel_flags | FLAG_UNCERTAIN; }
769
+ }
770
+ }
771
+ if (cp_chain_run) {
772
+ let t = cp_chain(vec3<f32>(cm[0].x, cm[1].x, cm[2].x), C[0].x, C[1].x, C[2].x, C[3].x, C[4].x, C[5].x, vec3<f32>(cm[0].y, cm[1].y, cm[2].y), vec3<f32>(C[0].y, C[3].y, C[5].y));
773
+ if (t.flag) { pixel_flags = pixel_flags | FLAG_UNCERTAIN; }
774
+ let rows = array<vec3<f32>, 3>(t.a0, t.a1, t.a2);
775
+ for (var comp = 0u; comp < 3u; comp = comp + 1u) {
776
+ hm[comp] = vec2<f32>(t.hm[comp], t.bm[comp]); hv[comp] = vec2<f32>(t.hv[comp], t.bv[comp]);
777
+ for (var c = 0u; c < 3u; c = c + 1u) { an[comp * 3u + c] = vec2<f32>(rows[comp][c], abs(rows[comp][c]) * t.ju); }
778
+ }
779
+ cp_used = true;
780
+ }
781
+ if (cp_used) { for (var k = 0u; k < 9u; k = k + 1u) { cp_a[k] = an[k]; } }
782
+ }
783
+ for (var comp = 0u; comp < 3u; comp = comp + 1u) {
784
+ h_cand[d * 3u + comp] = hm[comp];
785
+ e_mean[comp] = b_add(e_mean[comp], b_scale(hm[comp], share));
786
+ }
787
+
788
+ for (var c = 0u; c < 3u; c = c + 1u) {
789
+ if (!cp_used) {
773
790
  an[c] = b_mul(b_mul(hs[0], b_rounded(m_of(0u, c))), bs[c]);
774
791
  an[3u + c] = b_mul(b_mul(hs[1], b_rounded(m_of(1u, c))), bs[c]);
775
792
  an[6u + c] = b_mul(b_mul(hs[2], b_rounded(m_of(2u, c))), bs[c]);
793
+ }
776
794
  var bc = b0; if (c == 1u) { bc = b1; } if (c == 2u) { bc = b2; }
777
795
  let wgt = b_mul(b_mul(b_scale(aj, share), b_rounded(bc)), csv[c]);
778
796
  cov[0] = b_add(cov[0], b_mul(b_mul(wgt, an[c]), an[c])); cov[1] = b_add(cov[1], b_mul(b_mul(wgt, an[c]), an[3u + c])); cov[2] = b_add(cov[2], b_mul(b_mul(wgt, an[c]), an[6u + c]));
@@ -813,24 +831,21 @@ let d0 = b_scale(bs[0], aux[eb + 2u] - aux[eb + 5u]);
813
831
  let d1 = b_scale(bs[1], aux[eb + 3u] - aux[eb + 6u]);
814
832
  let d2 = b_scale(bs[2], aux[eb + 4u] - aux[eb + 7u]);
815
833
  let sv = vec2<f32>(sites[o + 1u], sites[o + 4u]);
816
- let a = b_mul(hs[0], b_add(b_add(b_mul(b_rounded(params.m00), d0), b_mul(b_rounded(params.m01), d1)), b_mul(b_rounded(params.m02), d2)));
817
- let b = b_mul(hs[1], b_add(b_add(b_mul(b_rounded(params.m10), d0), b_mul(b_rounded(params.m11), d1)), b_mul(b_rounded(params.m12), d2)));
818
- let c = b_mul(hs[2], b_add(b_add(b_mul(b_rounded(params.m20), d0), b_mul(b_rounded(params.m21), d1)), b_mul(b_rounded(params.m22), d2)));
834
+ var a = b_mul(hs[0], b_add(b_add(b_mul(b_rounded(params.m00), d0), b_mul(b_rounded(params.m01), d1)), b_mul(b_rounded(params.m02), d2)));
835
+ var b = b_mul(hs[1], b_add(b_add(b_mul(b_rounded(params.m10), d0), b_mul(b_rounded(params.m11), d1)), b_mul(b_rounded(params.m12), d2)));
836
+ var c = b_mul(hs[2], b_add(b_add(b_mul(b_rounded(params.m20), d0), b_mul(b_rounded(params.m21), d1)), b_mul(b_rounded(params.m22), d2)));
837
+ if (cp_used) {
838
+
839
+ let e0 = aux[eb + 2u] - aux[eb + 5u]; let e1 = aux[eb + 3u] - aux[eb + 6u]; let e2 = aux[eb + 4u] - aux[eb + 7u];
840
+ a = b_add(b_add(b_scale(cp_a[0], e0), b_scale(cp_a[1], e1)), b_scale(cp_a[2], e2));
841
+ b = b_add(b_add(b_scale(cp_a[3], e0), b_scale(cp_a[4], e1)), b_scale(cp_a[5], e2));
842
+ c = b_add(b_add(b_scale(cp_a[6], e0), b_scale(cp_a[7], e1)), b_scale(cp_a[8], e2));
843
+ }
819
844
  x0 = b_add(x0, b_mul(b_mul(a, a), sv)); x1 = b_add(x1, b_mul(b_mul(b, b), sv)); x2 = b_add(x2, b_mul(b_mul(c, c), sv));
820
845
  }
821
846
  let ajaj = b_mul(aj, aj);
822
847
  res[0] = b_add(res[0], b_mul(ajaj, b_scale(x0, 0.25))); res[1] = b_add(res[1], b_mul(ajaj, b_scale(x1, 0.25))); res[2] = b_add(res[2], b_mul(ajaj, b_scale(x2, 0.25)));
823
848
  }
824
- if (params.correction == 1u) {
825
-
826
- if (abs(pixel_variance.x - params.threshold) <= pixel_variance.y) { pixel_flags = pixel_flags | FLAG_TIE; }
827
- if (pixel_variance.x > params.threshold) {
828
- var corr: array<vec2<f32>, 3>;
829
- flat_correction(cm_average[0], cm_average[1], cm_average[2], &corr);
830
- for (var comp = 0u; comp < 3u; comp = comp + 1u) { s[comp] = b_add(s[comp], b_mul(aj, corr[comp])); }
831
- pixel_flags = pixel_flags | FLAG_CORRECTED;
832
- }
833
- }
834
849
  }
835
850
  }
836
851
 
@@ -847,6 +862,11 @@ flags[3u * i + 1u] = mask0;
847
862
  flags[3u * i + 2u] = mask1;
848
863
  }
849
864
  `;
865
+ /** The pixel pass (`DISPLAY_VIEW_QUALITY_PIXELS_BODY_WGSL`) with the coupled steps' text: the stub by default, the coupled steps on demand (#593). */
866
+ export function assembleDisplayViewQualityPixelsWgsl(coupledWgsl = DISPLAY_VIEW_COUPLED_STUB_WGSL) {
867
+ return DISPLAY_VIEW_QUALITY_PIXELS_BODY_WGSL + "\n" + coupledWgsl;
868
+ }
869
+ export const DISPLAY_VIEW_QUALITY_PIXELS_WGSL = /* @__PURE__ */ assembleDisplayViewQualityPixelsWgsl();
850
870
  /** The lattice pass with a material module appended (`fn material_at(p: vec2<f32>) -> u32`, `var<private> material_uncertain: bool`). */
851
871
  export function assembleDisplayViewQualityLatticeWgsl(materialModuleWgsl) {
852
872
  return DISPLAY_VIEW_QUALITY_LATTICE_WGSL + "\n" + materialModuleWgsl + "\n";