@photivra/engine 2.21.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.
- package/CHANGELOG.md +118 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +11 -0
- package/dist/capture/c2pa-embed.d.ts +116 -0
- package/dist/capture/c2pa-embed.js +493 -0
- package/dist/capture/c2pa-embed.js.map +1 -0
- package/dist/capture/dng-import.d.ts +73 -8
- package/dist/capture/dng-import.js +315 -80
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/dng-opcodes.d.ts +96 -0
- package/dist/capture/dng-opcodes.js +391 -0
- package/dist/capture/dng-opcodes.js.map +1 -0
- package/dist/capture/dng-profile-maps.d.ts +99 -0
- package/dist/capture/dng-profile-maps.js +381 -0
- package/dist/capture/dng-profile-maps.js.map +1 -0
- package/dist/capture/dng-stage-image.d.ts +23 -0
- package/dist/capture/dng-stage-image.js +65 -0
- package/dist/capture/dng-stage-image.js.map +1 -0
- package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
- package/dist/capture/jpeg-xl-tiles.js +151 -0
- package/dist/capture/jpeg-xl-tiles.js.map +1 -0
- package/dist/capture/lossy-jpeg.d.ts +17 -0
- package/dist/capture/lossy-jpeg.js +369 -0
- package/dist/capture/lossy-jpeg.js.map +1 -0
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
- package/dist/color/neighbor-highlight-reconstruction.js +234 -0
- package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +5 -0
- package/dist/color/sensor-color-development.js +7 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/color/spectral-spikiness.d.ts +11 -0
- package/dist/color/spectral-spikiness.js +23 -0
- package/dist/color/spectral-spikiness.js.map +1 -0
- package/dist/core/execution-error.d.ts +21 -0
- package/dist/core/execution-error.js +12 -0
- package/dist/core/execution-error.js.map +1 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +26 -1
- package/dist/facade/catalogue-lens-signal.js +25 -7
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +1 -1
- package/dist/facade/diffraction-provider.js +4 -4
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +38 -6
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-normal.d.ts +15 -0
- package/dist/facade/display-view-normal.js +44 -0
- package/dist/facade/display-view-normal.js.map +1 -0
- package/dist/facade/display-view-rendering.d.ts +97 -0
- package/dist/facade/display-view-rendering.js +598 -0
- package/dist/facade/display-view-rendering.js.map +1 -0
- package/dist/facade/display-view.d.ts +37 -65
- package/dist/facade/display-view.js +147 -476
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.js +2 -2
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +37 -8
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +1 -1
- package/dist/facade/generic-lens-capture.js +1 -1
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane.js +41 -10
- package/dist/facade/image-plane.js.map +1 -1
- package/dist/facade/on-demand-kits.d.ts +14 -0
- package/dist/facade/on-demand-kits.js +36 -0
- package/dist/facade/on-demand-kits.js.map +1 -0
- package/dist/facade/opt-in-kit.d.ts +9 -0
- package/dist/facade/opt-in-kit.js +11 -0
- package/dist/facade/opt-in-kit.js.map +1 -0
- package/dist/facade/quality-kit.d.ts +21 -0
- package/dist/facade/quality-kit.js +23 -0
- package/dist/facade/quality-kit.js.map +1 -0
- package/dist/facade/quality-paths.d.ts +22 -0
- package/dist/facade/quality-paths.js +44 -0
- package/dist/facade/quality-paths.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +18 -2
- package/dist/facade/scene-radiance.js +6 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +32 -13
- package/dist/facade/simulate-capture.js +162 -69
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +10 -12
- package/dist/facade/simulate-display-view.js +57 -48
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
- package/dist/gpu/display-view-gpu-renderer.js +134 -0
- package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
- package/dist/gpu/display-view-image-material-module.d.ts +3 -2
- package/dist/gpu/display-view-image-material-module.js +25 -3
- package/dist/gpu/display-view-image-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-quality.d.ts +43 -1
- package/dist/gpu/display-view-image-quality.js +256 -11
- package/dist/gpu/display-view-image-quality.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
- package/dist/gpu/display-view-quality-gpu.js +60 -249
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
- package/dist/gpu/display-view-speed-gpu.js +50 -12
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +2 -13
- package/dist/gpu/image-kernel-execution.js +2 -10
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
- package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
- package/dist/gpu/wgsl-display-view-coupled.js +353 -0
- package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -9
- package/dist/gpu/wgsl-display-view-quality-kernel.js +109 -111
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
- package/dist/gpu/wgsl-display-view-speed-kernel.js +71 -8
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/output/camera-channel-response.d.ts +50 -0
- package/dist/output/camera-channel-response.js +69 -0
- package/dist/output/camera-channel-response.js.map +1 -0
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/sdr-rendering.d.ts +18 -1
- package/dist/output/sdr-rendering.js +39 -2
- package/dist/output/sdr-rendering.js.map +1 -1
- package/dist/sensor/native-sensor-report.d.ts +10 -0
- package/dist/sensor/native-sensor-report.js +16 -0
- package/dist/sensor/native-sensor-report.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +81 -13
- package/dist/sensor/sensor-characteristics.js +10 -6
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/API_REFERENCE.md +117 -4
- package/docs/QUICK_START.md +37 -8
- package/docs/USAGE.md +104 -22
- package/docs/api/capture-c2pa-embed.md +177 -0
- package/docs/api/capture-dng-import.md +86 -10
- package/docs/api/capture-dng-opcodes.md +93 -0
- package/docs/api/capture-dng-profile-maps.md +141 -0
- package/docs/api/capture-dng-stage-image.md +43 -0
- package/docs/api/capture-jpeg-xl-tiles.md +33 -0
- package/docs/api/capture-lossy-jpeg.md +41 -0
- package/docs/api/capture-native-raw-development.md +2 -1
- package/docs/api/core-execution-error.md +43 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +255 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-fast-methods.md +38 -9
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-scene-radiance.md +1 -1
- package/docs/api/facade-simulate-capture.md +24 -7
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +1 -0
- package/package.json +6 -5
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
* point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could
|
|
26
26
|
* change the value by more than E of it (the Speed module's rule, per node).
|
|
27
27
|
*
|
|
28
|
+
* **Mixed light (#597).** Under `illumination` W_n is replaced by the irradiance / π at the point, E_n(p) = Σ_i w_i(p)·T_i,n +
|
|
29
|
+
* Σ_j c_j,n·h_j/d_j³ (src/facade/mixed-illumination.ts), from the light table (`imagePlaneLightTable`: the parts and the spectra at
|
|
30
|
+
* the nodes, no channel value). The blend's position t is formed in double-float32 with te, its error beyond its own low part; each
|
|
31
|
+
* weight comes from its own distance to the segment's ends (rounded once: E relative, plus te times the segment's slope); a regions
|
|
32
|
+
* edge, or a hard edge of equal stops, within the point's bound flags it; a choice of the neighbouring segment moves the weights by
|
|
33
|
+
* at most te times the steepest slope; each point source's h/d³ is a bounded value (B arithmetic). The node value's computed bound
|
|
34
|
+
* (the tables' rounding, the weights, the products and the sums) must stay within (IMAGE_PLANE_LIGHT_U − 1)·E of it, else the point
|
|
35
|
+
* is flagged, so a lit point carries (IMAGE_PLANE_LATTICE_U + IMAGE_PLANE_LIGHT_U)·E relative.
|
|
36
|
+
*
|
|
28
37
|
* **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged
|
|
29
38
|
* point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).
|
|
30
39
|
* Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero
|
|
@@ -43,17 +52,237 @@ import { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DIS
|
|
|
43
52
|
import { latticeCoordinate } from "./display-view-quality-gpu.js";
|
|
44
53
|
import { queryGeometry } from "./display-view-speed-gpu.js";
|
|
45
54
|
import { displayViewSignalTables } from "../facade/expected-signal.js";
|
|
55
|
+
import { calculateIlluminantIrradianceScale } from "../facade/scene-radiance.js";
|
|
46
56
|
const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
|
|
47
57
|
/** Relative roundings of a lattice point's radiance (see the module comment). */
|
|
48
58
|
export const IMAGE_PLANE_LATTICE_U = 32;
|
|
59
|
+
/**
|
|
60
|
+
* #597: relative roundings of the mixed light at a lattice point (a point whose computed bound exceeds IMAGE_PLANE_LIGHT_U − 1 is
|
|
61
|
+
* flagged; see the module comment), added to IMAGE_PLANE_LATTICE_U for a lit print.
|
|
62
|
+
*/
|
|
63
|
+
export const IMAGE_PLANE_LIGHT_U = 40;
|
|
64
|
+
/** #597: layout of the light table (float32 / u32 words; see `imagePlaneLightTable`). */
|
|
65
|
+
export const IMAGE_PLANE_LIGHT = /* @__PURE__ */ Object.freeze({ kind: 0, sources: 1, points: 2, regions: 3, outside: 4, regionsAt: 6, pointsAt: 7, tablesAt: 8, slack: 9,
|
|
66
|
+
surround: 10, maximumInverse: 11, halfHi: 12, halfLo: 13, aU: 16, aV: 18, dU: 20, dV: 22, inverse: 24, stops: 26, regionWords: 9, pointWords: 6, maximumPoints: 8 });
|
|
49
67
|
/** Lattice nodes of the print (5 × 5 × 5). */
|
|
50
68
|
const NODES = 125;
|
|
51
69
|
/** Words per pixel record. */
|
|
52
70
|
const RECORD_WORDS = 4;
|
|
53
71
|
/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */
|
|
54
|
-
export const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, suHi: 8, suLo: 9, svHi: 10, svLo: 11,
|
|
72
|
+
export const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, lit: 6, suHi: 8, suLo: 9, svHi: 10, svLo: 11,
|
|
55
73
|
kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });
|
|
56
74
|
const FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;
|
|
75
|
+
const L = IMAGE_PLANE_LIGHT;
|
|
76
|
+
/**
|
|
77
|
+
* #597: the mixed light at a lattice point (see the module comment): `light_prepare` once per point (the blend's source and weights
|
|
78
|
+
* with their error bound, and each point source's h/d³ as a bounded value), then `light_at(n)` per node, flagging the point when the
|
|
79
|
+
* computed bound exceeds (IMAGE_PLANE_LIGHT_U − 1)·E of the value.
|
|
80
|
+
*/
|
|
81
|
+
const LIGHT_WGSL = /* wgsl */ `
|
|
82
|
+
var<private> blend_i: u32;
|
|
83
|
+
var<private> blend_w0: f32;
|
|
84
|
+
var<private> blend_w1: f32;
|
|
85
|
+
var<private> blend_e0: f32;
|
|
86
|
+
var<private> blend_e1: f32;
|
|
87
|
+
var<private> blend_amb: f32;
|
|
88
|
+
var<private> fall_v: array<f32, ${L.maximumPoints}>;
|
|
89
|
+
var<private> fall_e: array<f32, ${L.maximumPoints}>;
|
|
90
|
+
fn lw(i: u32) -> u32 { return bitcast<u32>(light[i]); }
|
|
91
|
+
fn ld(i: u32) -> D { return D(light[i], light[i + 1u]); }
|
|
92
|
+
|
|
93
|
+
fn d_gt(a: D, b: D) -> bool { let d = d_sub(a, b); return d.h > 0.0 || (d.h == 0.0 && d.l > 0.0); }
|
|
94
|
+
|
|
95
|
+
fn bsqrt(a: B) -> B {
|
|
96
|
+
let v = sqrt(max(a.v, 0.0));
|
|
97
|
+
let low = a.v - SAFETY * a.e;
|
|
98
|
+
if (!(low > 0.0)) { material_uncertain = true; return B(v, 3.0e38); }
|
|
99
|
+
return B(v, a.e / (2.0 * sqrt(low)) + 5.0 * E * v);
|
|
100
|
+
}
|
|
101
|
+
fn light_prepare(u: D, v: D) {
|
|
102
|
+
let kind = lw(${L.kind}u);
|
|
103
|
+
let S = lw(${L.sources}u);
|
|
104
|
+
blend_i = 0u; blend_w0 = 1.0; blend_w1 = 0.0; blend_e0 = 0.0; blend_e1 = 0.0; blend_amb = 0.0;
|
|
105
|
+
|
|
106
|
+
let ce = 4.0 * DW * (abs(u.h) + abs(v.h) + 1.0) + light[${L.slack}u];
|
|
107
|
+
if (kind == 3u) {
|
|
108
|
+
var source = lw(${L.outside}u);
|
|
109
|
+
let bu = bd(u);
|
|
110
|
+
let bv = bd(v);
|
|
111
|
+
for (var r = 0u; r < lw(${L.regions}u); r = r + 1u) {
|
|
112
|
+
let b = lw(${L.regionsAt}u) + r * ${L.regionWords}u;
|
|
113
|
+
if (ge(bu, bd(ld(b + 1u))) && le(bu, bd(ld(b + 3u))) && ge(bv, bd(ld(b + 5u))) && le(bv, bd(ld(b + 7u)))) { source = lw(b); break; }
|
|
114
|
+
}
|
|
115
|
+
blend_i = source;
|
|
116
|
+
} else if (kind != 0u) {
|
|
117
|
+
|
|
118
|
+
var t: D;
|
|
119
|
+
var te: f32;
|
|
120
|
+
let a_u = d_sub(u, ld(${L.aU}u));
|
|
121
|
+
let a_v = d_sub(v, ld(${L.aV}u));
|
|
122
|
+
let inv = ld(${L.inverse}u);
|
|
123
|
+
let ce_t = ce + 4.0 * DW * (abs(light[${L.aU}u]) + abs(light[${L.aV}u]));
|
|
124
|
+
if (kind == 1u) {
|
|
125
|
+
let x = d_mul(a_u, ld(${L.dU}u));
|
|
126
|
+
let y = d_mul(a_v, ld(${L.dV}u));
|
|
127
|
+
let scale = (abs(x.h) + abs(y.h)) * abs(inv.h);
|
|
128
|
+
t = d_mul(d_add(x, y), inv);
|
|
129
|
+
te = 8.0 * DW * (scale + 1.0) + ce_t * (abs(light[${L.dU}u]) + abs(light[${L.dV}u])) * abs(inv.h);
|
|
130
|
+
if (t.h < 0.0) { t = D(0.0, 0.0); }
|
|
131
|
+
if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }
|
|
132
|
+
} else {
|
|
133
|
+
|
|
134
|
+
let dd = d_add(d_mul(a_u, a_u), d_mul(a_v, a_v));
|
|
135
|
+
let r0 = sqrt(max(dd.h, 0.0));
|
|
136
|
+
var r = D(r0, 0.0);
|
|
137
|
+
if (r0 > 0.0) { let res = d_sub(dd, d_mul(D(r0, 0.0), D(r0, 0.0))); r = quick_two_sum(r0, ob(ob(res.h + res.l) / ob(2.0 * r0))); }
|
|
138
|
+
t = d_mul(r, inv);
|
|
139
|
+
te = 8.0 * DW * (abs(t.h) + 1.0) + 2.0 * ce_t * abs(inv.h);
|
|
140
|
+
if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
var i = 0u;
|
|
144
|
+
loop { if (i + 1u >= S) { break; } if (d_gt(ld(${L.stops}u + (i + 1u) * 4u), t)) { break; } i = i + 1u; }
|
|
145
|
+
for (var k = 0u; k + 1u < S; k = k + 1u) {
|
|
146
|
+
if (light[${L.stops}u + k * 4u + 2u] == 0.0 && abs(d_sub(t, ld(${L.stops}u + k * 4u)).h) <= SAFETY * te + DW * (abs(t.h) + 1.0)) { material_uncertain = true; }
|
|
147
|
+
}
|
|
148
|
+
let si = ld(${L.stops}u + i * 4u);
|
|
149
|
+
|
|
150
|
+
blend_amb = te * light[${L.maximumInverse}u];
|
|
151
|
+
if (i + 1u < S && d_gt(t, si)) {
|
|
152
|
+
|
|
153
|
+
let inv_i = ld(${L.stops}u + i * 4u + 2u);
|
|
154
|
+
let fd = d_mul(d_sub(t, si), inv_i);
|
|
155
|
+
let gd = d_mul(d_sub(ld(${L.stops}u + (i + 1u) * 4u), t), inv_i);
|
|
156
|
+
blend_w1 = clamp(ob(fd.h + fd.l), 0.0, 1.0);
|
|
157
|
+
blend_w0 = clamp(ob(gd.h + gd.l), 0.0, 1.0);
|
|
158
|
+
let shared_e = (te + 4.0 * DW * (abs(t.h) + abs(si.h) + 1.0)) * abs(inv_i.h);
|
|
159
|
+
blend_e1 = E * blend_w1 + shared_e;
|
|
160
|
+
blend_e0 = E * blend_w0 + shared_e;
|
|
161
|
+
}
|
|
162
|
+
blend_i = i;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let half = ld(${L.halfHi}u);
|
|
166
|
+
let x = d_mul(u, half);
|
|
167
|
+
let y = d_mul(v, half);
|
|
168
|
+
let cm = ce * abs(half.h) + 4.0 * DW * (abs(x.h) + abs(y.h));
|
|
169
|
+
for (var j = 0u; j < lw(${L.points}u); j = j + 1u) {
|
|
170
|
+
let b = lw(${L.pointsAt}u) + j * ${L.pointWords}u;
|
|
171
|
+
var dx = bds(d_sub(x, ld(b)), abs(x.h) + abs(light[b]));
|
|
172
|
+
dx.e = dx.e + cm;
|
|
173
|
+
var dy = bds(d_sub(y, ld(b + 2u)), abs(y.h) + abs(light[b + 2u]));
|
|
174
|
+
dy.e = dy.e + cm;
|
|
175
|
+
let h = bd(ld(b + 4u));
|
|
176
|
+
let d2 = badd(badd(bmul(dx, dx), bmul(dy, dy)), bmul(h, h));
|
|
177
|
+
let fall = bdiv(h, bmul(d2, bsqrt(d2)));
|
|
178
|
+
fall_v[j] = fall.v;
|
|
179
|
+
fall_e[j] = fall.e;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
fn light_at(n: u32) -> f32 {
|
|
184
|
+
let N = photo.nodes;
|
|
185
|
+
let S = lw(${L.sources}u);
|
|
186
|
+
let P = lw(${L.points}u);
|
|
187
|
+
let tab = lw(${L.tablesAt}u);
|
|
188
|
+
var value = 0.0;
|
|
189
|
+
var err = 0.0;
|
|
190
|
+
if (S > 0u) {
|
|
191
|
+
let ti = light[tab + blend_i * N + n];
|
|
192
|
+
var top = ti;
|
|
193
|
+
for (var s = 0u; s < S; s = s + 1u) { top = max(top, light[tab + s * N + n]); }
|
|
194
|
+
var tj = 0.0;
|
|
195
|
+
if (blend_w1 == 0.0) { value = ti; } else { tj = light[tab + (blend_i + 1u) * N + n]; value = blend_w0 * ti + blend_w1 * tj; }
|
|
196
|
+
err = 3.0 * E * value + blend_e0 * ti + blend_e1 * tj + blend_amb * top;
|
|
197
|
+
}
|
|
198
|
+
for (var j = 0u; j < P; j = j + 1u) {
|
|
199
|
+
let c = light[tab + (S + j) * N + n];
|
|
200
|
+
let term = c * fall_v[j];
|
|
201
|
+
value = value + term;
|
|
202
|
+
err = err + c * fall_e[j] + 2.0 * E * term;
|
|
203
|
+
}
|
|
204
|
+
err = err + f32(P + 1u) * E * value;
|
|
205
|
+
if (!(err <= ${IMAGE_PLANE_LIGHT_U - 1}.0 * E * value)) { material_uncertain = true; }
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
209
|
+
/**
|
|
210
|
+
* #597: the light table of a lit print at the spectral nodes (the layout `IMAGE_PLANE_LIGHT`): the blend (kind, sources, its geometry
|
|
211
|
+
* and stops with each segment's inverse width, 0 at a hard edge), the regions, the point sources (x, y, h in metres), then per node
|
|
212
|
+
* T_i,n = k_i·S_i(λ_n)/π for each blend source and c_j·S_j(λ_n)/π for each point source: spectra only, no channel value. `slack` is the
|
|
213
|
+
* reference's own rounding of a query point in print-local units.
|
|
214
|
+
*/
|
|
215
|
+
export function imagePlaneLightTable(light, wavelengths, surroundReflectance, slack) {
|
|
216
|
+
const resolved = light.layout;
|
|
217
|
+
if (!resolved)
|
|
218
|
+
throw new ImageKernelExecutionError("unsupported-on-backend", "The print's mixed light carries no parts for the GPU; use cpu-reference.");
|
|
219
|
+
// The blend's sources come first, then the point sources (their report carries the position and the intensity, c = I·k(1 lux)).
|
|
220
|
+
const blend = resolved.blend;
|
|
221
|
+
const sources = resolved.sources.filter((s) => s.report.position === undefined), points = resolved.sources.filter((s) => s.report.position !== undefined).map((s) => {
|
|
222
|
+
const position = s.report.position;
|
|
223
|
+
return { ...position, scale: calculateIlluminantIrradianceScale(s.illuminant.shape, 1) * s.report.intensityCandela, shape: s.illuminant.shape };
|
|
224
|
+
});
|
|
225
|
+
const S = sources.length, P = points.length, N = wavelengths.length, regions = blend?.regions ?? [];
|
|
226
|
+
if (P > L.maximumPoints)
|
|
227
|
+
throw new ImageKernelExecutionError("unsupported-on-backend", "More point sources than the GPU pass holds.");
|
|
228
|
+
const regionsAt = L.stops + 4 * S, pointsAt = regionsAt + L.regionWords * regions.length, tablesAt = pointsAt + L.pointWords * P;
|
|
229
|
+
const table = new Float32Array(tablesAt + (S + P) * N), words = new Uint32Array(table.buffer);
|
|
230
|
+
const pair = (at, x) => { [table[at], table[at + 1]] = dwSplit(x); };
|
|
231
|
+
words[L.kind] = blend === null ? 0 : blend.kind === "linear-gradient" ? 1 : blend.kind === "radial" ? 2 : 3;
|
|
232
|
+
words[L.sources] = S;
|
|
233
|
+
words[L.points] = P;
|
|
234
|
+
words[L.regions] = regions.length;
|
|
235
|
+
words[L.outside] = blend?.outsideSource ?? 0;
|
|
236
|
+
words[L.regionsAt] = regionsAt;
|
|
237
|
+
words[L.pointsAt] = pointsAt;
|
|
238
|
+
words[L.tablesAt] = tablesAt;
|
|
239
|
+
table[L.slack] = slack;
|
|
240
|
+
table[L.surround] = surroundReflectance;
|
|
241
|
+
pair(L.halfHi, light.halfWidthM);
|
|
242
|
+
if (blend?.kind === "linear-gradient") {
|
|
243
|
+
const from = blend.from, du = blend.to.u - from.u, dv = blend.to.v - from.v;
|
|
244
|
+
pair(L.aU, from.u);
|
|
245
|
+
pair(L.aV, from.v);
|
|
246
|
+
pair(L.dU, du);
|
|
247
|
+
pair(L.dV, dv);
|
|
248
|
+
pair(L.inverse, 1 / (du * du + dv * dv));
|
|
249
|
+
}
|
|
250
|
+
else if (blend?.kind === "radial") {
|
|
251
|
+
pair(L.aU, blend.center.u);
|
|
252
|
+
pair(L.aV, blend.center.v);
|
|
253
|
+
pair(L.inverse, 1 / blend.radius);
|
|
254
|
+
}
|
|
255
|
+
let maximumInverse = 0;
|
|
256
|
+
const stops = blend?.stops ?? [];
|
|
257
|
+
stops.forEach((stop, i) => {
|
|
258
|
+
const next = stops[i + 1], inverse = next !== undefined && next > stop ? 1 / (next - stop) : 0;
|
|
259
|
+
pair(L.stops + 4 * i, stop);
|
|
260
|
+
pair(L.stops + 4 * i + 2, inverse);
|
|
261
|
+
maximumInverse = Math.max(maximumInverse, inverse);
|
|
262
|
+
});
|
|
263
|
+
table[L.maximumInverse] = maximumInverse;
|
|
264
|
+
regions.forEach((r, i) => {
|
|
265
|
+
const at = regionsAt + L.regionWords * i;
|
|
266
|
+
words[at] = r.source;
|
|
267
|
+
pair(at + 1, r.uMin);
|
|
268
|
+
pair(at + 3, r.uMax);
|
|
269
|
+
pair(at + 5, r.vMin);
|
|
270
|
+
pair(at + 7, r.vMax);
|
|
271
|
+
});
|
|
272
|
+
points.forEach((p, j) => { const at = pointsAt + L.pointWords * j; pair(at, p.xM); pair(at + 2, p.yM); pair(at + 4, p.heightM); });
|
|
273
|
+
// The largest irradiance / π anywhere (the blend's largest source and every point at normal incidence, 1/h²), for the tiny-value bound.
|
|
274
|
+
let largestIrradiance = 0;
|
|
275
|
+
wavelengths.forEach((nm, n) => {
|
|
276
|
+
let top = 0, atNormal = 0;
|
|
277
|
+
sources.forEach((source, i) => { const value = source.irradianceScale * source.illuminant.shape.evaluate(nm) / Math.PI; table[tablesAt + i * N + n] = value; top = Math.max(top, value); });
|
|
278
|
+
points.forEach((p, j) => { const value = p.scale * p.shape.evaluate(nm) / Math.PI; table[tablesAt + (S + j) * N + n] = value; atNormal += value / (p.heightM * p.heightM); });
|
|
279
|
+
largestIrradiance = Math.max(largestIrradiance, top + atNormal);
|
|
280
|
+
});
|
|
281
|
+
for (let i = tablesAt; i < table.length; i++)
|
|
282
|
+
if (!(table[i] >= 0 && table[i] <= FLOAT32_SAFE_MAGNITUDE))
|
|
283
|
+
throw new ImageKernelExecutionError("unsupported-float32-domain", "A light's spectrum is outside the float32 domain.");
|
|
284
|
+
return { table, largestIrradiance };
|
|
285
|
+
}
|
|
57
286
|
/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */
|
|
58
287
|
const photoCommon = (path) => displayViewModuleCommonWgsl(path).replace(/\blattice\.zero\b/gu, "photo.zero");
|
|
59
288
|
/** Pass 1's WGSL (see the module comment). */
|
|
@@ -63,14 +292,16 @@ export function imagePlaneQualityLatticeWgsl(path) {
|
|
|
63
292
|
@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;
|
|
64
293
|
@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;
|
|
65
294
|
struct PhotoParams {
|
|
66
|
-
width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32,
|
|
295
|
+
width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, lit: u32, pad1: u32,
|
|
67
296
|
su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,
|
|
68
297
|
hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,
|
|
69
298
|
};
|
|
70
299
|
@group(1) @binding(0) var<storage, read> records: array<u32>;
|
|
71
300
|
@group(1) @binding(1) var<uniform> photo: PhotoParams;
|
|
72
301
|
@group(1) @binding(2) var<storage, read> node_table: array<f32>;
|
|
302
|
+
@group(1) @binding(3) var<storage, read> light: array<f32>;
|
|
73
303
|
${photoCommon(path)}
|
|
304
|
+
${LIGHT_WGSL}
|
|
74
305
|
fn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }
|
|
75
306
|
struct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }
|
|
76
307
|
fn bilinear_axis(s: D, n: u32) -> Axis {
|
|
@@ -96,6 +327,8 @@ fn pixel_at(i: u32, j: u32) -> Pixel {
|
|
|
96
327
|
channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));
|
|
97
328
|
}
|
|
98
329
|
fn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }
|
|
330
|
+
// The white radiance at node n, or (#597) under mixed light the irradiance / π at the point.
|
|
331
|
+
fn white_at(white: u32, n: u32) -> f32 { if (photo.lit != 0u) { return light_at(n); } return node_table[white + n]; }
|
|
99
332
|
// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.
|
|
100
333
|
fn pixel_value(px: Pixel, n: u32) -> f32 {
|
|
101
334
|
let b = px.base;
|
|
@@ -122,8 +355,12 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
|
|
|
122
355
|
let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);
|
|
123
356
|
let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);
|
|
124
357
|
let aspect = D(photo.aspect_hi, photo.aspect_lo);
|
|
358
|
+
// #597: under mixed light the irradiance at the point (its blend and point sources) replaces the white radiance per node.
|
|
359
|
+
if (photo.lit != 0u) { light_prepare(u, v); }
|
|
125
360
|
if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {
|
|
126
|
-
for (var n = 0u; n < N; n = n + 1u) {
|
|
361
|
+
for (var n = 0u; n < N; n = n + 1u) {
|
|
362
|
+
if (photo.lit != 0u) { lattice_values[out + n] = light[${L.surround}u] * light_at(n); } else { lattice_values[out + n] = node_table[white + N + n]; }
|
|
363
|
+
}
|
|
127
364
|
lattice_flags[i] = select(0u, 1u, material_uncertain);
|
|
128
365
|
return;
|
|
129
366
|
}
|
|
@@ -133,7 +370,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
|
|
|
133
370
|
let last_x = f32(photo.width - 1u);
|
|
134
371
|
let last_y = f32(photo.height - 1u);
|
|
135
372
|
let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));
|
|
136
|
-
for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] =
|
|
373
|
+
for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = white_at(white, n) * pixel_value(px, n); }
|
|
137
374
|
} else {
|
|
138
375
|
let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);
|
|
139
376
|
let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);
|
|
@@ -149,7 +386,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
|
|
|
149
386
|
let s11 = pixel_value(p11, n);
|
|
150
387
|
let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);
|
|
151
388
|
if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }
|
|
152
|
-
lattice_values[out + n] =
|
|
389
|
+
lattice_values[out + n] = white_at(white, n) * value;
|
|
153
390
|
}
|
|
154
391
|
}
|
|
155
392
|
lattice_flags[i] = select(0u, 1u, material_uncertain);
|
|
@@ -214,7 +451,7 @@ write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, fla
|
|
|
214
451
|
* members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,
|
|
215
452
|
* the reference's float64 values rounded once.
|
|
216
453
|
*/
|
|
217
|
-
export function imagePlaneQualityGather(signalInput) {
|
|
454
|
+
export function imagePlaneQualityGather(signalInput, largestLitRadiance) {
|
|
218
455
|
const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;
|
|
219
456
|
const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups = [];
|
|
220
457
|
const rects = tables.groups.map((group) => {
|
|
@@ -251,8 +488,11 @@ export function imagePlaneQualityGather(signalInput) {
|
|
|
251
488
|
}
|
|
252
489
|
});
|
|
253
490
|
const image = signalInput.scene.image;
|
|
254
|
-
|
|
255
|
-
largestRadiance =
|
|
491
|
+
if (largestLitRadiance !== undefined)
|
|
492
|
+
largestRadiance = largestLitRadiance;
|
|
493
|
+
else
|
|
494
|
+
for (const w of image.whiteRadiance)
|
|
495
|
+
largestRadiance = Math.max(largestRadiance, w);
|
|
256
496
|
let tinyMass = 0;
|
|
257
497
|
for (let t = 0; t < T; t++)
|
|
258
498
|
tinyMass += tiny[t] * largestRadiance * N;
|
|
@@ -263,7 +503,9 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
|
|
|
263
503
|
const signalInput = gpu.signalInput, image = signalInput.scene.image;
|
|
264
504
|
if (!image || !gpu.placement)
|
|
265
505
|
throw new ImageKernelExecutionError("unsupported-on-backend", "The setup carries no image-plane print and placement; use cpu-reference.");
|
|
266
|
-
|
|
506
|
+
// #597: under mixed light, the light's table (its parts and spectra at the nodes), which replaces the white radiance in pass 1.
|
|
507
|
+
const k0 = gpu.placement.chartUnitsPerFrameUnit, lit = image.light ? imagePlaneLightTable(image.light, signalInput.wavelengthsNm, image.surroundReflectance, 1e-14 * k0) : undefined;
|
|
508
|
+
const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput, lit?.largestIrradiance);
|
|
267
509
|
let cw = 0, ch = 0;
|
|
268
510
|
for (const g of gather.groups) {
|
|
269
511
|
cw = Math.max(cw, g.cw);
|
|
@@ -287,6 +529,8 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
|
|
|
287
529
|
words[P.nearest] = scene.interpolation === "nearest" ? 1 : 0;
|
|
288
530
|
words[P.nodes] = N;
|
|
289
531
|
words[P.zero] = 0;
|
|
532
|
+
words[P.lit] = lit ? 1 : 0;
|
|
533
|
+
const lightTable = lit ? lit.table : new Float32Array(1);
|
|
290
534
|
[floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU);
|
|
291
535
|
[floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV);
|
|
292
536
|
[floats[P.kHi], floats[P.kLo]] = dwSplit(k);
|
|
@@ -300,7 +544,7 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
|
|
|
300
544
|
const label = path === "dekker" ? "image-plane" : "image-plane-fma";
|
|
301
545
|
const fractions = new Float64Array(3);
|
|
302
546
|
const passes = {
|
|
303
|
-
gather, siteGamma: (IMAGE_PLANE_LATTICE_U + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,
|
|
547
|
+
gather, siteGamma: (IMAGE_PLANE_LATTICE_U + (lit ? IMAGE_PLANE_LIGHT_U : 0) + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,
|
|
304
548
|
lattice: ["photivra-display-view-quality-photo-lattice:" + label, imagePlaneQualityLatticeWgsl(path)],
|
|
305
549
|
sites: ["photivra-display-view-quality-photo-sites", IMAGE_PLANE_QUALITY_SITES_WGSL],
|
|
306
550
|
async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal) {
|
|
@@ -350,7 +594,8 @@ export function imagePlaneQualityPasses(runtime, gpu, path) {
|
|
|
350
594
|
return runtime.device.createBindGroup({ layout, entries: [
|
|
351
595
|
{ binding: 0, resource: { buffer: scope.upload("photivra-display-view-photo-records", records, BUFFER_USAGE.STORAGE) } },
|
|
352
596
|
{ binding: 1, resource: { buffer: scope.upload("photivra-display-view-photo-params", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },
|
|
353
|
-
{ binding: 2, resource: { buffer: scope.upload("photivra-display-view-photo-nodes", nodeTable, BUFFER_USAGE.STORAGE) } }
|
|
597
|
+
{ binding: 2, resource: { buffer: scope.upload("photivra-display-view-photo-nodes", nodeTable, BUFFER_USAGE.STORAGE) } },
|
|
598
|
+
{ binding: 3, resource: { buffer: scope.upload("photivra-display-view-photo-light", lightTable, BUFFER_USAGE.STORAGE) } }
|
|
354
599
|
] });
|
|
355
600
|
}
|
|
356
601
|
function resolve(values, flags, groupWords) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display-view-image-quality.js","sourceRoot":"","sources":["../../src/gpu/display-view-image-quality.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The image-plane print in the GPU display view's Quality passes (#581): diffraction kernels, the defocus disc, the anti-aliasing\n * filter's spots and AHD for a photo, loaded with the image-plane GPU module (only after `prepareImagePlaneScenes()`). Passes 1\n * and 2 are the print's own; pass 3 (the display pixels) and the host finishing are the Quality renderer's, unchanged.\n *\n * **What the reference computes** (expected-signal.ts, the radiance path): with site kernels, per site of channel c,\n * Σ_g Σ_d Σ_n C_n,c(d)·L_n(p_g + d), where L_n(p) is the print's spectral radiance at spectral node n at lattice point p (the\n * provider's `radianceAt` on the lattice formula) and C_n,c(d) the reference's combined kernel per node (`perNode` of its\n * tables: Σ_members w·A_c(λ_n)·K_n(t)); without kernels, per query offset w·Σ_n A_c(λ_n)·L_n(p_o), the same value as the direct\n * path's `signalAt`.\n *\n * **Pass 1 (lattice radiance).** One invocation per lattice point of each group: the exact point (p + c)·g in double-float32\n * (p + c is exact in float32, checked by the Quality host; the scale's hi + lo from float64), the print-local point, the print's\n * edge in the composite kernel's bound arithmetic, the pixel coordinates, the bilinear weights as the Speed module forms them,\n * and for every node n\n *\n * L_n = W_n·Σ_j b_j·(ρ_black + (ρ_white − ρ_black)·Σ_corners w_jk·s_k(λ_n)),\n *\n * the reference's arithmetic regrouped. Each pixel's lattice cell and fractions come from the host (float64, as the reference\n * decodes the pixel), per band for the rows its lattice windows reach: 16 bytes per pixel (the cell, and per channel the smaller\n * of f and 1 − f rounded once, with a bit for which), so every corner weight carries at most 2E relative per channel. All terms\n * are non-negative: L_n carries `IMAGE_PLANE_LATTICE_U`·E relative (s 1, three channel weights 6, the nested trilinear form 6,\n * the paper 3, the bilinear weights 4, their products and sums 4, the moved-weight budget 1, W_n 2: 27, declared 32), or the\n * point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could\n * change the value by more than E of it (the Speed module's rule, per node).\n *\n * **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged\n * point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).\n * Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero\n * weight) is flagged and its display pixels are recomputed on the host.\n *\n * **Pass 2 (sites).** The blocked sum (#492's order) of Σ_n L_n·C_n,c(t) over each group's dense tap rectangle (without kernels the\n * members' span, zero where no member sits): a term carries L's bound, the table's rounding and the product, the node sum's N − 1,\n * and the row, group and total additions, so the electrons carry (IMAGE_PLANE_LATTICE_U + N + cw + ch + G + 4)·E relative.\n * Then the Quality site tail (readout moments, white clip, running bounds), unchanged.\n */\n\nimport { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from \"./image-kernel-execution.js\";\nimport { BUFFER_USAGE, linearDispatch, runComputePass, type GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { dwSplit } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { displayViewModuleCommonWgsl } from \"./display-view-composite-material-module.js\";\nimport { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE } from \"./wgsl-display-view-quality-kernel.js\";\nimport { latticeCoordinate, type DisplayViewQualityImagePasses, type SignalGather } from \"./display-view-quality-gpu.js\";\nimport { queryGeometry } from \"./display-view-speed-gpu.js\";\nimport { displayViewSignalTables, type DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport type { DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport type { ImagePlaneSignalScene } from \"../facade/scene-radiance.js\";\n\nconst E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;\n/** Relative roundings of a lattice point's radiance (see the module comment). */\nexport const IMAGE_PLANE_LATTICE_U = 32;\n/** Lattice nodes of the print (5 × 5 × 5). */\nconst NODES = 125;\n/** Words per pixel record. */\nconst RECORD_WORDS = 4;\n/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */\nexport const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, suHi: 8, suLo: 9, svHi: 10, svLo: 11,\n kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });\nconst FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;\n\n/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */\nconst photoCommon = (path: ChartCompositeProductPath): string => displayViewModuleCommonWgsl(path).replace(/\\blattice\\.zero\\b/gu, \"photo.zero\");\n\n/** Pass 1's WGSL (see the module comment). */\nexport function imagePlaneQualityLatticeWgsl(path: ChartCompositeProductPath): string {\n return `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;\n@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;\nstruct PhotoParams {\n width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, pad0: u32, pad1: u32,\n su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,\n hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,\n};\n@group(1) @binding(0) var<storage, read> records: array<u32>;\n@group(1) @binding(1) var<uniform> photo: PhotoParams;\n@group(1) @binding(2) var<storage, read> node_table: array<f32>;\n${photoCommon(path)}\nfn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }\nstruct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }\nfn bilinear_axis(s: D, n: u32) -> Axis {\n var f0 = floor(s.h);\n if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }\n let frac = d_sub(s, D(f0, 0.0));\n let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);\n var w0 = 0.0; var w1 = 0.0;\n if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }\n let last = i32(n) - 1;\n return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);\n}\n// A channel's two corner weights from its record: the stored smaller one and 1 minus it.\nfn channel_weights(word: u32, bit: u32, small: f32) -> vec2<f32> {\n if (((word >> bit) & 1u) == 0u) { return vec2<f32>(ob(1.0 - small), small); }\n return vec2<f32>(small, ob(1.0 - small));\n}\nstruct Pixel { base: u32, r: vec2<f32>, g: vec2<f32>, b: vec2<f32> }\nfn pixel_at(i: u32, j: u32) -> Pixel {\n let o = ((j - photo.row0) * photo.width + i) * ${RECORD_WORDS}u;\n let w = records[o];\n return Pixel((w & 3u) * 25u + ((w >> 2u) & 3u) * 5u + ((w >> 4u) & 3u), channel_weights(w, 6u, bitcast<f32>(records[o + 1u])),\n channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));\n}\nfn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }\n// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.\nfn pixel_value(px: Pixel, n: u32) -> f32 {\n let b = px.base;\n let lat = px.r.x * (px.g.x * (px.b.x * node(b, n) + px.b.y * node(b + 1u, n)) + px.g.y * (px.b.x * node(b + 5u, n) + px.b.y * node(b + 6u, n))) +\n px.r.y * (px.g.x * (px.b.x * node(b + 25u, n) + px.b.y * node(b + 26u, n)) + px.g.y * (px.b.x * node(b + 30u, n) + px.b.y * node(b + 31u, n)));\n return photo.black + photo.span * lat;\n}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (i >= params.lattice_total) { return; }\n var g = 0u;\n for (var k = 1u; k < params.groups; k = k + 1u) { if (i >= groups[k * GROUP + 4u]) { g = k; } }\n let base = g * GROUP;\n let lw = groups[base + 2u];\n let local = i - groups[base + 4u];\n let q = bitcast<i32>(groups[base + 1u]) + i32(local / lw);\n let p = bitcast<i32>(groups[base]) + i32(local % lw);\n material_uncertain = false;\n let N = photo.nodes;\n let out = i * N;\n let white = ${NODES}u * N;\n let k = D(photo.k_hi, photo.k_lo);\n let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);\n let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);\n let aspect = D(photo.aspect_hi, photo.aspect_lo);\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + N + n]; }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n return;\n }\n let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.hw_hi, photo.hw_lo));\n let y = d_mul(d_sub(aspect, v), D(photo.hh_hi, photo.hh_lo));\n if (photo.nearest != 0u) {\n let last_x = f32(photo.width - 1u);\n let last_y = f32(photo.height - 1u);\n let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + n] * pixel_value(px, n); }\n } else {\n let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);\n let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);\n let p00 = pixel_at(ax.i0, ay.i0);\n let p10 = pixel_at(ax.i1, ay.i0);\n let p01 = pixel_at(ax.i0, ay.i1);\n let p11 = pixel_at(ax.i1, ay.i1);\n let slack = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let s00 = pixel_value(p00, n);\n let s10 = pixel_value(p10, n);\n let s01 = pixel_value(p01, n);\n let s11 = pixel_value(p11, n);\n let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);\n if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }\n lattice_values[out + n] = node_table[white + n] * value;\n }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n}\n`;\n}\n\n/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */\nexport const IMAGE_PLANE_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(3) var<storage, read> table: array<f32>;\n@group(0) @binding(4) var<storage, read> lattice: array<f32>;\n@group(0) @binding(5) var<storage, read_write> sites: array<f32>;\n@group(0) @binding(6) var<storage, read> lattice_flags: array<u32>;\n${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (local >= params.count) { return; }\n let i = params.site_first + local;\n let ii = i % params.site_w;\n let jj = i / params.site_w;\n let c = channel_of(params.site_x0 + i32(ii), params.site_y0 + i32(jj));\n let T = params.taps;\n let N = bitcast<u32>(params.pad_h);\n var sum = 0.0;\n var flagged = false;\n for (var k = 0u; k < params.groups; k = k + 1u) {\n let g = k * GROUP;\n let lw = groups[g + 2u];\n let cw = lw - params.site_w + 1u;\n let ch = groups[g + 3u] - params.site_h + 1u;\n let lattice0 = groups[g + 4u] + jj * lw + ii;\n let tap0 = groups[g + 5u];\n var group_sum = 0.0;\n for (var dy = 0u; dy < ch; dy = dy + 1u) {\n let row = lattice0 + dy * lw;\n let row_tap = tap0 + dy * cw;\n var row_sum = 0.0;\n for (var dx = 0u; dx < cw; dx = dx + 1u) {\n let point = row + dx;\n let t = row_tap + dx;\n var cell = 0.0;\n var weight = 0.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let w = table[(n * 3u + c) * T + t];\n weight = weight + w;\n cell = cell + lattice[point * N + n] * w;\n }\n if (lattice_flags[point] != 0u && weight > 0.0) { flagged = true; }\n row_sum = row_sum + cell;\n }\n group_sum = group_sum + row_sum;\n }\n sum = sum + group_sum;\n }\n let e = sum + dark_of(c);\n write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, flagged));\n}\n`;\n\n/**\n * The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the\n * members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,\n * the reference's float64 values rounded once.\n */\nexport function imagePlaneQualityGather(signalInput: DisplayViewExpectedSignalInput): SignalGather {\n const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;\n const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups: SignalGather[\"groups\"] = [];\n const rects = tables.groups.map((group) => {\n const cw = signalInput.kernels ? group.cw : group.maxIx - group.minIx + 1, ch = signalInput.kernels ? group.ch : group.maxIy - group.minIy + 1;\n return { group, cw, ch, dpMin: group.minIx - R, dqMin: group.minIy - R };\n });\n let T = 0;\n for (const r of rects) {\n groups.push({ dpMin: r.dpMin, dqMin: r.dqMin, dpSpan: r.cw - 1, dqSpan: r.ch - 1, cu: 0.5 + r.group.fx - W / 2, cv: 0.5 + r.group.fy - H / 2, firstTap: T, cw: r.cw, ch: r.ch });\n T += r.cw * r.ch;\n }\n const table = new Float32Array(N * 3 * T), tiny = new Float64Array(T);\n let largestRadiance = 0;\n rects.forEach((r, g) => {\n const first = groups[g]!.firstTap;\n for (let n = 0; n < N; n++) for (let c = 0; c < 3; c++) {\n const values = new Float64Array(r.cw * r.ch);\n if (signalInput.kernels) values.set(r.group.perNode[n]![c]!);\n else for (const member of r.group.members) { const at = (member.iy - r.group.minIy) * r.cw + member.ix - r.group.minIx; values[at] = values[at]! + member.weight * A[c]![n]!; }\n for (let t = 0; t < values.length; t++) {\n const value = values[t]!;\n if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A combined kernel value is outside the float32 domain.\");\n table[(n * 3 + c) * T + first + t] = value;\n if (value > 0 && value < FLOAT32_MINIMUM_NORMAL) tiny[first + t] = Math.max(tiny[first + t]!, value);\n }\n }\n });\n const image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image!;\n for (const w of image.whiteRadiance) largestRadiance = Math.max(largestRadiance, w);\n let tinyMass = 0;\n for (let t = 0; t < T; t++) tinyMass += tiny[t]! * largestRadiance * N;\n return { groups, taps: new Uint32Array(0), tapCount: T, nodes: N, table, spread: new Float32Array(3 * Math.max(1, T)), materials: 0, tinyMass };\n}\n\n/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */\nexport function imagePlaneQualityPasses(runtime: WebGpuRuntime, gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewQualityImagePasses {\n const signalInput = gpu.signalInput, image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image;\n if (!image || !gpu.placement) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The setup carries no image-plane print and placement; use cpu-reference.\");\n const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput);\n let cw = 0, ch = 0;\n for (const g of gather.groups) { cw = Math.max(cw, g.cw); ch = Math.max(ch, g.ch); }\n // The lattice scale: the lattice formula's g with site kernels, else the environment's query lattice (checked within 2⁻⁴⁰ by queryGeometry).\n const g = 2 / signalInput.outputWidth, geometry = signalInput.kernels ? { scaleU: g, scaleV: -g } : queryGeometry(signalInput);\n const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect), k = gpu.placement.chartUnitsPerFrameUnit;\n // The node table: s_k(λ_n) per node, then W_n, then the surround's radiance per node.\n const nodeTable = new Float32Array((NODES + 2) * N);\n signalInput.wavelengthsNm.forEach((nm, n) => {\n const reflectances = scene.nodeReflectances(nm);\n for (let node = 0; node < NODES; node++) nodeTable[node * N + n] = reflectances[node]!;\n nodeTable[NODES * N + n] = image.whiteRadiance[n]!;\n nodeTable[(NODES + 1) * N + n] = image.surroundReflectance * image.whiteRadiance[n]!;\n });\n const P = IMAGE_PLANE_QUALITY_PARAMS, params = new ArrayBuffer(4 * P.words), words = new Uint32Array(params), floats = new Float32Array(params);\n words[P.width] = scene.width; words[P.height] = scene.height; words[P.nearest] = scene.interpolation === \"nearest\" ? 1 : 0; words[P.nodes] = N; words[P.zero] = 0;\n [floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU); [floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV); [floats[P.kHi], floats[P.kLo]] = dwSplit(k);\n [floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW); [floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH); [floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);\n floats[P.slackX] = 1e-14 * k * halfW; floats[P.slackY] = 1e-14 * k * halfH; floats[P.black] = scene.paperBlackReflectance; floats[P.span] = scene.paperWhiteReflectance - scene.paperBlackReflectance;\n const label = path === \"dekker\" ? \"image-plane\" : \"image-plane-fma\";\n const fractions = new Float64Array(3);\n const passes: DisplayViewQualityImagePasses = {\n gather, siteGamma: (IMAGE_PLANE_LATTICE_U + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,\n lattice: [\"photivra-display-view-quality-photo-lattice:\" + label, imagePlaneQualityLatticeWgsl(path)],\n sites: [\"photivra-display-view-quality-photo-sites\", IMAGE_PLANE_QUALITY_SITES_WGSL],\n async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal): Promise<{ values: GPUBuffer; flags: GPUBuffer; resolved: number }> {\n let values = band.buffer(\"photivra-display-view-photo-lattice\", latticeTotal * 4 * N, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n let flags = band.buffer(\"photivra-display-view-photo-lattice-flags\", latticeTotal * 4, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n const group0 = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: latticeParams } },\n { binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: values } }, { binding: 3, resource: { buffer: flags } }] });\n const [valueData, flagData] = await runComputePass(runtime, band, pipeline, [group0, bandGroup(band, pipeline.getBindGroupLayout(1), groupWords)],\n linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension),\n [{ buffer: values, bytes: latticeTotal * 4 * N }, { buffer: flags, bytes: latticeTotal * 4 }], signal);\n const valueArray = new Float32Array(valueData!, 0, latticeTotal * N), flagArray = new Uint32Array(flagData!, 0, latticeTotal), resolved = resolve(valueArray, flagArray, groupWords);\n if (resolved > 0) { values = band.upload(\"photivra-display-view-photo-lattice-resolved\", valueArray, BUFFER_USAGE.STORAGE); flags = band.upload(\"photivra-display-view-photo-flags-resolved\", flagArray, BUFFER_USAGE.STORAGE); }\n return { values, flags, resolved };\n }\n };\n function bandGroup(scope: GpuResourceScope, layout: GPUBindGroupLayout, groupWords: Uint32Array): GPUBindGroup {\n // The pixel rows the band's lattice windows reach (the print-local v of each window's first and last rows, two rows of margin).\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupFloats = new Float32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length);\n let low = Infinity, high = -Infinity;\n for (let gi = 0; gi < gather.groups.length; gi++) {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, cv = groupFloats[base + 7]!;\n for (const q of [ints[base + 1]!, ints[base + 1]! + groupWords[base + 3]! - 1]) {\n const Y = (scene.aspect - (q + cv) * geometry.scaleV * k) * halfH;\n low = Math.min(low, Y); high = Math.max(high, Y);\n }\n }\n const row0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), row1 = Math.max(row0, Math.min(scene.height - 1, Math.floor(high) + 2));\n const records = new Uint32Array((row1 - row0 + 1) * scene.width * RECORD_WORDS), recordFloats = new Float32Array(records.buffer);\n for (let j = row0, o = 0; j <= row1; j++) for (let i = 0; i < scene.width; i++, o += RECORD_WORDS) {\n const cell = scene.pixelLattice(j * scene.width + i, fractions);\n let word = Math.floor(cell / 25) | ((Math.floor(cell / 5) % 5) << 2) | ((cell % 5) << 4);\n for (let c = 0; c < 3; c++) {\n const f = fractions[c]!, high1 = f > 0.5;\n if (high1) word |= 1 << (6 + c);\n recordFloats[o + 1 + c] = high1 ? 1 - f : f;\n }\n records[o] = word;\n }\n const bandParams = params.slice(0);\n new Uint32Array(bandParams)[P.row0] = row0;\n if (records.byteLength > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"A band's pixel records exceed the device's storage binding limit.\");\n return runtime.device.createBindGroup({ layout, entries: [\n { binding: 0, resource: { buffer: scope.upload(\"photivra-display-view-photo-records\", records, BUFFER_USAGE.STORAGE) } },\n { binding: 1, resource: { buffer: scope.upload(\"photivra-display-view-photo-params\", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },\n { binding: 2, resource: { buffer: scope.upload(\"photivra-display-view-photo-nodes\", nodeTable, BUFFER_USAGE.STORAGE) } }] });\n }\n function resolve(values: Float32Array, flags: Uint32Array, groupWords: Uint32Array): number {\n if (!signalInput.kernels) return 0;\n // The reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV) and its radiance per node.\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupsOf = displayViewSignalTables(signalInput).groups;\n const W = signalInput.nativeWidth, H = signalInput.nativeHeight, scale = 2 / signalInput.outputWidth;\n let resolved = 0;\n groupsOf.forEach((group, gi) => {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, lx0 = ints[base]!, ly0 = ints[base + 1]!, lw = groupWords[base + 2]!, lh = groupWords[base + 3]!, first = groupWords[base + 4]!;\n for (let local = 0; local < lw * lh; local++) {\n if (flags[first + local] === 0) continue;\n const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);\n const u = latticeCoordinate(p, group.fx, W, scale), v = -latticeCoordinate(q, group.fy, H, scale);\n for (let n = 0; n < N; n++) values[(first + local) * N + n] = signalInput.scene.kind === \"radiance\" ? signalInput.scene.radianceAt(u, v, signalInput.wavelengthsNm[n]!) : 0;\n flags[first + local] = 0;\n resolved++;\n }\n });\n return resolved;\n }\n return passes;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"display-view-image-quality.js","sourceRoot":"","sources":["../../src/gpu/display-view-image-quality.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The image-plane print in the GPU display view's Quality passes (#581): diffraction kernels, the defocus disc, the anti-aliasing\n * filter's spots and AHD for a photo, loaded with the image-plane GPU module (only after `prepareImagePlaneScenes()`). Passes 1\n * and 2 are the print's own; pass 3 (the display pixels) and the host finishing are the Quality renderer's, unchanged.\n *\n * **What the reference computes** (expected-signal.ts, the radiance path): with site kernels, per site of channel c,\n * Σ_g Σ_d Σ_n C_n,c(d)·L_n(p_g + d), where L_n(p) is the print's spectral radiance at spectral node n at lattice point p (the\n * provider's `radianceAt` on the lattice formula) and C_n,c(d) the reference's combined kernel per node (`perNode` of its\n * tables: Σ_members w·A_c(λ_n)·K_n(t)); without kernels, per query offset w·Σ_n A_c(λ_n)·L_n(p_o), the same value as the direct\n * path's `signalAt`.\n *\n * **Pass 1 (lattice radiance).** One invocation per lattice point of each group: the exact point (p + c)·g in double-float32\n * (p + c is exact in float32, checked by the Quality host; the scale's hi + lo from float64), the print-local point, the print's\n * edge in the composite kernel's bound arithmetic, the pixel coordinates, the bilinear weights as the Speed module forms them,\n * and for every node n\n *\n * L_n = W_n·Σ_j b_j·(ρ_black + (ρ_white − ρ_black)·Σ_corners w_jk·s_k(λ_n)),\n *\n * the reference's arithmetic regrouped. Each pixel's lattice cell and fractions come from the host (float64, as the reference\n * decodes the pixel), per band for the rows its lattice windows reach: 16 bytes per pixel (the cell, and per channel the smaller\n * of f and 1 − f rounded once, with a bit for which), so every corner weight carries at most 2E relative per channel. All terms\n * are non-negative: L_n carries `IMAGE_PLANE_LATTICE_U`·E relative (s 1, three channel weights 6, the nested trilinear form 6,\n * the paper 3, the bilinear weights 4, their products and sums 4, the moved-weight budget 1, W_n 2: 27, declared 32), or the\n * point is flagged: on the print's edge, near a nearest-pixel edge, or where a bilinear weight moved by the point's error could\n * change the value by more than E of it (the Speed module's rule, per node).\n *\n * **Mixed light (#597).** Under `illumination` W_n is replaced by the irradiance / π at the point, E_n(p) = Σ_i w_i(p)·T_i,n +\n * Σ_j c_j,n·h_j/d_j³ (src/facade/mixed-illumination.ts), from the light table (`imagePlaneLightTable`: the parts and the spectra at\n * the nodes, no channel value). The blend's position t is formed in double-float32 with te, its error beyond its own low part; each\n * weight comes from its own distance to the segment's ends (rounded once: E relative, plus te times the segment's slope); a regions\n * edge, or a hard edge of equal stops, within the point's bound flags it; a choice of the neighbouring segment moves the weights by\n * at most te times the steepest slope; each point source's h/d³ is a bounded value (B arithmetic). The node value's computed bound\n * (the tables' rounding, the weights, the products and the sums) must stay within (IMAGE_PLANE_LIGHT_U − 1)·E of it, else the point\n * is flagged, so a lit point carries (IMAGE_PLANE_LATTICE_U + IMAGE_PLANE_LIGHT_U)·E relative.\n *\n * **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged\n * point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).\n * Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero\n * weight) is flagged and its display pixels are recomputed on the host.\n *\n * **Pass 2 (sites).** The blocked sum (#492's order) of Σ_n L_n·C_n,c(t) over each group's dense tap rectangle (without kernels the\n * members' span, zero where no member sits): a term carries L's bound, the table's rounding and the product, the node sum's N − 1,\n * and the row, group and total additions, so the electrons carry (IMAGE_PLANE_LATTICE_U + N + cw + ch + G + 4)·E relative.\n * Then the Quality site tail (readout moments, white clip, running bounds), unchanged.\n */\n\nimport { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from \"./image-kernel-execution.js\";\nimport { BUFFER_USAGE, linearDispatch, runComputePass, type GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { dwSplit } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { displayViewModuleCommonWgsl } from \"./display-view-composite-material-module.js\";\nimport { DISPLAY_VIEW_QUALITY_COMMON_WGSL, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE } from \"./wgsl-display-view-quality-kernel.js\";\nimport { latticeCoordinate, type DisplayViewQualityImagePasses, type SignalGather } from \"./display-view-quality-gpu.js\";\nimport { queryGeometry } from \"./display-view-speed-gpu.js\";\nimport { displayViewSignalTables, type DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport type { DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport { calculateIlluminantIrradianceScale, type ImagePlaneSignalScene } from \"../facade/scene-radiance.js\";\n\nconst E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;\n/** Relative roundings of a lattice point's radiance (see the module comment). */\nexport const IMAGE_PLANE_LATTICE_U = 32;\n/**\n * #597: relative roundings of the mixed light at a lattice point (a point whose computed bound exceeds IMAGE_PLANE_LIGHT_U − 1 is\n * flagged; see the module comment), added to IMAGE_PLANE_LATTICE_U for a lit print.\n */\nexport const IMAGE_PLANE_LIGHT_U = 40;\n/** #597: layout of the light table (float32 / u32 words; see `imagePlaneLightTable`). */\nexport const IMAGE_PLANE_LIGHT = /* @__PURE__ */ Object.freeze({ kind: 0, sources: 1, points: 2, regions: 3, outside: 4, regionsAt: 6, pointsAt: 7, tablesAt: 8, slack: 9,\n surround: 10, maximumInverse: 11, halfHi: 12, halfLo: 13, aU: 16, aV: 18, dU: 20, dV: 22, inverse: 24, stops: 26, regionWords: 9, pointWords: 6, maximumPoints: 8 });\n/** Lattice nodes of the print (5 × 5 × 5). */\nconst NODES = 125;\n/** Words per pixel record. */\nconst RECORD_WORDS = 4;\n/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */\nexport const IMAGE_PLANE_QUALITY_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, nodes: 3, row0: 4, zero: 5, lit: 6, suHi: 8, suLo: 9, svHi: 10, svLo: 11,\n kHi: 12, kLo: 13, halfWHi: 14, halfWLo: 15, halfHHi: 16, halfHLo: 17, aspectHi: 18, aspectLo: 19, slackX: 20, slackY: 21, black: 22, span: 23, words: 24 });\nconst FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;\n\nconst L = IMAGE_PLANE_LIGHT;\n/**\n * #597: the mixed light at a lattice point (see the module comment): `light_prepare` once per point (the blend's source and weights\n * with their error bound, and each point source's h/d³ as a bounded value), then `light_at(n)` per node, flagging the point when the\n * computed bound exceeds (IMAGE_PLANE_LIGHT_U − 1)·E of the value.\n */\nconst LIGHT_WGSL = /* wgsl */ `\nvar<private> blend_i: u32;\nvar<private> blend_w0: f32;\nvar<private> blend_w1: f32;\nvar<private> blend_e0: f32;\nvar<private> blend_e1: f32;\nvar<private> blend_amb: f32;\nvar<private> fall_v: array<f32, ${L.maximumPoints}>;\nvar<private> fall_e: array<f32, ${L.maximumPoints}>;\nfn lw(i: u32) -> u32 { return bitcast<u32>(light[i]); }\nfn ld(i: u32) -> D { return D(light[i], light[i + 1u]); }\n// a > b for double-float32 values.\nfn d_gt(a: D, b: D) -> bool { let d = d_sub(a, b); return d.h > 0.0 || (d.h == 0.0 && d.l > 0.0); }\n// sqrt of a bounded value: |√x − √x̃| ≤ e / (2√(v − e)), and WGSL's sqrt within 5E (inherited from 1 / inverseSqrt).\nfn bsqrt(a: B) -> B {\n let v = sqrt(max(a.v, 0.0));\n let low = a.v - SAFETY * a.e;\n if (!(low > 0.0)) { material_uncertain = true; return B(v, 3.0e38); }\n return B(v, a.e / (2.0 * sqrt(low)) + 5.0 * E * v);\n}\nfn light_prepare(u: D, v: D) {\n let kind = lw(${L.kind}u);\n let S = lw(${L.sources}u);\n blend_i = 0u; blend_w0 = 1.0; blend_w1 = 0.0; blend_e0 = 0.0; blend_e1 = 0.0; blend_amb = 0.0;\n // A point's error beyond its double-float32 arithmetic: the recovery and the reference's own rounding (the host's slack).\n let ce = 4.0 * DW * (abs(u.h) + abs(v.h) + 1.0) + light[${L.slack}u];\n if (kind == 3u) {\n var source = lw(${L.outside}u);\n let bu = bd(u);\n let bv = bd(v);\n for (var r = 0u; r < lw(${L.regions}u); r = r + 1u) {\n let b = lw(${L.regionsAt}u) + r * ${L.regionWords}u;\n if (ge(bu, bd(ld(b + 1u))) && le(bu, bd(ld(b + 3u))) && ge(bv, bd(ld(b + 5u))) && le(bv, bd(ld(b + 7u)))) { source = lw(b); break; }\n }\n blend_i = source;\n } else if (kind != 0u) {\n // t in double-float32 and te, its error bound beyond its own low part (the arithmetic and the point's error).\n var t: D;\n var te: f32;\n let a_u = d_sub(u, ld(${L.aU}u));\n let a_v = d_sub(v, ld(${L.aV}u));\n let inv = ld(${L.inverse}u);\n let ce_t = ce + 4.0 * DW * (abs(light[${L.aU}u]) + abs(light[${L.aV}u]));\n if (kind == 1u) {\n let x = d_mul(a_u, ld(${L.dU}u));\n let y = d_mul(a_v, ld(${L.dV}u));\n let scale = (abs(x.h) + abs(y.h)) * abs(inv.h);\n t = d_mul(d_add(x, y), inv);\n te = 8.0 * DW * (scale + 1.0) + ce_t * (abs(light[${L.dU}u]) + abs(light[${L.dV}u])) * abs(inv.h);\n if (t.h < 0.0) { t = D(0.0, 0.0); }\n if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }\n } else {\n // The distance in double-float32: the float32 root and one Newton step.\n let dd = d_add(d_mul(a_u, a_u), d_mul(a_v, a_v));\n let r0 = sqrt(max(dd.h, 0.0));\n var r = D(r0, 0.0);\n if (r0 > 0.0) { let res = d_sub(dd, d_mul(D(r0, 0.0), D(r0, 0.0))); r = quick_two_sum(r0, ob(ob(res.h + res.l) / ob(2.0 * r0))); }\n t = d_mul(r, inv);\n te = 8.0 * DW * (abs(t.h) + 1.0) + 2.0 * ce_t * abs(inv.h);\n if (d_gt(t, D(1.0, 0.0))) { t = D(1.0, 0.0); }\n }\n // The segment [s_i, s_i+1) that holds t (the reference's order); a hard edge (equal stops) within the bound of t flags the point.\n var i = 0u;\n loop { if (i + 1u >= S) { break; } if (d_gt(ld(${L.stops}u + (i + 1u) * 4u), t)) { break; } i = i + 1u; }\n for (var k = 0u; k + 1u < S; k = k + 1u) {\n if (light[${L.stops}u + k * 4u + 2u] == 0.0 && abs(d_sub(t, ld(${L.stops}u + k * 4u)).h) <= SAFETY * te + DW * (abs(t.h) + 1.0)) { material_uncertain = true; }\n }\n let si = ld(${L.stops}u + i * 4u);\n // A neighbouring segment's choice moves the weights by at most t's bound times the steepest segment's slope.\n blend_amb = te * light[${L.maximumInverse}u];\n if (i + 1u < S && d_gt(t, si)) {\n // Each weight from its own distance to the segment's ends (double-float32, rounded once), so a small weight keeps a small relative error.\n let inv_i = ld(${L.stops}u + i * 4u + 2u);\n let fd = d_mul(d_sub(t, si), inv_i);\n let gd = d_mul(d_sub(ld(${L.stops}u + (i + 1u) * 4u), t), inv_i);\n blend_w1 = clamp(ob(fd.h + fd.l), 0.0, 1.0);\n blend_w0 = clamp(ob(gd.h + gd.l), 0.0, 1.0);\n let shared_e = (te + 4.0 * DW * (abs(t.h) + abs(si.h) + 1.0)) * abs(inv_i.h);\n blend_e1 = E * blend_w1 + shared_e;\n blend_e0 = E * blend_w0 + shared_e;\n }\n blend_i = i;\n }\n // Point sources: h/d³ at the point in metres (print-local × the print's half width), as bounded values.\n let half = ld(${L.halfHi}u);\n let x = d_mul(u, half);\n let y = d_mul(v, half);\n let cm = ce * abs(half.h) + 4.0 * DW * (abs(x.h) + abs(y.h));\n for (var j = 0u; j < lw(${L.points}u); j = j + 1u) {\n let b = lw(${L.pointsAt}u) + j * ${L.pointWords}u;\n var dx = bds(d_sub(x, ld(b)), abs(x.h) + abs(light[b]));\n dx.e = dx.e + cm;\n var dy = bds(d_sub(y, ld(b + 2u)), abs(y.h) + abs(light[b + 2u]));\n dy.e = dy.e + cm;\n let h = bd(ld(b + 4u));\n let d2 = badd(badd(bmul(dx, dx), bmul(dy, dy)), bmul(h, h));\n let fall = bdiv(h, bmul(d2, bsqrt(d2)));\n fall_v[j] = fall.v;\n fall_e[j] = fall.e;\n }\n}\n// The irradiance / π at node n of the prepared point: the blend's sources and the point sources from their spectra at the nodes.\nfn light_at(n: u32) -> f32 {\n let N = photo.nodes;\n let S = lw(${L.sources}u);\n let P = lw(${L.points}u);\n let tab = lw(${L.tablesAt}u);\n var value = 0.0;\n var err = 0.0;\n if (S > 0u) {\n let ti = light[tab + blend_i * N + n];\n var top = ti;\n for (var s = 0u; s < S; s = s + 1u) { top = max(top, light[tab + s * N + n]); }\n var tj = 0.0;\n if (blend_w1 == 0.0) { value = ti; } else { tj = light[tab + (blend_i + 1u) * N + n]; value = blend_w0 * ti + blend_w1 * tj; }\n err = 3.0 * E * value + blend_e0 * ti + blend_e1 * tj + blend_amb * top;\n }\n for (var j = 0u; j < P; j = j + 1u) {\n let c = light[tab + (S + j) * N + n];\n let term = c * fall_v[j];\n value = value + term;\n err = err + c * fall_e[j] + 2.0 * E * term;\n }\n err = err + f32(P + 1u) * E * value;\n if (!(err <= ${IMAGE_PLANE_LIGHT_U - 1}.0 * E * value)) { material_uncertain = true; }\n return value;\n}\n`;\n\n/**\n * #597: the light table of a lit print at the spectral nodes (the layout `IMAGE_PLANE_LIGHT`): the blend (kind, sources, its geometry\n * and stops with each segment's inverse width, 0 at a hard edge), the regions, the point sources (x, y, h in metres), then per node\n * T_i,n = k_i·S_i(λ_n)/π for each blend source and c_j·S_j(λ_n)/π for each point source: spectra only, no channel value. `slack` is the\n * reference's own rounding of a query point in print-local units.\n */\nexport function imagePlaneLightTable(light: NonNullable<ImagePlaneSignalScene[\"image\"][\"light\"]>, wavelengths: readonly number[], surroundReflectance: number, slack: number):\n { table: Float32Array<ArrayBuffer>; largestIrradiance: number } {\n const resolved = light.layout;\n if (!resolved) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The print's mixed light carries no parts for the GPU; use cpu-reference.\");\n // The blend's sources come first, then the point sources (their report carries the position and the intensity, c = I·k(1 lux)).\n const blend = resolved.blend as { kind: string; from?: { u: number; v: number }; to?: { u: number; v: number }; center?: { u: number; v: number }; radius?: number;\n regions?: readonly { source: number; uMin: number; uMax: number; vMin: number; vMax: number }[]; outsideSource?: number; stops?: readonly number[] } | null;\n const sources = resolved.sources.filter((s) => s.report.position === undefined), points = resolved.sources.filter((s) => s.report.position !== undefined).map((s) => {\n const position = s.report.position as { xM: number; yM: number; heightM: number };\n return { ...position, scale: calculateIlluminantIrradianceScale(s.illuminant.shape, 1) * (s.report.intensityCandela as number), shape: s.illuminant.shape };\n });\n const S = sources.length, P = points.length, N = wavelengths.length, regions = blend?.regions ?? [];\n if (P > L.maximumPoints) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"More point sources than the GPU pass holds.\");\n const regionsAt = L.stops + 4 * S, pointsAt = regionsAt + L.regionWords * regions.length, tablesAt = pointsAt + L.pointWords * P;\n const table = new Float32Array(tablesAt + (S + P) * N), words = new Uint32Array(table.buffer);\n const pair = (at: number, x: number): void => { [table[at], table[at + 1]] = dwSplit(x); };\n words[L.kind] = blend === null ? 0 : blend.kind === \"linear-gradient\" ? 1 : blend.kind === \"radial\" ? 2 : 3;\n words[L.sources] = S; words[L.points] = P; words[L.regions] = regions.length; words[L.outside] = blend?.outsideSource ?? 0;\n words[L.regionsAt] = regionsAt; words[L.pointsAt] = pointsAt; words[L.tablesAt] = tablesAt;\n table[L.slack] = slack; table[L.surround] = surroundReflectance;\n pair(L.halfHi, light.halfWidthM);\n if (blend?.kind === \"linear-gradient\") {\n const from = blend.from!, du = blend.to!.u - from.u, dv = blend.to!.v - from.v;\n pair(L.aU, from.u); pair(L.aV, from.v); pair(L.dU, du); pair(L.dV, dv); pair(L.inverse, 1 / (du * du + dv * dv));\n } else if (blend?.kind === \"radial\") { pair(L.aU, blend.center!.u); pair(L.aV, blend.center!.v); pair(L.inverse, 1 / blend.radius!); }\n let maximumInverse = 0;\n const stops = blend?.stops ?? [];\n stops.forEach((stop, i) => {\n const next = stops[i + 1], inverse = next !== undefined && next > stop ? 1 / (next - stop) : 0;\n pair(L.stops + 4 * i, stop); pair(L.stops + 4 * i + 2, inverse);\n maximumInverse = Math.max(maximumInverse, inverse);\n });\n table[L.maximumInverse] = maximumInverse;\n regions.forEach((r, i) => {\n const at = regionsAt + L.regionWords * i;\n words[at] = r.source; pair(at + 1, r.uMin); pair(at + 3, r.uMax); pair(at + 5, r.vMin); pair(at + 7, r.vMax);\n });\n points.forEach((p, j) => { const at = pointsAt + L.pointWords * j; pair(at, p.xM); pair(at + 2, p.yM); pair(at + 4, p.heightM); });\n // The largest irradiance / π anywhere (the blend's largest source and every point at normal incidence, 1/h²), for the tiny-value bound.\n let largestIrradiance = 0;\n wavelengths.forEach((nm, n) => {\n let top = 0, atNormal = 0;\n sources.forEach((source, i) => { const value = source.irradianceScale * source.illuminant.shape.evaluate(nm) / Math.PI; table[tablesAt + i * N + n] = value; top = Math.max(top, value); });\n points.forEach((p, j) => { const value = p.scale * p.shape.evaluate(nm) / Math.PI; table[tablesAt + (S + j) * N + n] = value; atNormal += value / (p.heightM * p.heightM); });\n largestIrradiance = Math.max(largestIrradiance, top + atNormal);\n });\n for (let i = tablesAt; i < table.length; i++) if (!(table[i]! >= 0 && table[i]! <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A light's spectrum is outside the float32 domain.\");\n return { table, largestIrradiance };\n}\n\n/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */\nconst photoCommon = (path: ChartCompositeProductPath): string => displayViewModuleCommonWgsl(path).replace(/\\blattice\\.zero\\b/gu, \"photo.zero\");\n\n/** Pass 1's WGSL (see the module comment). */\nexport function imagePlaneQualityLatticeWgsl(path: ChartCompositeProductPath): string {\n return `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;\n@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;\nstruct PhotoParams {\n width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, lit: u32, pad1: u32,\n su_hi: f32, su_lo: f32, sv_hi: f32, sv_lo: f32, k_hi: f32, k_lo: f32, hw_hi: f32, hw_lo: f32,\n hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,\n};\n@group(1) @binding(0) var<storage, read> records: array<u32>;\n@group(1) @binding(1) var<uniform> photo: PhotoParams;\n@group(1) @binding(2) var<storage, read> node_table: array<f32>;\n@group(1) @binding(3) var<storage, read> light: array<f32>;\n${photoCommon(path)}\n${LIGHT_WGSL}\nfn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }\nstruct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }\nfn bilinear_axis(s: D, n: u32) -> Axis {\n var f0 = floor(s.h);\n if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }\n let frac = d_sub(s, D(f0, 0.0));\n let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);\n var w0 = 0.0; var w1 = 0.0;\n if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }\n let last = i32(n) - 1;\n return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);\n}\n// A channel's two corner weights from its record: the stored smaller one and 1 minus it.\nfn channel_weights(word: u32, bit: u32, small: f32) -> vec2<f32> {\n if (((word >> bit) & 1u) == 0u) { return vec2<f32>(ob(1.0 - small), small); }\n return vec2<f32>(small, ob(1.0 - small));\n}\nstruct Pixel { base: u32, r: vec2<f32>, g: vec2<f32>, b: vec2<f32> }\nfn pixel_at(i: u32, j: u32) -> Pixel {\n let o = ((j - photo.row0) * photo.width + i) * ${RECORD_WORDS}u;\n let w = records[o];\n return Pixel((w & 3u) * 25u + ((w >> 2u) & 3u) * 5u + ((w >> 4u) & 3u), channel_weights(w, 6u, bitcast<f32>(records[o + 1u])),\n channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));\n}\nfn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }\n// The white radiance at node n, or (#597) under mixed light the irradiance / π at the point.\nfn white_at(white: u32, n: u32) -> f32 { if (photo.lit != 0u) { return light_at(n); } return node_table[white + n]; }\n// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.\nfn pixel_value(px: Pixel, n: u32) -> f32 {\n let b = px.base;\n let lat = px.r.x * (px.g.x * (px.b.x * node(b, n) + px.b.y * node(b + 1u, n)) + px.g.y * (px.b.x * node(b + 5u, n) + px.b.y * node(b + 6u, n))) +\n px.r.y * (px.g.x * (px.b.x * node(b + 25u, n) + px.b.y * node(b + 26u, n)) + px.g.y * (px.b.x * node(b + 30u, n) + px.b.y * node(b + 31u, n)));\n return photo.black + photo.span * lat;\n}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (i >= params.lattice_total) { return; }\n var g = 0u;\n for (var k = 1u; k < params.groups; k = k + 1u) { if (i >= groups[k * GROUP + 4u]) { g = k; } }\n let base = g * GROUP;\n let lw = groups[base + 2u];\n let local = i - groups[base + 4u];\n let q = bitcast<i32>(groups[base + 1u]) + i32(local / lw);\n let p = bitcast<i32>(groups[base]) + i32(local % lw);\n material_uncertain = false;\n let N = photo.nodes;\n let out = i * N;\n let white = ${NODES}u * N;\n let k = D(photo.k_hi, photo.k_lo);\n let u = d_mul(d_mul(D(f32(p) + bitcast<f32>(groups[base + 6u]), 0.0), D(photo.su_hi, photo.su_lo)), k);\n let v = d_mul(d_mul(D(f32(q) + bitcast<f32>(groups[base + 7u]), 0.0), D(photo.sv_hi, photo.sv_lo)), k);\n let aspect = D(photo.aspect_hi, photo.aspect_lo);\n // #597: under mixed light the irradiance at the point (its blend and point sources) replaces the white radiance per node.\n if (photo.lit != 0u) { light_prepare(u, v); }\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {\n for (var n = 0u; n < N; n = n + 1u) {\n if (photo.lit != 0u) { lattice_values[out + n] = light[${L.surround}u] * light_at(n); } else { lattice_values[out + n] = node_table[white + N + n]; }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n return;\n }\n let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.hw_hi, photo.hw_lo));\n let y = d_mul(d_sub(aspect, v), D(photo.hh_hi, photo.hh_lo));\n if (photo.nearest != 0u) {\n let last_x = f32(photo.width - 1u);\n let last_y = f32(photo.height - 1u);\n let px = pixel_at(u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x)), u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y)));\n for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = white_at(white, n) * pixel_value(px, n); }\n } else {\n let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);\n let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);\n let p00 = pixel_at(ax.i0, ay.i0);\n let p10 = pixel_at(ax.i1, ay.i0);\n let p01 = pixel_at(ax.i0, ay.i1);\n let p11 = pixel_at(ax.i1, ay.i1);\n let slack = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let s00 = pixel_value(p00, n);\n let s10 = pixel_value(p10, n);\n let s01 = pixel_value(p01, n);\n let s11 = pixel_value(p11, n);\n let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);\n if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }\n lattice_values[out + n] = white_at(white, n) * value;\n }\n }\n lattice_flags[i] = select(0u, 1u, material_uncertain);\n}\n`;\n}\n\n/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */\nexport const IMAGE_PLANE_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}\n@group(0) @binding(1) var<storage, read> groups: array<u32>;\n@group(0) @binding(3) var<storage, read> table: array<f32>;\n@group(0) @binding(4) var<storage, read> lattice: array<f32>;\n@group(0) @binding(5) var<storage, read_write> sites: array<f32>;\n@group(0) @binding(6) var<storage, read> lattice_flags: array<u32>;\n${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}\n@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {\n let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;\n if (local >= params.count) { return; }\n let i = params.site_first + local;\n let ii = i % params.site_w;\n let jj = i / params.site_w;\n let c = channel_of(params.site_x0 + i32(ii), params.site_y0 + i32(jj));\n let T = params.taps;\n let N = bitcast<u32>(params.pad_h);\n var sum = 0.0;\n var flagged = false;\n for (var k = 0u; k < params.groups; k = k + 1u) {\n let g = k * GROUP;\n let lw = groups[g + 2u];\n let cw = lw - params.site_w + 1u;\n let ch = groups[g + 3u] - params.site_h + 1u;\n let lattice0 = groups[g + 4u] + jj * lw + ii;\n let tap0 = groups[g + 5u];\n var group_sum = 0.0;\n for (var dy = 0u; dy < ch; dy = dy + 1u) {\n let row = lattice0 + dy * lw;\n let row_tap = tap0 + dy * cw;\n var row_sum = 0.0;\n for (var dx = 0u; dx < cw; dx = dx + 1u) {\n let point = row + dx;\n let t = row_tap + dx;\n var cell = 0.0;\n var weight = 0.0;\n for (var n = 0u; n < N; n = n + 1u) {\n let w = table[(n * 3u + c) * T + t];\n weight = weight + w;\n cell = cell + lattice[point * N + n] * w;\n }\n if (lattice_flags[point] != 0u && weight > 0.0) { flagged = true; }\n row_sum = row_sum + cell;\n }\n group_sum = group_sum + row_sum;\n }\n sum = sum + group_sum;\n }\n let e = sum + dark_of(c);\n write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, flagged));\n}\n`;\n\n/**\n * The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the\n * members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,\n * the reference's float64 values rounded once.\n */\nexport function imagePlaneQualityGather(signalInput: DisplayViewExpectedSignalInput, largestLitRadiance?: number): SignalGather {\n const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;\n const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups: SignalGather[\"groups\"] = [];\n const rects = tables.groups.map((group) => {\n const cw = signalInput.kernels ? group.cw : group.maxIx - group.minIx + 1, ch = signalInput.kernels ? group.ch : group.maxIy - group.minIy + 1;\n return { group, cw, ch, dpMin: group.minIx - R, dqMin: group.minIy - R };\n });\n let T = 0;\n for (const r of rects) {\n groups.push({ dpMin: r.dpMin, dqMin: r.dqMin, dpSpan: r.cw - 1, dqSpan: r.ch - 1, cu: 0.5 + r.group.fx - W / 2, cv: 0.5 + r.group.fy - H / 2, firstTap: T, cw: r.cw, ch: r.ch });\n T += r.cw * r.ch;\n }\n const table = new Float32Array(N * 3 * T), tiny = new Float64Array(T);\n let largestRadiance = 0;\n rects.forEach((r, g) => {\n const first = groups[g]!.firstTap;\n for (let n = 0; n < N; n++) for (let c = 0; c < 3; c++) {\n const values = new Float64Array(r.cw * r.ch);\n if (signalInput.kernels) values.set(r.group.perNode[n]![c]!);\n else for (const member of r.group.members) { const at = (member.iy - r.group.minIy) * r.cw + member.ix - r.group.minIx; values[at] = values[at]! + member.weight * A[c]![n]!; }\n for (let t = 0; t < values.length; t++) {\n const value = values[t]!;\n if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A combined kernel value is outside the float32 domain.\");\n table[(n * 3 + c) * T + first + t] = value;\n if (value > 0 && value < FLOAT32_MINIMUM_NORMAL) tiny[first + t] = Math.max(tiny[first + t]!, value);\n }\n }\n });\n const image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image!;\n if (largestLitRadiance !== undefined) largestRadiance = largestLitRadiance;\n else for (const w of image.whiteRadiance) largestRadiance = Math.max(largestRadiance, w);\n let tinyMass = 0;\n for (let t = 0; t < T; t++) tinyMass += tiny[t]! * largestRadiance * N;\n return { groups, taps: new Uint32Array(0), tapCount: T, nodes: N, table, spread: new Float32Array(3 * Math.max(1, T)), materials: 0, tinyMass };\n}\n\n/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */\nexport function imagePlaneQualityPasses(runtime: WebGpuRuntime, gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewQualityImagePasses {\n const signalInput = gpu.signalInput, image = (signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image;\n if (!image || !gpu.placement) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The setup carries no image-plane print and placement; use cpu-reference.\");\n // #597: under mixed light, the light's table (its parts and spectra at the nodes), which replaces the white radiance in pass 1.\n const k0 = gpu.placement.chartUnitsPerFrameUnit, lit = image.light ? imagePlaneLightTable(image.light, signalInput.wavelengthsNm, image.surroundReflectance, 1e-14 * k0) : undefined;\n const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput, lit?.largestIrradiance);\n let cw = 0, ch = 0;\n for (const g of gather.groups) { cw = Math.max(cw, g.cw); ch = Math.max(ch, g.ch); }\n // The lattice scale: the lattice formula's g with site kernels, else the environment's query lattice (checked within 2⁻⁴⁰ by queryGeometry).\n const g = 2 / signalInput.outputWidth, geometry = signalInput.kernels ? { scaleU: g, scaleV: -g } : queryGeometry(signalInput);\n const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect), k = gpu.placement.chartUnitsPerFrameUnit;\n // The node table: s_k(λ_n) per node, then W_n, then the surround's radiance per node.\n const nodeTable = new Float32Array((NODES + 2) * N);\n signalInput.wavelengthsNm.forEach((nm, n) => {\n const reflectances = scene.nodeReflectances(nm);\n for (let node = 0; node < NODES; node++) nodeTable[node * N + n] = reflectances[node]!;\n nodeTable[NODES * N + n] = image.whiteRadiance[n]!;\n nodeTable[(NODES + 1) * N + n] = image.surroundReflectance * image.whiteRadiance[n]!;\n });\n const P = IMAGE_PLANE_QUALITY_PARAMS, params = new ArrayBuffer(4 * P.words), words = new Uint32Array(params), floats = new Float32Array(params);\n words[P.width] = scene.width; words[P.height] = scene.height; words[P.nearest] = scene.interpolation === \"nearest\" ? 1 : 0; words[P.nodes] = N; words[P.zero] = 0; words[P.lit] = lit ? 1 : 0;\n const lightTable = lit ? lit.table : new Float32Array(1);\n [floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU); [floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV); [floats[P.kHi], floats[P.kLo]] = dwSplit(k);\n [floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW); [floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH); [floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);\n floats[P.slackX] = 1e-14 * k * halfW; floats[P.slackY] = 1e-14 * k * halfH; floats[P.black] = scene.paperBlackReflectance; floats[P.span] = scene.paperWhiteReflectance - scene.paperBlackReflectance;\n const label = path === \"dekker\" ? \"image-plane\" : \"image-plane-fma\";\n const fractions = new Float64Array(3);\n const passes: DisplayViewQualityImagePasses = {\n gather, siteGamma: (IMAGE_PLANE_LATTICE_U + (lit ? IMAGE_PLANE_LIGHT_U : 0) + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,\n lattice: [\"photivra-display-view-quality-photo-lattice:\" + label, imagePlaneQualityLatticeWgsl(path)],\n sites: [\"photivra-display-view-quality-photo-sites\", IMAGE_PLANE_QUALITY_SITES_WGSL],\n async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal): Promise<{ values: GPUBuffer; flags: GPUBuffer; resolved: number }> {\n let values = band.buffer(\"photivra-display-view-photo-lattice\", latticeTotal * 4 * N, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n let flags = band.buffer(\"photivra-display-view-photo-lattice-flags\", latticeTotal * 4, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);\n const group0 = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: latticeParams } },\n { binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: values } }, { binding: 3, resource: { buffer: flags } }] });\n const [valueData, flagData] = await runComputePass(runtime, band, pipeline, [group0, bandGroup(band, pipeline.getBindGroupLayout(1), groupWords)],\n linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension),\n [{ buffer: values, bytes: latticeTotal * 4 * N }, { buffer: flags, bytes: latticeTotal * 4 }], signal);\n const valueArray = new Float32Array(valueData!, 0, latticeTotal * N), flagArray = new Uint32Array(flagData!, 0, latticeTotal), resolved = resolve(valueArray, flagArray, groupWords);\n if (resolved > 0) { values = band.upload(\"photivra-display-view-photo-lattice-resolved\", valueArray, BUFFER_USAGE.STORAGE); flags = band.upload(\"photivra-display-view-photo-flags-resolved\", flagArray, BUFFER_USAGE.STORAGE); }\n return { values, flags, resolved };\n }\n };\n function bandGroup(scope: GpuResourceScope, layout: GPUBindGroupLayout, groupWords: Uint32Array): GPUBindGroup {\n // The pixel rows the band's lattice windows reach (the print-local v of each window's first and last rows, two rows of margin).\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupFloats = new Float32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length);\n let low = Infinity, high = -Infinity;\n for (let gi = 0; gi < gather.groups.length; gi++) {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, cv = groupFloats[base + 7]!;\n for (const q of [ints[base + 1]!, ints[base + 1]! + groupWords[base + 3]! - 1]) {\n const Y = (scene.aspect - (q + cv) * geometry.scaleV * k) * halfH;\n low = Math.min(low, Y); high = Math.max(high, Y);\n }\n }\n const row0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), row1 = Math.max(row0, Math.min(scene.height - 1, Math.floor(high) + 2));\n const records = new Uint32Array((row1 - row0 + 1) * scene.width * RECORD_WORDS), recordFloats = new Float32Array(records.buffer);\n for (let j = row0, o = 0; j <= row1; j++) for (let i = 0; i < scene.width; i++, o += RECORD_WORDS) {\n const cell = scene.pixelLattice(j * scene.width + i, fractions);\n let word = Math.floor(cell / 25) | ((Math.floor(cell / 5) % 5) << 2) | ((cell % 5) << 4);\n for (let c = 0; c < 3; c++) {\n const f = fractions[c]!, high1 = f > 0.5;\n if (high1) word |= 1 << (6 + c);\n recordFloats[o + 1 + c] = high1 ? 1 - f : f;\n }\n records[o] = word;\n }\n const bandParams = params.slice(0);\n new Uint32Array(bandParams)[P.row0] = row0;\n if (records.byteLength > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"A band's pixel records exceed the device's storage binding limit.\");\n return runtime.device.createBindGroup({ layout, entries: [\n { binding: 0, resource: { buffer: scope.upload(\"photivra-display-view-photo-records\", records, BUFFER_USAGE.STORAGE) } },\n { binding: 1, resource: { buffer: scope.upload(\"photivra-display-view-photo-params\", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },\n { binding: 2, resource: { buffer: scope.upload(\"photivra-display-view-photo-nodes\", nodeTable, BUFFER_USAGE.STORAGE) } },\n { binding: 3, resource: { buffer: scope.upload(\"photivra-display-view-photo-light\", lightTable, BUFFER_USAGE.STORAGE) } }] });\n }\n function resolve(values: Float32Array, flags: Uint32Array, groupWords: Uint32Array): number {\n if (!signalInput.kernels) return 0;\n // The reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV) and its radiance per node.\n const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupsOf = displayViewSignalTables(signalInput).groups;\n const W = signalInput.nativeWidth, H = signalInput.nativeHeight, scale = 2 / signalInput.outputWidth;\n let resolved = 0;\n groupsOf.forEach((group, gi) => {\n const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, lx0 = ints[base]!, ly0 = ints[base + 1]!, lw = groupWords[base + 2]!, lh = groupWords[base + 3]!, first = groupWords[base + 4]!;\n for (let local = 0; local < lw * lh; local++) {\n if (flags[first + local] === 0) continue;\n const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);\n const u = latticeCoordinate(p, group.fx, W, scale), v = -latticeCoordinate(q, group.fy, H, scale);\n for (let n = 0; n < N; n++) values[(first + local) * N + n] = signalInput.scene.kind === \"radiance\" ? signalInput.scene.radianceAt(u, v, signalInput.wavelengthsNm[n]!) : 0;\n flags[first + local] = 0;\n resolved++;\n }\n });\n return resolved;\n }\n return passes;\n}\n"]}
|