@photivra/engine 2.20.0 → 2.21.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 +178 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +7 -0
- package/dist/capture/dng-lens-correction-opcodes.d.ts +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
- package/dist/capture/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/native-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +41 -73
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +12 -3
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- 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/equipment/generic-lens-catalogue.d.ts +3 -2
- package/dist/equipment/generic-lens-catalogue.js +18 -5
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +16 -11
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +2 -2
- package/dist/facade/catalogue-lens-signal.js +58 -71
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +2 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +6 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.js +25 -23
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.js +5 -23
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +1 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.js +9 -1
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +2 -2
- package/dist/facade/generic-lens-capture.js +2 -2
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +353 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +129 -4
- package/dist/facade/scene-radiance.js +48 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +39 -10
- package/dist/facade/simulate-capture.js +252 -92
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +14 -6
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-material-module.d.ts +86 -0
- package/dist/gpu/display-view-image-material-module.js +339 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +45 -0
- package/dist/gpu/display-view-image-quality.js +380 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +67 -1
- package/dist/gpu/display-view-quality-gpu.js +136 -205
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +51 -3
- package/dist/gpu/display-view-speed-gpu.js +131 -93
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +29 -15
- package/dist/gpu/wgsl-display-view-quality-kernel.js +58 -36
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +21 -6
- package/dist/gpu/wgsl-display-view-speed-kernel.js +46 -11
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.js +54 -46
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/output/developed-lens-corrections.d.ts +19 -5
- package/dist/output/developed-lens-corrections.js +28 -9
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -5
- package/dist/output/generic-lens-corrections.js +52 -8
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/sensor/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +10 -0
- package/dist/sensor/sensor-characteristics.js +20 -9
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +98 -1
- package/docs/QUICK_START.md +125 -0
- package/docs/USAGE.md +92 -1
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +10 -5
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +207 -2
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +9 -1
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +26 -6
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -4
- package/docs/api/output-print-plan.md +35 -1
- package/llms.txt +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* The image-plane print in the GPU display view's Quality passes (#581): diffraction kernels, the defocus disc, the anti-aliasing
|
|
4
|
+
* filter's spots and AHD for a photo, loaded with the image-plane GPU module (only after `prepareImagePlaneScenes()`). Passes 1
|
|
5
|
+
* and 2 are the print's own; pass 3 (the display pixels) and the host finishing are the Quality renderer's, unchanged.
|
|
6
|
+
*
|
|
7
|
+
* **What the reference computes** (expected-signal.ts, the radiance path): with site kernels, per site of channel c,
|
|
8
|
+
* Σ_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
|
|
9
|
+
* provider's `radianceAt` on the lattice formula) and C_n,c(d) the reference's combined kernel per node (`perNode` of its
|
|
10
|
+
* 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
|
|
11
|
+
* path's `signalAt`.
|
|
12
|
+
*
|
|
13
|
+
* **Pass 1 (lattice radiance).** One invocation per lattice point of each group: the exact point (p + c)·g in double-float32
|
|
14
|
+
* (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
|
|
15
|
+
* edge in the composite kernel's bound arithmetic, the pixel coordinates, the bilinear weights as the Speed module forms them,
|
|
16
|
+
* and for every node n
|
|
17
|
+
*
|
|
18
|
+
* L_n = W_n·Σ_j b_j·(ρ_black + (ρ_white − ρ_black)·Σ_corners w_jk·s_k(λ_n)),
|
|
19
|
+
*
|
|
20
|
+
* the reference's arithmetic regrouped. Each pixel's lattice cell and fractions come from the host (float64, as the reference
|
|
21
|
+
* decodes the pixel), per band for the rows its lattice windows reach: 16 bytes per pixel (the cell, and per channel the smaller
|
|
22
|
+
* 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
|
|
23
|
+
* are non-negative: L_n carries `IMAGE_PLANE_LATTICE_U`·E relative (s 1, three channel weights 6, the nested trilinear form 6,
|
|
24
|
+
* 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
|
|
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
|
+
* change the value by more than E of it (the Speed module's rule, per node).
|
|
27
|
+
*
|
|
28
|
+
* **Flagged points.** With site kernels the reference evaluates the lattice formula itself, so the host replaces a flagged
|
|
29
|
+
* point's values with the reference's own `radianceAt` there and clears its flag (as #492 resolves uncertain chart materials).
|
|
30
|
+
* Without kernels the reference reads the environment's own query points, so a site that reads a flagged point (with a nonzero
|
|
31
|
+
* weight) is flagged and its display pixels are recomputed on the host.
|
|
32
|
+
*
|
|
33
|
+
* **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
|
|
34
|
+
* 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,
|
|
35
|
+
* and the row, group and total additions, so the electrons carry (IMAGE_PLANE_LATTICE_U + N + cw + ch + G + 4)·E relative.
|
|
36
|
+
* Then the Quality site tail (readout moments, white clip, running bounds), unchanged.
|
|
37
|
+
*/
|
|
38
|
+
import { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from "./image-kernel-execution.js";
|
|
39
|
+
import { BUFFER_USAGE, linearDispatch, runComputePass } from "./webgpu-runtime.js";
|
|
40
|
+
import { dwSplit } from "./chart-composite-gpu.js";
|
|
41
|
+
import { displayViewModuleCommonWgsl } from "./display-view-composite-material-module.js";
|
|
42
|
+
import { 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";
|
|
43
|
+
import { latticeCoordinate } from "./display-view-quality-gpu.js";
|
|
44
|
+
import { queryGeometry } from "./display-view-speed-gpu.js";
|
|
45
|
+
import { displayViewSignalTables } from "../facade/expected-signal.js";
|
|
46
|
+
const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
|
|
47
|
+
/** Relative roundings of a lattice point's radiance (see the module comment). */
|
|
48
|
+
export const IMAGE_PLANE_LATTICE_U = 32;
|
|
49
|
+
/** Lattice nodes of the print (5 × 5 × 5). */
|
|
50
|
+
const NODES = 125;
|
|
51
|
+
/** Words per pixel record. */
|
|
52
|
+
const RECORD_WORDS = 4;
|
|
53
|
+
/** 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,
|
|
55
|
+
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
|
+
const FLOAT32_SAFE_MAGNITUDE = 2 ** 120, FLOAT32_MINIMUM_NORMAL = 2 ** -126;
|
|
57
|
+
/** The display-view helpers of the composite kernel's common part, with the barrier word read from the photo uniform. */
|
|
58
|
+
const photoCommon = (path) => displayViewModuleCommonWgsl(path).replace(/\blattice\.zero\b/gu, "photo.zero");
|
|
59
|
+
/** Pass 1's WGSL (see the module comment). */
|
|
60
|
+
export function imagePlaneQualityLatticeWgsl(path) {
|
|
61
|
+
return `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}
|
|
62
|
+
@group(0) @binding(1) var<storage, read> groups: array<u32>;
|
|
63
|
+
@group(0) @binding(2) var<storage, read_write> lattice_values: array<f32>;
|
|
64
|
+
@group(0) @binding(3) var<storage, read_write> lattice_flags: array<u32>;
|
|
65
|
+
struct PhotoParams {
|
|
66
|
+
width: u32, height: u32, nearest: u32, nodes: u32, row0: u32, zero: u32, pad0: u32, pad1: u32,
|
|
67
|
+
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
|
+
hh_hi: f32, hh_lo: f32, aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32, black: f32, span: f32,
|
|
69
|
+
};
|
|
70
|
+
@group(1) @binding(0) var<storage, read> records: array<u32>;
|
|
71
|
+
@group(1) @binding(1) var<uniform> photo: PhotoParams;
|
|
72
|
+
@group(1) @binding(2) var<storage, read> node_table: array<f32>;
|
|
73
|
+
${photoCommon(path)}
|
|
74
|
+
fn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }
|
|
75
|
+
struct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }
|
|
76
|
+
fn bilinear_axis(s: D, n: u32) -> Axis {
|
|
77
|
+
var f0 = floor(s.h);
|
|
78
|
+
if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }
|
|
79
|
+
let frac = d_sub(s, D(f0, 0.0));
|
|
80
|
+
let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);
|
|
81
|
+
var w0 = 0.0; var w1 = 0.0;
|
|
82
|
+
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); }
|
|
83
|
+
let last = i32(n) - 1;
|
|
84
|
+
return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);
|
|
85
|
+
}
|
|
86
|
+
// A channel's two corner weights from its record: the stored smaller one and 1 minus it.
|
|
87
|
+
fn channel_weights(word: u32, bit: u32, small: f32) -> vec2<f32> {
|
|
88
|
+
if (((word >> bit) & 1u) == 0u) { return vec2<f32>(ob(1.0 - small), small); }
|
|
89
|
+
return vec2<f32>(small, ob(1.0 - small));
|
|
90
|
+
}
|
|
91
|
+
struct Pixel { base: u32, r: vec2<f32>, g: vec2<f32>, b: vec2<f32> }
|
|
92
|
+
fn pixel_at(i: u32, j: u32) -> Pixel {
|
|
93
|
+
let o = ((j - photo.row0) * photo.width + i) * ${RECORD_WORDS}u;
|
|
94
|
+
let w = records[o];
|
|
95
|
+
return Pixel((w & 3u) * 25u + ((w >> 2u) & 3u) * 5u + ((w >> 4u) & 3u), channel_weights(w, 6u, bitcast<f32>(records[o + 1u])),
|
|
96
|
+
channel_weights(w, 7u, bitcast<f32>(records[o + 2u])), channel_weights(w, 8u, bitcast<f32>(records[o + 3u])));
|
|
97
|
+
}
|
|
98
|
+
fn node(k: u32, n: u32) -> f32 { return node_table[k * photo.nodes + n]; }
|
|
99
|
+
// One pixel's reflectance at node n: the reference's nested trilinear form, then the paper.
|
|
100
|
+
fn pixel_value(px: Pixel, n: u32) -> f32 {
|
|
101
|
+
let b = px.base;
|
|
102
|
+
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))) +
|
|
103
|
+
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)));
|
|
104
|
+
return photo.black + photo.span * lat;
|
|
105
|
+
}
|
|
106
|
+
@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
|
|
107
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {
|
|
108
|
+
let i = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;
|
|
109
|
+
if (i >= params.lattice_total) { return; }
|
|
110
|
+
var g = 0u;
|
|
111
|
+
for (var k = 1u; k < params.groups; k = k + 1u) { if (i >= groups[k * GROUP + 4u]) { g = k; } }
|
|
112
|
+
let base = g * GROUP;
|
|
113
|
+
let lw = groups[base + 2u];
|
|
114
|
+
let local = i - groups[base + 4u];
|
|
115
|
+
let q = bitcast<i32>(groups[base + 1u]) + i32(local / lw);
|
|
116
|
+
let p = bitcast<i32>(groups[base]) + i32(local % lw);
|
|
117
|
+
material_uncertain = false;
|
|
118
|
+
let N = photo.nodes;
|
|
119
|
+
let out = i * N;
|
|
120
|
+
let white = ${NODES}u * N;
|
|
121
|
+
let k = D(photo.k_hi, photo.k_lo);
|
|
122
|
+
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
|
+
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
|
+
let aspect = D(photo.aspect_hi, photo.aspect_lo);
|
|
125
|
+
if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) {
|
|
126
|
+
for (var n = 0u; n < N; n = n + 1u) { lattice_values[out + n] = node_table[white + N + n]; }
|
|
127
|
+
lattice_flags[i] = select(0u, 1u, material_uncertain);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.hw_hi, photo.hw_lo));
|
|
131
|
+
let y = d_mul(d_sub(aspect, v), D(photo.hh_hi, photo.hh_lo));
|
|
132
|
+
if (photo.nearest != 0u) {
|
|
133
|
+
let last_x = f32(photo.width - 1u);
|
|
134
|
+
let last_y = f32(photo.height - 1u);
|
|
135
|
+
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] = node_table[white + n] * pixel_value(px, n); }
|
|
137
|
+
} else {
|
|
138
|
+
let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);
|
|
139
|
+
let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);
|
|
140
|
+
let p00 = pixel_at(ax.i0, ay.i0);
|
|
141
|
+
let p10 = pixel_at(ax.i1, ay.i0);
|
|
142
|
+
let p01 = pixel_at(ax.i0, ay.i1);
|
|
143
|
+
let p11 = pixel_at(ax.i1, ay.i1);
|
|
144
|
+
let slack = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0;
|
|
145
|
+
for (var n = 0u; n < N; n = n + 1u) {
|
|
146
|
+
let s00 = pixel_value(p00, n);
|
|
147
|
+
let s10 = pixel_value(p10, n);
|
|
148
|
+
let s01 = pixel_value(p01, n);
|
|
149
|
+
let s11 = pixel_value(p11, n);
|
|
150
|
+
let value = ay.w0 * (ax.w0 * s00 + ax.w1 * s10) + ay.w1 * (ax.w0 * s01 + ax.w1 * s11);
|
|
151
|
+
if (!(slack * max(max(s00, s10), max(s01, s11)) <= E * value)) { material_uncertain = true; }
|
|
152
|
+
lattice_values[out + n] = node_table[white + n] * value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
lattice_flags[i] = select(0u, 1u, material_uncertain);
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */
|
|
160
|
+
export const IMAGE_PLANE_QUALITY_SITES_WGSL = /* wgsl */ `${DISPLAY_VIEW_QUALITY_COMMON_WGSL}
|
|
161
|
+
@group(0) @binding(1) var<storage, read> groups: array<u32>;
|
|
162
|
+
@group(0) @binding(3) var<storage, read> table: array<f32>;
|
|
163
|
+
@group(0) @binding(4) var<storage, read> lattice: array<f32>;
|
|
164
|
+
@group(0) @binding(5) var<storage, read_write> sites: array<f32>;
|
|
165
|
+
@group(0) @binding(6) var<storage, read> lattice_flags: array<u32>;
|
|
166
|
+
${DISPLAY_VIEW_QUALITY_SITE_TAIL_WGSL}
|
|
167
|
+
@compute @workgroup_size(${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE})
|
|
168
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) wg: vec3<u32>) {
|
|
169
|
+
let local = gid.x + gid.y * wg.x * ${DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE}u;
|
|
170
|
+
if (local >= params.count) { return; }
|
|
171
|
+
let i = params.site_first + local;
|
|
172
|
+
let ii = i % params.site_w;
|
|
173
|
+
let jj = i / params.site_w;
|
|
174
|
+
let c = channel_of(params.site_x0 + i32(ii), params.site_y0 + i32(jj));
|
|
175
|
+
let T = params.taps;
|
|
176
|
+
let N = bitcast<u32>(params.pad_h);
|
|
177
|
+
var sum = 0.0;
|
|
178
|
+
var flagged = false;
|
|
179
|
+
for (var k = 0u; k < params.groups; k = k + 1u) {
|
|
180
|
+
let g = k * GROUP;
|
|
181
|
+
let lw = groups[g + 2u];
|
|
182
|
+
let cw = lw - params.site_w + 1u;
|
|
183
|
+
let ch = groups[g + 3u] - params.site_h + 1u;
|
|
184
|
+
let lattice0 = groups[g + 4u] + jj * lw + ii;
|
|
185
|
+
let tap0 = groups[g + 5u];
|
|
186
|
+
var group_sum = 0.0;
|
|
187
|
+
for (var dy = 0u; dy < ch; dy = dy + 1u) {
|
|
188
|
+
let row = lattice0 + dy * lw;
|
|
189
|
+
let row_tap = tap0 + dy * cw;
|
|
190
|
+
var row_sum = 0.0;
|
|
191
|
+
for (var dx = 0u; dx < cw; dx = dx + 1u) {
|
|
192
|
+
let point = row + dx;
|
|
193
|
+
let t = row_tap + dx;
|
|
194
|
+
var cell = 0.0;
|
|
195
|
+
var weight = 0.0;
|
|
196
|
+
for (var n = 0u; n < N; n = n + 1u) {
|
|
197
|
+
let w = table[(n * 3u + c) * T + t];
|
|
198
|
+
weight = weight + w;
|
|
199
|
+
cell = cell + lattice[point * N + n] * w;
|
|
200
|
+
}
|
|
201
|
+
if (lattice_flags[point] != 0u && weight > 0.0) { flagged = true; }
|
|
202
|
+
row_sum = row_sum + cell;
|
|
203
|
+
}
|
|
204
|
+
group_sum = group_sum + row_sum;
|
|
205
|
+
}
|
|
206
|
+
sum = sum + group_sum;
|
|
207
|
+
}
|
|
208
|
+
let e = sum + dark_of(c);
|
|
209
|
+
write_site(i, c, e, params.site_gamma * e + params.site_tiny, select(0u, 1u, flagged));
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
/**
|
|
213
|
+
* The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the
|
|
214
|
+
* 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
|
+
* the reference's float64 values rounded once.
|
|
216
|
+
*/
|
|
217
|
+
export function imagePlaneQualityGather(signalInput) {
|
|
218
|
+
const tables = displayViewSignalTables(signalInput), W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0;
|
|
219
|
+
const N = signalInput.wavelengthsNm.length, A = signalInput.electronsPerRadiance, groups = [];
|
|
220
|
+
const rects = tables.groups.map((group) => {
|
|
221
|
+
const cw = signalInput.kernels ? group.cw : group.maxIx - group.minIx + 1, ch = signalInput.kernels ? group.ch : group.maxIy - group.minIy + 1;
|
|
222
|
+
return { group, cw, ch, dpMin: group.minIx - R, dqMin: group.minIy - R };
|
|
223
|
+
});
|
|
224
|
+
let T = 0;
|
|
225
|
+
for (const r of rects) {
|
|
226
|
+
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 });
|
|
227
|
+
T += r.cw * r.ch;
|
|
228
|
+
}
|
|
229
|
+
const table = new Float32Array(N * 3 * T), tiny = new Float64Array(T);
|
|
230
|
+
let largestRadiance = 0;
|
|
231
|
+
rects.forEach((r, g) => {
|
|
232
|
+
const first = groups[g].firstTap;
|
|
233
|
+
for (let n = 0; n < N; n++)
|
|
234
|
+
for (let c = 0; c < 3; c++) {
|
|
235
|
+
const values = new Float64Array(r.cw * r.ch);
|
|
236
|
+
if (signalInput.kernels)
|
|
237
|
+
values.set(r.group.perNode[n][c]);
|
|
238
|
+
else
|
|
239
|
+
for (const member of r.group.members) {
|
|
240
|
+
const at = (member.iy - r.group.minIy) * r.cw + member.ix - r.group.minIx;
|
|
241
|
+
values[at] = values[at] + member.weight * A[c][n];
|
|
242
|
+
}
|
|
243
|
+
for (let t = 0; t < values.length; t++) {
|
|
244
|
+
const value = values[t];
|
|
245
|
+
if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE))
|
|
246
|
+
throw new ImageKernelExecutionError("unsupported-float32-domain", "A combined kernel value is outside the float32 domain.");
|
|
247
|
+
table[(n * 3 + c) * T + first + t] = value;
|
|
248
|
+
if (value > 0 && value < FLOAT32_MINIMUM_NORMAL)
|
|
249
|
+
tiny[first + t] = Math.max(tiny[first + t], value);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
const image = signalInput.scene.image;
|
|
254
|
+
for (const w of image.whiteRadiance)
|
|
255
|
+
largestRadiance = Math.max(largestRadiance, w);
|
|
256
|
+
let tinyMass = 0;
|
|
257
|
+
for (let t = 0; t < T; t++)
|
|
258
|
+
tinyMass += tiny[t] * largestRadiance * N;
|
|
259
|
+
return { groups, taps: new Uint32Array(0), tapCount: T, nodes: N, table, spread: new Float32Array(3 * Math.max(1, T)), materials: 0, tinyMass };
|
|
260
|
+
}
|
|
261
|
+
/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */
|
|
262
|
+
export function imagePlaneQualityPasses(runtime, gpu, path) {
|
|
263
|
+
const signalInput = gpu.signalInput, image = signalInput.scene.image;
|
|
264
|
+
if (!image || !gpu.placement)
|
|
265
|
+
throw new ImageKernelExecutionError("unsupported-on-backend", "The setup carries no image-plane print and placement; use cpu-reference.");
|
|
266
|
+
const scene = image.scene, N = signalInput.wavelengthsNm.length, gather = imagePlaneQualityGather(signalInput);
|
|
267
|
+
let cw = 0, ch = 0;
|
|
268
|
+
for (const g of gather.groups) {
|
|
269
|
+
cw = Math.max(cw, g.cw);
|
|
270
|
+
ch = Math.max(ch, g.ch);
|
|
271
|
+
}
|
|
272
|
+
// The lattice scale: the lattice formula's g with site kernels, else the environment's query lattice (checked within 2⁻⁴⁰ by queryGeometry).
|
|
273
|
+
const g = 2 / signalInput.outputWidth, geometry = signalInput.kernels ? { scaleU: g, scaleV: -g } : queryGeometry(signalInput);
|
|
274
|
+
const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect), k = gpu.placement.chartUnitsPerFrameUnit;
|
|
275
|
+
// The node table: s_k(λ_n) per node, then W_n, then the surround's radiance per node.
|
|
276
|
+
const nodeTable = new Float32Array((NODES + 2) * N);
|
|
277
|
+
signalInput.wavelengthsNm.forEach((nm, n) => {
|
|
278
|
+
const reflectances = scene.nodeReflectances(nm);
|
|
279
|
+
for (let node = 0; node < NODES; node++)
|
|
280
|
+
nodeTable[node * N + n] = reflectances[node];
|
|
281
|
+
nodeTable[NODES * N + n] = image.whiteRadiance[n];
|
|
282
|
+
nodeTable[(NODES + 1) * N + n] = image.surroundReflectance * image.whiteRadiance[n];
|
|
283
|
+
});
|
|
284
|
+
const P = IMAGE_PLANE_QUALITY_PARAMS, params = new ArrayBuffer(4 * P.words), words = new Uint32Array(params), floats = new Float32Array(params);
|
|
285
|
+
words[P.width] = scene.width;
|
|
286
|
+
words[P.height] = scene.height;
|
|
287
|
+
words[P.nearest] = scene.interpolation === "nearest" ? 1 : 0;
|
|
288
|
+
words[P.nodes] = N;
|
|
289
|
+
words[P.zero] = 0;
|
|
290
|
+
[floats[P.suHi], floats[P.suLo]] = dwSplit(geometry.scaleU);
|
|
291
|
+
[floats[P.svHi], floats[P.svLo]] = dwSplit(geometry.scaleV);
|
|
292
|
+
[floats[P.kHi], floats[P.kLo]] = dwSplit(k);
|
|
293
|
+
[floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW);
|
|
294
|
+
[floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH);
|
|
295
|
+
[floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);
|
|
296
|
+
floats[P.slackX] = 1e-14 * k * halfW;
|
|
297
|
+
floats[P.slackY] = 1e-14 * k * halfH;
|
|
298
|
+
floats[P.black] = scene.paperBlackReflectance;
|
|
299
|
+
floats[P.span] = scene.paperWhiteReflectance - scene.paperBlackReflectance;
|
|
300
|
+
const label = path === "dekker" ? "image-plane" : "image-plane-fma";
|
|
301
|
+
const fractions = new Float64Array(3);
|
|
302
|
+
const passes = {
|
|
303
|
+
gather, siteGamma: (IMAGE_PLANE_LATTICE_U + N + cw + ch + gather.groups.length + 4) * E, pointFloats: N,
|
|
304
|
+
lattice: ["photivra-display-view-quality-photo-lattice:" + label, imagePlaneQualityLatticeWgsl(path)],
|
|
305
|
+
sites: ["photivra-display-view-quality-photo-sites", IMAGE_PLANE_QUALITY_SITES_WGSL],
|
|
306
|
+
async runLattice(band, pipeline, latticeParams, groupBuffer, groupWords, latticeTotal, signal) {
|
|
307
|
+
let values = band.buffer("photivra-display-view-photo-lattice", latticeTotal * 4 * N, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
308
|
+
let flags = band.buffer("photivra-display-view-photo-lattice-flags", latticeTotal * 4, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
309
|
+
const group0 = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: latticeParams } },
|
|
310
|
+
{ binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: values } }, { binding: 3, resource: { buffer: flags } }] });
|
|
311
|
+
const [valueData, flagData] = await runComputePass(runtime, band, pipeline, [group0, bandGroup(band, pipeline.getBindGroupLayout(1), groupWords)], linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), [{ buffer: values, bytes: latticeTotal * 4 * N }, { buffer: flags, bytes: latticeTotal * 4 }], signal);
|
|
312
|
+
const valueArray = new Float32Array(valueData, 0, latticeTotal * N), flagArray = new Uint32Array(flagData, 0, latticeTotal), resolved = resolve(valueArray, flagArray, groupWords);
|
|
313
|
+
if (resolved > 0) {
|
|
314
|
+
values = band.upload("photivra-display-view-photo-lattice-resolved", valueArray, BUFFER_USAGE.STORAGE);
|
|
315
|
+
flags = band.upload("photivra-display-view-photo-flags-resolved", flagArray, BUFFER_USAGE.STORAGE);
|
|
316
|
+
}
|
|
317
|
+
return { values, flags, resolved };
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
function bandGroup(scope, layout, groupWords) {
|
|
321
|
+
// 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).
|
|
322
|
+
const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupFloats = new Float32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length);
|
|
323
|
+
let low = Infinity, high = -Infinity;
|
|
324
|
+
for (let gi = 0; gi < gather.groups.length; gi++) {
|
|
325
|
+
const base = gi * DISPLAY_VIEW_QUALITY_GROUP_WORDS, cv = groupFloats[base + 7];
|
|
326
|
+
for (const q of [ints[base + 1], ints[base + 1] + groupWords[base + 3] - 1]) {
|
|
327
|
+
const Y = (scene.aspect - (q + cv) * geometry.scaleV * k) * halfH;
|
|
328
|
+
low = Math.min(low, Y);
|
|
329
|
+
high = Math.max(high, Y);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
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));
|
|
333
|
+
const records = new Uint32Array((row1 - row0 + 1) * scene.width * RECORD_WORDS), recordFloats = new Float32Array(records.buffer);
|
|
334
|
+
for (let j = row0, o = 0; j <= row1; j++)
|
|
335
|
+
for (let i = 0; i < scene.width; i++, o += RECORD_WORDS) {
|
|
336
|
+
const cell = scene.pixelLattice(j * scene.width + i, fractions);
|
|
337
|
+
let word = Math.floor(cell / 25) | ((Math.floor(cell / 5) % 5) << 2) | ((cell % 5) << 4);
|
|
338
|
+
for (let c = 0; c < 3; c++) {
|
|
339
|
+
const f = fractions[c], high1 = f > 0.5;
|
|
340
|
+
if (high1)
|
|
341
|
+
word |= 1 << (6 + c);
|
|
342
|
+
recordFloats[o + 1 + c] = high1 ? 1 - f : f;
|
|
343
|
+
}
|
|
344
|
+
records[o] = word;
|
|
345
|
+
}
|
|
346
|
+
const bandParams = params.slice(0);
|
|
347
|
+
new Uint32Array(bandParams)[P.row0] = row0;
|
|
348
|
+
if (records.byteLength > runtime.maximumStorageBindingBytes)
|
|
349
|
+
throw new ImageKernelExecutionError("insufficient-limits", "A band's pixel records exceed the device's storage binding limit.");
|
|
350
|
+
return runtime.device.createBindGroup({ layout, entries: [
|
|
351
|
+
{ binding: 0, resource: { buffer: scope.upload("photivra-display-view-photo-records", records, BUFFER_USAGE.STORAGE) } },
|
|
352
|
+
{ 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) } }
|
|
354
|
+
] });
|
|
355
|
+
}
|
|
356
|
+
function resolve(values, flags, groupWords) {
|
|
357
|
+
if (!signalInput.kernels)
|
|
358
|
+
return 0;
|
|
359
|
+
// The reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV) and its radiance per node.
|
|
360
|
+
const ints = new Int32Array(groupWords.buffer, groupWords.byteOffset, groupWords.length), groupsOf = displayViewSignalTables(signalInput).groups;
|
|
361
|
+
const W = signalInput.nativeWidth, H = signalInput.nativeHeight, scale = 2 / signalInput.outputWidth;
|
|
362
|
+
let resolved = 0;
|
|
363
|
+
groupsOf.forEach((group, gi) => {
|
|
364
|
+
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];
|
|
365
|
+
for (let local = 0; local < lw * lh; local++) {
|
|
366
|
+
if (flags[first + local] === 0)
|
|
367
|
+
continue;
|
|
368
|
+
const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);
|
|
369
|
+
const u = latticeCoordinate(p, group.fx, W, scale), v = -latticeCoordinate(q, group.fy, H, scale);
|
|
370
|
+
for (let n = 0; n < N; n++)
|
|
371
|
+
values[(first + local) * N + n] = signalInput.scene.kind === "radiance" ? signalInput.scene.radianceAt(u, v, signalInput.wavelengthsNm[n]) : 0;
|
|
372
|
+
flags[first + local] = 0;
|
|
373
|
+
resolved++;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
return resolved;
|
|
377
|
+
}
|
|
378
|
+
return passes;
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=display-view-image-quality.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -31,7 +31,7 @@ import { type DisplayViewMaterialModules } from "./display-view-composite-materi
|
|
|
31
31
|
import { type DisplayViewGpuConformanceCase, type DisplayViewGpuRenderer, type DisplayViewGpuRendererFactory, type DisplayViewGpuRequest } from "../facade/display-view-gpu.js";
|
|
32
32
|
import { type DisplayViewGpuMaterialModule } from "./display-view-speed-gpu.js";
|
|
33
33
|
import { type ImageKernelExecution, type ImageKernelRelativeErrorBound } from "./image-kernel-execution.js";
|
|
34
|
-
import { type WebGpuRuntime } from "./webgpu-runtime.js";
|
|
34
|
+
import { GpuResourceScope, type WebGpuRuntime } from "./webgpu-runtime.js";
|
|
35
35
|
type PipelineSource = (label: string, code: string) => Promise<GPUComputePipeline>;
|
|
36
36
|
type YieldControl = ((signal: AbortSignal) => Promise<void>) | undefined;
|
|
37
37
|
/**
|
|
@@ -66,6 +66,25 @@ export interface DisplayViewQualityGpuInput {
|
|
|
66
66
|
blockedSum?: boolean;
|
|
67
67
|
/** Largest taps (sites × taps per site) per site-pass dispatch; default `DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH`. */
|
|
68
68
|
maximumSiteTapsPerDispatch?: number;
|
|
69
|
+
/** #581: an image-plane print's own passes 1 and 2 (`material` is then unused). */
|
|
70
|
+
image?: DisplayViewQualityImagePasses;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* #581: an image-plane print's passes 1 and 2 (src/gpu/display-view-image-quality.ts): pass 1 writes `pointFloats` radiance values
|
|
74
|
+
* per lattice point and a flag word; pass 2 is the blocked sum over them (the flags at binding 6).
|
|
75
|
+
*/
|
|
76
|
+
export interface DisplayViewQualityImagePasses {
|
|
77
|
+
gather: SignalGather;
|
|
78
|
+
siteGamma: number;
|
|
79
|
+
pointFloats: number;
|
|
80
|
+
lattice: [label: string, wgsl: string];
|
|
81
|
+
sites: [label: string, wgsl: string];
|
|
82
|
+
/** Runs pass 1 over a band's lattice windows, and with site kernels resolves its flagged points with the reference (their count). */
|
|
83
|
+
runLattice(band: GpuResourceScope, pipeline: GPUComputePipeline, params: GPUBuffer, groups: GPUBuffer, groupWords: Uint32Array, latticeTotal: number, signal: AbortSignal): Promise<{
|
|
84
|
+
values: GPUBuffer;
|
|
85
|
+
flags: GPUBuffer;
|
|
86
|
+
resolved: number;
|
|
87
|
+
}>;
|
|
69
88
|
}
|
|
70
89
|
/** Wall-clock milliseconds per stage of one render (upload, dispatch and readback of each pass; host work apart). */
|
|
71
90
|
export interface DisplayViewQualityStageTimings {
|
|
@@ -148,6 +167,53 @@ export declare function footprintShape(setup: DisplayViewSetup): {
|
|
|
148
167
|
* 2·reach + 2 sites on each axis (so a reflected border site lies in the band's window).
|
|
149
168
|
*/
|
|
150
169
|
export declare function displayViewQualityGpuUnsupportedReason(setup: DisplayViewSetup, signalInput: DisplayViewExpectedSignalInput): string | null;
|
|
170
|
+
/** The lattice groups, taps and the spectral table of the expected signal (see the module comment). */
|
|
171
|
+
export interface SignalGather {
|
|
172
|
+
/**
|
|
173
|
+
* Per group: dpMin, dqMin, dpSpan, dqSpan (lattice extent beyond the site window), cu, cv; its first tap, and (with site
|
|
174
|
+
* kernels) its dense tap rectangle cw × ch (dp = dpMin + dx, dq = dqMin + dy at tap first + dy·cw + dx).
|
|
175
|
+
*/
|
|
176
|
+
groups: {
|
|
177
|
+
dpMin: number;
|
|
178
|
+
dqMin: number;
|
|
179
|
+
dpSpan: number;
|
|
180
|
+
dqSpan: number;
|
|
181
|
+
cu: number;
|
|
182
|
+
cv: number;
|
|
183
|
+
firstTap: number;
|
|
184
|
+
cw: number;
|
|
185
|
+
ch: number;
|
|
186
|
+
}[];
|
|
187
|
+
/** Per tap: group, dp − dpMin, dq − dqMin. */
|
|
188
|
+
taps: Uint32Array<ArrayBuffer>;
|
|
189
|
+
tapCount: number;
|
|
190
|
+
/** Spectral nodes N. */
|
|
191
|
+
nodes: number;
|
|
192
|
+
/**
|
|
193
|
+
* 2.21.0 (signals integrated from the material spectrum per site): per channel c, tap t and node n the channel's response and
|
|
194
|
+
* kernel C_c(t, n) at (c·T + t)·N + n (`nodeKernels`; without site kernels the member's weight times A_c(λ_n)), then per material
|
|
195
|
+
* m its spectral radiance L_m(λ_n) at 3·T·N + m·N + n, each float64 rounded once. No per-material channel value is formed: the
|
|
196
|
+
* site pass integrates Σ_t Σ_n C_c(t, n)·L_{m_t}(λ_n).
|
|
197
|
+
*/
|
|
198
|
+
table: Float32Array<ArrayBuffer>;
|
|
199
|
+
/**
|
|
200
|
+
* Per channel c and tap t (at c·T + t): a bound on the difference between any two materials' signals at that tap,
|
|
201
|
+
* Σ_n C_c(t, n)·(max_m L_m(λ_n) − min_m L_m(λ_n)), from the spectra's envelope (no per-material value), rounded up.
|
|
202
|
+
*/
|
|
203
|
+
spread: Float32Array<ArrayBuffer>;
|
|
204
|
+
materials: number;
|
|
205
|
+
/**
|
|
206
|
+
* An absolute bound on what a device that flushes results below the float32 normal range (WGSL allows it) can drop from a site:
|
|
207
|
+
* per tap the largest product C·max L below that range over the channels, plus one smallest normal per partial addition and
|
|
208
|
+
* per compensation step ((T + 2)·N of them), float64.
|
|
209
|
+
*/
|
|
210
|
+
tinyMass: number;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* One axis of the reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV, written as it writes them):
|
|
214
|
+
* (i + 0.5 − size/2)·g with no shift, (i + 0.5 + f − size/2)·g otherwise; v is its negative.
|
|
215
|
+
*/
|
|
216
|
+
export declare const latticeCoordinate: (i: number, f: number, size: number, g: number) => number;
|
|
151
217
|
/** Renders one region of a Quality display view on the GPU (see the module comment). */
|
|
152
218
|
export declare function renderDisplayViewQualityOnGpu(runtime: WebGpuRuntime, bandBytes: number, input: DisplayViewQualityGpuInput, abort: AbortController, yieldControl: YieldControl, pipelines: PipelineSource): Promise<DisplayViewQualityGpuResult>;
|
|
153
219
|
/**
|