@photivra/engine 2.10.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +139 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +21 -3
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/dist/core/portable-math.d.ts +6 -0
  7. package/dist/core/portable-math.js +39 -0
  8. package/dist/core/portable-math.js.map +1 -1
  9. package/dist/core/version.d.ts +1 -1
  10. package/dist/core/version.js +1 -1
  11. package/dist/core/version.js.map +1 -1
  12. package/dist/facade/chart-material-layers.d.ts +115 -0
  13. package/dist/facade/chart-material-layers.js +224 -0
  14. package/dist/facade/chart-material-layers.js.map +1 -0
  15. package/dist/facade/chart-radiance-batch.d.ts +8 -2
  16. package/dist/facade/chart-radiance-batch.js +11 -4
  17. package/dist/facade/chart-radiance-batch.js.map +1 -1
  18. package/dist/facade/chart-scenes.d.ts +134 -0
  19. package/dist/facade/chart-scenes.js +32 -22
  20. package/dist/facade/chart-scenes.js.map +1 -1
  21. package/dist/facade/diffraction-provider.d.ts +8 -2
  22. package/dist/facade/diffraction-provider.js +71 -2
  23. package/dist/facade/diffraction-provider.js.map +1 -1
  24. package/dist/facade/display-view-gpu-session.d.ts +18 -0
  25. package/dist/facade/display-view-gpu-session.js +280 -0
  26. package/dist/facade/display-view-gpu-session.js.map +1 -0
  27. package/dist/facade/display-view-gpu.d.ts +146 -0
  28. package/dist/facade/display-view-gpu.js +59 -0
  29. package/dist/facade/display-view-gpu.js.map +1 -0
  30. package/dist/facade/display-view.d.ts +48 -1
  31. package/dist/facade/display-view.js +7 -7
  32. package/dist/facade/display-view.js.map +1 -1
  33. package/dist/facade/expected-signal-setup.d.ts +5 -0
  34. package/dist/facade/expected-signal-setup.js +16 -0
  35. package/dist/facade/expected-signal-setup.js.map +1 -1
  36. package/dist/facade/expected-signal.d.ts +48 -3
  37. package/dist/facade/expected-signal.js +8 -0
  38. package/dist/facade/expected-signal.js.map +1 -1
  39. package/dist/facade/scene-radiance.d.ts +4 -2
  40. package/dist/facade/scene-radiance.js +7 -4
  41. package/dist/facade/scene-radiance.js.map +1 -1
  42. package/dist/facade/simulate-capture.d.ts +32 -5
  43. package/dist/facade/simulate-capture.js +148 -24
  44. package/dist/facade/simulate-capture.js.map +1 -1
  45. package/dist/facade/simulate-display-view.d.ts +31 -3
  46. package/dist/facade/simulate-display-view.js +72 -12
  47. package/dist/facade/simulate-display-view.js.map +1 -1
  48. package/dist/gpu/chart-composite-gpu.d.ts +119 -0
  49. package/dist/gpu/chart-composite-gpu.js +572 -0
  50. package/dist/gpu/chart-composite-gpu.js.map +1 -0
  51. package/dist/gpu/chart-radiance-gpu.d.ts +6 -6
  52. package/dist/gpu/chart-radiance-gpu.js +9 -11
  53. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  54. package/dist/gpu/display-view-chart-material-module.d.ts +10 -0
  55. package/dist/gpu/display-view-chart-material-module.js +58 -0
  56. package/dist/gpu/display-view-chart-material-module.js.map +1 -0
  57. package/dist/gpu/display-view-composite-material-module.d.ts +68 -0
  58. package/dist/gpu/display-view-composite-material-module.js +248 -0
  59. package/dist/gpu/display-view-composite-material-module.js.map +1 -0
  60. package/dist/gpu/display-view-quality-gpu.d.ts +185 -0
  61. package/dist/gpu/display-view-quality-gpu.js +1000 -0
  62. package/dist/gpu/display-view-quality-gpu.js.map +1 -0
  63. package/dist/gpu/display-view-speed-gpu.d.ts +156 -0
  64. package/dist/gpu/display-view-speed-gpu.js +524 -0
  65. package/dist/gpu/display-view-speed-gpu.js.map +1 -0
  66. package/dist/gpu/image-kernel-accelerator.d.ts +23 -4
  67. package/dist/gpu/image-kernel-accelerator.js +58 -10
  68. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  69. package/dist/gpu/image-kernel-execution.d.ts +55 -3
  70. package/dist/gpu/image-kernel-execution.js.map +1 -1
  71. package/dist/gpu/sensor-stage-gpu.d.ts +11 -0
  72. package/dist/gpu/sensor-stage-gpu.js +42 -13
  73. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  74. package/dist/gpu/webgpu-runtime.d.ts +4 -2
  75. package/dist/gpu/webgpu-runtime.js +13 -4
  76. package/dist/gpu/webgpu-runtime.js.map +1 -1
  77. package/dist/gpu/webgpu-switch.d.ts +4 -0
  78. package/dist/gpu/webgpu-switch.js +29 -0
  79. package/dist/gpu/webgpu-switch.js.map +1 -0
  80. package/dist/gpu/wgsl-chart-composite-kernel.d.ts +52 -0
  81. package/dist/gpu/wgsl-chart-composite-kernel.js +515 -0
  82. package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -0
  83. package/dist/gpu/wgsl-chart-kernel.d.ts +11 -9
  84. package/dist/gpu/wgsl-chart-kernel.js +33 -27
  85. package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
  86. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +95 -0
  87. package/dist/gpu/wgsl-display-view-quality-kernel.js +769 -0
  88. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -0
  89. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +75 -0
  90. package/dist/gpu/wgsl-display-view-speed-kernel.js +386 -0
  91. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -0
  92. package/dist/gpu/wgsl-sensor-kernel.d.ts +28 -9
  93. package/dist/gpu/wgsl-sensor-kernel.js +94 -50
  94. package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
  95. package/dist/index.d.ts +4 -1
  96. package/dist/index.js +2 -0
  97. package/dist/index.js.map +1 -1
  98. package/dist/optics/defocus-disc-kernel.d.ts +61 -0
  99. package/dist/optics/defocus-disc-kernel.js +191 -0
  100. package/dist/optics/defocus-disc-kernel.js.map +1 -0
  101. package/docs/API_REFERENCE.md +45 -1
  102. package/docs/QUICK_START.md +102 -3
  103. package/docs/USAGE.md +52 -2
  104. package/docs/api/core-version.md +1 -1
  105. package/docs/api/exports.json +117 -2
  106. package/docs/api/facade-chart-material-layers.md +137 -0
  107. package/docs/api/facade-chart-radiance-batch.md +7 -2
  108. package/docs/api/facade-display-view-gpu.md +94 -0
  109. package/docs/api/facade-scene-radiance.md +3 -2
  110. package/docs/api/facade-simulate-capture.md +23 -6
  111. package/docs/api/facade-simulate-display-view.md +17 -2
  112. package/docs/api/gpu-image-kernel-accelerator.md +10 -3
  113. package/docs/api/gpu-image-kernel-execution.md +71 -4
  114. package/docs/api/gpu-webgpu-switch.md +23 -0
  115. package/package.json +3 -1
@@ -0,0 +1,248 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * The composite-chart and mask-layer material module of the GPU display view (#491): the Speed kernel's `material_at`
4
+ * (the seam of #478, `DisplayViewGpuMaterialModule`) built from the composite chart kernel's classification (#477,
5
+ * `wgsl-chart-composite-kernel.ts`: `material()`, its bound arithmetic and double-float32), so that composite charts,
6
+ * every 2.3 element as a plain chart, the sine-profile Siemens star and declared `material-layers` scenes render on the
7
+ * GPU. The scene's packed records (`packChartScene`) are uploaded once per renderer and reused across a view's parts,
8
+ * as the composite kernel reuses them per accelerator; the device probe of the composite kernel gates the module.
9
+ *
10
+ * **The query point.** The Speed kernel hands the module p = fl(fl(x + c)·scale) per axis: the site coordinate x plus the
11
+ * offset constant c = 0.5 + o − W/2 (`DISPLAY_VIEW_AUX.offsets`), times the frame-units-per-site scale rounded once to
12
+ * float32, so p carries up to 2E relative error (E = 2⁻²³) against the reference's float64 query point. The composite
13
+ * kernel's chirp decisions (the zone plate's phase, the stars' sector cross products) need the point to about 2⁻⁴⁰; fed
14
+ * p directly they would flag most of a 64-cycle sine star. The module therefore **recovers the exact lattice point**: it
15
+ * knows the offsets c_o (uploaded as float32 hi + lo pairs) and the scale (hi + lo), divides p by the scale's high part
16
+ * (within 6E·|s| of s = x + c_o), picks the offset whose c_o puts the quotient nearest an integer x, and re-forms the point
17
+ * in double-float32 as (x + c_o)·scale from the exact integer and the float64 constants. The host admits a lattice only
18
+ * when the recovery is unambiguous: the distinct fractional parts of the offsets (mod 1) lie more than four tolerances
19
+ * apart, the tolerance being 8E·max|s| + 2⁻¹²; a point whose residual exceeds the tolerance on the device is flagged.
20
+ * The recovered point differs from the exact (x + c_o)·g by at most about 2⁻⁴⁶ relative (two double-float32 operations
21
+ * and the split constants' 2⁻⁴⁸), inside the composite kernel's own allowance for a point (DW = 2⁻⁴⁰ relative in `bd`).
22
+ * The reference's own float64 query point differs from the exact lattice value by its float64 rounding only: measured at
23
+ * most 5.6·10⁻¹⁶ absolute over every site and offset of the #491 fixtures, and checked by the host at the frame's four
24
+ * corners within 2⁻⁴⁸·(|p| + 1); that is inside the composite kernel's 10⁻¹⁴ slack for the reference's rounding. So the
25
+ * composite kernel's bounds and SAFETY factor (4) apply unchanged, and the module's point error is far inside the Speed
26
+ * kernel's contract (≤ 2E + 10⁻¹⁴): no Speed bound term changes.
27
+ *
28
+ * **A fixed-size chart** (`chartSize`): the reference maps the frame point to chart-local units, cu = (u·m)/h with m the
29
+ * metres per frame unit and h the chart's half width, and returns the surround material outside |cu| ≤ 1, |cv| ≤ aspect.
30
+ * The module multiplies the recovered point by k = m/h (hi + lo) in double-float32 and tests the chart rectangle in bound
31
+ * arithmetic (flagging points within their bound of its edge); the surround is the materials table's last entry.
32
+ */
33
+ import { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from "./image-kernel-execution.js";
34
+ import { chartCompositeWgsl } from "./wgsl-chart-composite-kernel.js";
35
+ import { chartGpuPresetCode } from "./chart-radiance-gpu.js";
36
+ import { dwSplit, packChartScene, probeChartCompositeDevice } from "./chart-composite-gpu.js";
37
+ import { chartPresetMaterialModule } from "./display-view-chart-material-module.js";
38
+ import { BUFFER_USAGE, GpuResourceScope } from "./webgpu-runtime.js";
39
+ import { DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS } from "./wgsl-display-view-speed-kernel.js";
40
+ /** One WGSL rounding of + − × in either direction (E = 2⁻²³). */
41
+ const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
42
+ /** The module's id in the pipeline cache (one shader per product path for every composite scene; the scene is data): the Dekker path's. */
43
+ export const CHART_COMPOSITE_MATERIAL_MODULE_ID = "chart-composite";
44
+ /** The module's id per product path (the fma path's shader is a different pipeline). */
45
+ export function chartCompositeMaterialModuleId(path) { return path === "dekker" ? CHART_COMPOSITE_MATERIAL_MODULE_ID : CHART_COMPOSITE_MATERIAL_MODULE_ID + "-fma"; }
46
+ /** Scenes whose records stay uploaded per renderer (the composite kernel keeps the same number per accelerator). */
47
+ const KEPT_SCENES = 4;
48
+ /** The corner check of the query-point model against the reference's own query points, relative to |p| + 1 (inside the composite kernel's 10⁻¹⁴ slack). */
49
+ const MODEL_TOLERANCE = 2 ** -48;
50
+ /** Layout of the lattice uniform (float32 / u32 words, 48 bytes) and the offsets table (one vec4 per offset: cu hi, cu lo, cv hi, cv lo). */
51
+ export const DISPLAY_VIEW_LATTICE = Object.freeze({ scaleUHi: 0, scaleULo: 1, scaleVHi: 2, scaleVLo: 3, chartKHi: 4, chartKLo: 5, chartAspect: 6, tolerance: 7, offsets: 8, placed: 9, surround: 10, zero: 11, words: 12 });
52
+ /**
53
+ * The composite kernel's classification (`material(p: vec4<f32>)` with its bound arithmetic, double-float32, cos polynomial,
54
+ * elements and layers) renamed for the display-view kernel, its records bound at @group(1), and the lattice recovery that
55
+ * defines `material_at(p: vec2<f32>)` (see the module comment). The constants E and SLACK come from the kernel's preamble. `path`
56
+ * is the double-word product path the device's probe chose (default Dekker's).
57
+ */
58
+ export function chartCompositeMaterialWgsl(path = "dekker") {
59
+ const source = chartCompositeWgsl(path);
60
+ const commonStart = source.indexOf("const DW: f32"), commonEnd = source.indexOf("struct Params"), bodyStart = source.indexOf("const SEGMENT_BOX"), bodyEnd = source.indexOf("@compute");
61
+ if (commonStart < 0 || commonEnd < commonStart || bodyStart < commonEnd || bodyEnd < bodyStart)
62
+ throw new ImageKernelExecutionError("gpu-validation", "The composite chart kernel's material functions were not found.");
63
+ const rename = (code) => code.replace(/^const SLACK: f32 = [^\n]*\n/mu, "").replace(/\bflagged\b/gu, "material_uncertain").replace(/\bmargin\(/gu, "bmargin(").replace(/\bparams\.zero\b/gu, "lattice.zero");
64
+ return `${rename(source.slice(commonStart, commonEnd))}
65
+ @group(1) @binding(0) var<storage, read> af: array<f32>;
66
+ @group(1) @binding(1) var<storage, read> ai: array<u32>;
67
+ struct LatticeParams {
68
+ scale_u_hi: f32, scale_u_lo: f32, scale_v_hi: f32, scale_v_lo: f32,
69
+ chart_k_hi: f32, chart_k_lo: f32, chart_aspect: f32, tolerance: f32,
70
+ offsets: u32, placed: u32, surround: u32, zero: u32,
71
+ };
72
+ @group(1) @binding(2) var<uniform> lattice: LatticeParams;
73
+ @group(1) @binding(3) var<storage, read> lattice_offsets: array<vec4<f32>>;
74
+ ${rename(source.slice(bodyStart, bodyEnd))}
75
+ // The exact lattice point of one axis from the kernel's p = fl(fl(x + c)·scale): the offset whose constant c puts p/scale
76
+ // nearest an integer x, then (x + c)·scale in double-float32 from the exact integer and the float64 constants (hi + lo).
77
+ fn lattice_point(p: f32, scale: D, axis: u32) -> D {
78
+ let rough = p / scale.h;
79
+ var best = 3.0e38; var site = 0.0; var c_hi = 0.0; var c_lo = 0.0;
80
+ for (var o = 0u; o < lattice.offsets; o = o + 1u) {
81
+ let row = lattice_offsets[o];
82
+ let hi = select(row.z, row.x, axis == 0u);
83
+ let lo = select(row.w, row.y, axis == 0u);
84
+ let d = rough - hi;
85
+ let n = round(d);
86
+ let r = abs(d - n);
87
+ if (r < best) { best = r; site = n; c_hi = hi; c_lo = lo; }
88
+ }
89
+ if (!(best <= lattice.tolerance)) { material_uncertain = true; }
90
+ return d_mul(d_add(D(site, 0.0), D(c_hi, c_lo)), scale);
91
+ }
92
+ fn material_at(p: vec2<f32>) -> u32 {
93
+ var u = lattice_point(p.x, D(lattice.scale_u_hi, lattice.scale_u_lo), 0u);
94
+ var v = lattice_point(p.y, D(lattice.scale_v_hi, lattice.scale_v_lo), 1u);
95
+ if (lattice.placed != 0u) {
96
+ let k = D(lattice.chart_k_hi, lattice.chart_k_lo);
97
+ u = d_mul(u, k); v = d_mul(v, k);
98
+ if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bc(lattice.chart_aspect)))) { return lattice.surround; }
99
+ }
100
+ return material(vec4<f32>(u.h, u.l, v.h, v.l));
101
+ }
102
+ `;
103
+ }
104
+ /**
105
+ * The query lattice of an expected-signal input as the module needs it, or an `unsupported-on-backend` error: the scale per
106
+ * axis from the reference's own query points (two sites a frame apart), the model (x + c)·scale checked against the reference
107
+ * at the frame's four corners for every offset within 2⁻⁴⁸·(|p| + 1), the offsets as hi + lo pairs, and the recovery tolerance
108
+ * 8E·max|x + c| + 2⁻¹², which the distinct fractional parts of the offsets must clear four times over.
109
+ */
110
+ export function displayViewLatticeConstants(signalInput, placement, surroundMaterial) {
111
+ const W = signalInput.nativeWidth, H = signalInput.nativeHeight, offsets = signalInput.offsets, query = signalInput.queryPoint;
112
+ if (!query)
113
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The expected signal has no query points (the Quality path); use cpu-reference.");
114
+ if (offsets.length < 1 || offsets.length > DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS)
115
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The kernel holds 1 to " + DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS + " query offsets.");
116
+ if (!(W >= 2 && H >= 2 && W <= 2 ** 20 && H <= 2 ** 20))
117
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The native frame is outside the lattice recovery's range (2 to 2^20 sites per axis).");
118
+ const point = new Float64Array(2), x1 = W - 1, y1 = H - 1;
119
+ query(0, 0, 0, point);
120
+ const u0 = point[0], v0 = point[1];
121
+ query(x1, 0, 0, point);
122
+ const u1 = point[0];
123
+ query(0, y1, 0, point);
124
+ const v1 = point[1];
125
+ const scaleU = (u1 - u0) / x1, scaleV = (v1 - v0) / y1;
126
+ if (!(Number.isFinite(scaleU) && Number.isFinite(scaleV) && scaleU !== 0 && scaleV !== 0))
127
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The request's query lattice has no scale; use cpu-reference.");
128
+ const cu = new Float64Array(offsets.length), cv = new Float64Array(offsets.length);
129
+ let smax = 0;
130
+ for (let o = 0; o < offsets.length; o++) {
131
+ const offset = offsets[o];
132
+ cu[o] = 0.5 + offset.x - W / 2;
133
+ cv[o] = 0.5 + offset.y - H / 2;
134
+ smax = Math.max(smax, Math.abs(cu[o]), Math.abs(x1 + cu[o]), Math.abs(cv[o]), Math.abs(y1 + cv[o]));
135
+ for (const [x, y] of [[0, 0], [x1, 0], [0, y1], [x1, y1]]) {
136
+ query(x, y, o, point);
137
+ const mu = (x + cu[o]) * scaleU, mv = (y + cv[o]) * scaleV;
138
+ if (!(Math.abs(mu - point[0]) <= MODEL_TOLERANCE * (Math.abs(point[0]) + 1) && Math.abs(mv - point[1]) <= MODEL_TOLERANCE * (Math.abs(point[1]) + 1)))
139
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The request's query points are not a rectilinear site lattice within 2^-48; use cpu-reference.");
140
+ }
141
+ }
142
+ const tolerance = Math.fround(8 * E * smax + 2 ** -12);
143
+ if (!(tolerance < 1 / 8))
144
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The query lattice is too wide for the exact point recovery; use cpu-reference.");
145
+ // Distinct fractional parts of the offsets must be told apart on the device: more than four tolerances apart, or an exact integer apart (the same lattice).
146
+ for (const axis of [cu, cv]) {
147
+ for (let a = 0; a < axis.length; a++)
148
+ for (let b = a + 1; b < axis.length; b++) {
149
+ const delta = axis[a] - axis[b], fractional = Math.abs(delta - Math.round(delta));
150
+ if (fractional !== 0 && !(fractional > 4 * tolerance))
151
+ throw new ImageKernelExecutionError("unsupported-on-backend", "Two query offsets are too close (mod one site) for the exact point recovery; use cpu-reference.");
152
+ }
153
+ }
154
+ const params = new ArrayBuffer(4 * DISPLAY_VIEW_LATTICE.words), floats = new Float32Array(params), words = new Uint32Array(params);
155
+ const [suH, suL] = dwSplit(scaleU), [svH, svL] = dwSplit(scaleV);
156
+ floats[DISPLAY_VIEW_LATTICE.scaleUHi] = suH;
157
+ floats[DISPLAY_VIEW_LATTICE.scaleULo] = suL;
158
+ floats[DISPLAY_VIEW_LATTICE.scaleVHi] = svH;
159
+ floats[DISPLAY_VIEW_LATTICE.scaleVLo] = svL;
160
+ if (placement) {
161
+ const [kH, kL] = dwSplit(placement.chartUnitsPerFrameUnit);
162
+ if (!(Number.isFinite(kH) && kH > 0 && Number.isFinite(placement.aspect) && placement.aspect > 0))
163
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The chart placement is not finite.");
164
+ floats[DISPLAY_VIEW_LATTICE.chartKHi] = kH;
165
+ floats[DISPLAY_VIEW_LATTICE.chartKLo] = kL;
166
+ floats[DISPLAY_VIEW_LATTICE.chartAspect] = placement.aspect;
167
+ }
168
+ floats[DISPLAY_VIEW_LATTICE.tolerance] = tolerance;
169
+ words[DISPLAY_VIEW_LATTICE.offsets] = offsets.length;
170
+ words[DISPLAY_VIEW_LATTICE.placed] = placement ? 1 : 0;
171
+ words[DISPLAY_VIEW_LATTICE.surround] = surroundMaterial;
172
+ words[DISPLAY_VIEW_LATTICE.zero] = 0; // the rounding barrier's zero (`lattice.zero`)
173
+ const table = new Float32Array(4 * offsets.length);
174
+ for (let o = 0; o < offsets.length; o++) {
175
+ const [uH, uL] = dwSplit(cu[o]), [vH, vL] = dwSplit(cv[o]);
176
+ table[4 * o] = uH;
177
+ table[4 * o + 1] = uL;
178
+ table[4 * o + 2] = vH;
179
+ table[4 * o + 3] = vL;
180
+ }
181
+ return { params, offsets: table, tolerance, scaleU, scaleV };
182
+ }
183
+ /** The resolved scene behind a GPU setup: the chart whose geometry is packed, the layers (or null) and the materials the kernel indexes. */
184
+ function sceneOf(gpu) {
185
+ if (gpu.layers)
186
+ return { chart: gpu.layers.chart, layers: gpu.layers, materials: gpu.layers.materials, key: gpu.layers };
187
+ if (gpu.chart)
188
+ return { chart: gpu.chart, layers: null, materials: gpu.chart.materials, key: gpu.chart };
189
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The GPU material lookup covers chart presets, composite charts and declared material-layers scenes; a caller scene function runs on cpu-reference (describe it as material-layers).");
190
+ }
191
+ export function createDisplayViewMaterialModules(runtime, pipelines) {
192
+ const presets = new WeakMap(), uploads = [];
193
+ let probed = null;
194
+ const wgsl = new Map();
195
+ const probe = (signal) => {
196
+ probed ??= probeChartCompositeDevice(runtime, pipelines, signal).catch((error) => { probed = null; throw error; });
197
+ return probed;
198
+ };
199
+ const records = (scene) => {
200
+ const index = uploads.findIndex((e) => e.key === scene.key);
201
+ if (index >= 0) {
202
+ const [entry] = uploads.splice(index, 1);
203
+ uploads.push(entry);
204
+ return entry;
205
+ }
206
+ // packChartScene reads the plan's chart, layers and materials only.
207
+ const packed = packChartScene({ chart: scene.chart, layers: scene.layers, materials: scene.materials });
208
+ if (Math.max(packed.af.byteLength, packed.ai.byteLength) > runtime.maximumStorageBindingBytes)
209
+ throw new ImageKernelExecutionError("insufficient-limits", "Chart records exceed the device's storage binding limit.");
210
+ const scope = new GpuResourceScope(runtime);
211
+ const entry = { key: scene.key, scope, af: scope.upload("photivra-display-view-chart-floats", packed.af, BUFFER_USAGE.STORAGE), ai: scope.upload("photivra-display-view-chart-records", packed.ai, BUFFER_USAGE.STORAGE), packed };
212
+ uploads.push(entry);
213
+ while (uploads.length > KEPT_SCENES)
214
+ uploads.shift().scope.release();
215
+ return entry;
216
+ };
217
+ return {
218
+ async moduleFor(gpu, signal) {
219
+ const scene = sceneOf(gpu), placement = gpu.placement ?? null, signalInput = gpu.signalInput;
220
+ const tableMaterials = signalInput.scene.kind === "materials" ? signalInput.scene.materials.length : -1;
221
+ if (tableMaterials !== scene.materials.length + (placement ? 1 : 0))
222
+ throw new ImageKernelExecutionError("unsupported-on-backend", "The expected signal's materials do not match the scene's (" + tableMaterials + " against " + scene.materials.length + (placement ? " plus the surround" : "") + "); use cpu-reference.");
223
+ if (!scene.layers && !placement && chartGpuPresetCode({ chart: scene.chart }) !== undefined) {
224
+ let module = presets.get(scene.chart);
225
+ if (!module) {
226
+ module = chartPresetMaterialModule(scene.chart);
227
+ presets.set(scene.chart, module);
228
+ }
229
+ return module;
230
+ }
231
+ const record = await probe(signal), path = record.productPath ?? "dekker";
232
+ const lattice = displayViewLatticeConstants(signalInput, placement, scene.materials.length), uploaded = records(scene);
233
+ let code = wgsl.get(path);
234
+ if (code === undefined) {
235
+ code = chartCompositeMaterialWgsl(path);
236
+ wgsl.set(path, code);
237
+ }
238
+ return { id: chartCompositeMaterialModuleId(path), wgsl: code, compositeProbe: record, bindings: [
239
+ { binding: 0, kind: "storage", buffer: uploaded.af }, { binding: 1, kind: "storage", buffer: uploaded.ai },
240
+ { binding: 2, kind: "uniform", data: new Uint8Array(lattice.params) }, { binding: 3, kind: "storage", data: lattice.offsets }
241
+ ] };
242
+ },
243
+ get uploadedScenes() { return uploads.length; },
244
+ release() { for (const entry of uploads.splice(0))
245
+ entry.scope.release(); }
246
+ };
247
+ }
248
+ //# sourceMappingURL=display-view-composite-material-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display-view-composite-material-module.js","sourceRoot":"","sources":["../../src/gpu/display-view-composite-material-module.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The composite-chart and mask-layer material module of the GPU display view (#491): the Speed kernel's `material_at`\n * (the seam of #478, `DisplayViewGpuMaterialModule`) built from the composite chart kernel's classification (#477,\n * `wgsl-chart-composite-kernel.ts`: `material()`, its bound arithmetic and double-float32), so that composite charts,\n * every 2.3 element as a plain chart, the sine-profile Siemens star and declared `material-layers` scenes render on the\n * GPU. The scene's packed records (`packChartScene`) are uploaded once per renderer and reused across a view's parts,\n * as the composite kernel reuses them per accelerator; the device probe of the composite kernel gates the module.\n *\n * **The query point.** The Speed kernel hands the module p = fl(fl(x + c)·scale) per axis: the site coordinate x plus the\n * offset constant c = 0.5 + o − W/2 (`DISPLAY_VIEW_AUX.offsets`), times the frame-units-per-site scale rounded once to\n * float32, so p carries up to 2E relative error (E = 2⁻²³) against the reference's float64 query point. The composite\n * kernel's chirp decisions (the zone plate's phase, the stars' sector cross products) need the point to about 2⁻⁴⁰; fed\n * p directly they would flag most of a 64-cycle sine star. The module therefore **recovers the exact lattice point**: it\n * knows the offsets c_o (uploaded as float32 hi + lo pairs) and the scale (hi + lo), divides p by the scale's high part\n * (within 6E·|s| of s = x + c_o), picks the offset whose c_o puts the quotient nearest an integer x, and re-forms the point\n * in double-float32 as (x + c_o)·scale from the exact integer and the float64 constants. The host admits a lattice only\n * when the recovery is unambiguous: the distinct fractional parts of the offsets (mod 1) lie more than four tolerances\n * apart, the tolerance being 8E·max|s| + 2⁻¹²; a point whose residual exceeds the tolerance on the device is flagged.\n * The recovered point differs from the exact (x + c_o)·g by at most about 2⁻⁴⁶ relative (two double-float32 operations\n * and the split constants' 2⁻⁴⁸), inside the composite kernel's own allowance for a point (DW = 2⁻⁴⁰ relative in `bd`).\n * The reference's own float64 query point differs from the exact lattice value by its float64 rounding only: measured at\n * most 5.6·10⁻¹⁶ absolute over every site and offset of the #491 fixtures, and checked by the host at the frame's four\n * corners within 2⁻⁴⁸·(|p| + 1); that is inside the composite kernel's 10⁻¹⁴ slack for the reference's rounding. So the\n * composite kernel's bounds and SAFETY factor (4) apply unchanged, and the module's point error is far inside the Speed\n * kernel's contract (≤ 2E + 10⁻¹⁴): no Speed bound term changes.\n *\n * **A fixed-size chart** (`chartSize`): the reference maps the frame point to chart-local units, cu = (u·m)/h with m the\n * metres per frame unit and h the chart's half width, and returns the surround material outside |cu| ≤ 1, |cv| ≤ aspect.\n * The module multiplies the recovered point by k = m/h (hi + lo) in double-float32 and tests the chart rectangle in bound\n * arithmetic (flagging points within their bound of its edge); the surround is the materials table's last entry.\n */\nimport { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF } from \"./image-kernel-execution.js\";\nimport { chartCompositeWgsl, type ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { chartGpuPresetCode } from \"./chart-radiance-gpu.js\";\nimport { dwSplit, packChartScene, probeChartCompositeDevice, type PackedChartScene } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProbeRecord } from \"./image-kernel-execution.js\";\nimport { chartPresetMaterialModule } from \"./display-view-chart-material-module.js\";\nimport type { ChartRadianceBatchPlan } from \"../facade/chart-radiance-batch.js\";\nimport type { ResolvedChartScene } from \"../facade/chart-scenes.js\";\nimport type { ResolvedChartMaterialLayers } from \"../facade/chart-material-layers.js\";\nimport type { DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport type { DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport { BUFFER_USAGE, GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS } from \"./wgsl-display-view-speed-kernel.js\";\nimport type { DisplayViewGpuMaterialModule } from \"./display-view-speed-gpu.js\";\n\ntype PipelineSource = (label: string, code: string) => Promise<GPUComputePipeline>;\n\n/** One WGSL rounding of + − × in either direction (E = 2⁻²³). */\nconst E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;\n/** The module's id in the pipeline cache (one shader per product path for every composite scene; the scene is data): the Dekker path's. */\nexport const CHART_COMPOSITE_MATERIAL_MODULE_ID = \"chart-composite\";\n/** The module's id per product path (the fma path's shader is a different pipeline). */\nexport function chartCompositeMaterialModuleId(path: ChartCompositeProductPath): string { return path === \"dekker\" ? CHART_COMPOSITE_MATERIAL_MODULE_ID : CHART_COMPOSITE_MATERIAL_MODULE_ID + \"-fma\"; }\n/** Scenes whose records stay uploaded per renderer (the composite kernel keeps the same number per accelerator). */\nconst KEPT_SCENES = 4;\n/** The corner check of the query-point model against the reference's own query points, relative to |p| + 1 (inside the composite kernel's 10⁻¹⁴ slack). */\nconst MODEL_TOLERANCE = 2 ** -48;\n\n/** Layout of the lattice uniform (float32 / u32 words, 48 bytes) and the offsets table (one vec4 per offset: cu hi, cu lo, cv hi, cv lo). */\nexport const DISPLAY_VIEW_LATTICE = Object.freeze({ scaleUHi: 0, scaleULo: 1, scaleVHi: 2, scaleVLo: 3, chartKHi: 4, chartKLo: 5, chartAspect: 6, tolerance: 7, offsets: 8, placed: 9, surround: 10, zero: 11, words: 12 });\n\n/**\n * The composite kernel's classification (`material(p: vec4<f32>)` with its bound arithmetic, double-float32, cos polynomial,\n * elements and layers) renamed for the display-view kernel, its records bound at @group(1), and the lattice recovery that\n * defines `material_at(p: vec2<f32>)` (see the module comment). The constants E and SLACK come from the kernel's preamble. `path`\n * is the double-word product path the device's probe chose (default Dekker's).\n */\nexport function chartCompositeMaterialWgsl(path: ChartCompositeProductPath = \"dekker\"): string {\n const source = chartCompositeWgsl(path);\n const commonStart = source.indexOf(\"const DW: f32\"), commonEnd = source.indexOf(\"struct Params\"), bodyStart = source.indexOf(\"const SEGMENT_BOX\"), bodyEnd = source.indexOf(\"@compute\");\n if (commonStart < 0 || commonEnd < commonStart || bodyStart < commonEnd || bodyEnd < bodyStart) throw new ImageKernelExecutionError(\"gpu-validation\", \"The composite chart kernel's material functions were not found.\");\n const rename = (code: string): string => code.replace(/^const SLACK: f32 = [^\\n]*\\n/mu, \"\").replace(/\\bflagged\\b/gu, \"material_uncertain\").replace(/\\bmargin\\(/gu, \"bmargin(\").replace(/\\bparams\\.zero\\b/gu, \"lattice.zero\");\n return `${rename(source.slice(commonStart, commonEnd))}\n@group(1) @binding(0) var<storage, read> af: array<f32>;\n@group(1) @binding(1) var<storage, read> ai: array<u32>;\nstruct LatticeParams {\n scale_u_hi: f32, scale_u_lo: f32, scale_v_hi: f32, scale_v_lo: f32,\n chart_k_hi: f32, chart_k_lo: f32, chart_aspect: f32, tolerance: f32,\n offsets: u32, placed: u32, surround: u32, zero: u32,\n};\n@group(1) @binding(2) var<uniform> lattice: LatticeParams;\n@group(1) @binding(3) var<storage, read> lattice_offsets: array<vec4<f32>>;\n${rename(source.slice(bodyStart, bodyEnd))}\n// The exact lattice point of one axis from the kernel's p = fl(fl(x + c)·scale): the offset whose constant c puts p/scale\n// nearest an integer x, then (x + c)·scale in double-float32 from the exact integer and the float64 constants (hi + lo).\nfn lattice_point(p: f32, scale: D, axis: u32) -> D {\n let rough = p / scale.h;\n var best = 3.0e38; var site = 0.0; var c_hi = 0.0; var c_lo = 0.0;\n for (var o = 0u; o < lattice.offsets; o = o + 1u) {\n let row = lattice_offsets[o];\n let hi = select(row.z, row.x, axis == 0u);\n let lo = select(row.w, row.y, axis == 0u);\n let d = rough - hi;\n let n = round(d);\n let r = abs(d - n);\n if (r < best) { best = r; site = n; c_hi = hi; c_lo = lo; }\n }\n if (!(best <= lattice.tolerance)) { material_uncertain = true; }\n return d_mul(d_add(D(site, 0.0), D(c_hi, c_lo)), scale);\n}\nfn material_at(p: vec2<f32>) -> u32 {\n var u = lattice_point(p.x, D(lattice.scale_u_hi, lattice.scale_u_lo), 0u);\n var v = lattice_point(p.y, D(lattice.scale_v_hi, lattice.scale_v_lo), 1u);\n if (lattice.placed != 0u) {\n let k = D(lattice.chart_k_hi, lattice.chart_k_lo);\n u = d_mul(u, k); v = d_mul(v, k);\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bc(lattice.chart_aspect)))) { return lattice.surround; }\n }\n return material(vec4<f32>(u.h, u.l, v.h, v.l));\n}\n`;\n}\n\n/** A fixed-size chart's placement for the module: chart-local frame units per frame unit (m/h) and the chart's aspect. */\nexport interface DisplayViewChartPlacement { chartUnitsPerFrameUnit: number; aspect: number }\n\n/** The lattice constants of a render (see the module comment): the uniform words and the offsets table. */\nexport interface DisplayViewLatticeConstants { params: ArrayBuffer; offsets: Float32Array<ArrayBuffer>; tolerance: number; scaleU: number; scaleV: number }\n\n/**\n * The query lattice of an expected-signal input as the module needs it, or an `unsupported-on-backend` error: the scale per\n * axis from the reference's own query points (two sites a frame apart), the model (x + c)·scale checked against the reference\n * at the frame's four corners for every offset within 2⁻⁴⁸·(|p| + 1), the offsets as hi + lo pairs, and the recovery tolerance\n * 8E·max|x + c| + 2⁻¹², which the distinct fractional parts of the offsets must clear four times over.\n */\nexport function displayViewLatticeConstants(signalInput: DisplayViewExpectedSignalInput, placement: DisplayViewChartPlacement | null, surroundMaterial: number): DisplayViewLatticeConstants {\n const W = signalInput.nativeWidth, H = signalInput.nativeHeight, offsets = signalInput.offsets, query = signalInput.queryPoint;\n if (!query) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The expected signal has no query points (the Quality path); use cpu-reference.\");\n if (offsets.length < 1 || offsets.length > DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The kernel holds 1 to \" + DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS + \" query offsets.\");\n if (!(W >= 2 && H >= 2 && W <= 2 ** 20 && H <= 2 ** 20)) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The native frame is outside the lattice recovery's range (2 to 2^20 sites per axis).\");\n const point = new Float64Array(2), x1 = W - 1, y1 = H - 1;\n query(0, 0, 0, point); const u0 = point[0]!, v0 = point[1]!;\n query(x1, 0, 0, point); const u1 = point[0]!;\n query(0, y1, 0, point); const v1 = point[1]!;\n const scaleU = (u1 - u0) / x1, scaleV = (v1 - v0) / y1;\n if (!(Number.isFinite(scaleU) && Number.isFinite(scaleV) && scaleU !== 0 && scaleV !== 0)) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The request's query lattice has no scale; use cpu-reference.\");\n const cu = new Float64Array(offsets.length), cv = new Float64Array(offsets.length);\n let smax = 0;\n for (let o = 0; o < offsets.length; o++) {\n const offset = offsets[o]!;\n cu[o] = 0.5 + offset.x - W / 2; cv[o] = 0.5 + offset.y - H / 2;\n smax = Math.max(smax, Math.abs(cu[o]!), Math.abs(x1 + cu[o]!), Math.abs(cv[o]!), Math.abs(y1 + cv[o]!));\n for (const [x, y] of [[0, 0], [x1, 0], [0, y1], [x1, y1]] as const) {\n query(x, y, o, point);\n const mu = (x + cu[o]!) * scaleU, mv = (y + cv[o]!) * scaleV;\n if (!(Math.abs(mu - point[0]!) <= MODEL_TOLERANCE * (Math.abs(point[0]!) + 1) && Math.abs(mv - point[1]!) <= MODEL_TOLERANCE * (Math.abs(point[1]!) + 1)))\n throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The request's query points are not a rectilinear site lattice within 2^-48; use cpu-reference.\");\n }\n }\n const tolerance = Math.fround(8 * E * smax + 2 ** -12);\n if (!(tolerance < 1 / 8)) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The query lattice is too wide for the exact point recovery; use cpu-reference.\");\n // Distinct fractional parts of the offsets must be told apart on the device: more than four tolerances apart, or an exact integer apart (the same lattice).\n for (const axis of [cu, cv]) {\n for (let a = 0; a < axis.length; a++) for (let b = a + 1; b < axis.length; b++) {\n const delta = axis[a]! - axis[b]!, fractional = Math.abs(delta - Math.round(delta));\n if (fractional !== 0 && !(fractional > 4 * tolerance))\n throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"Two query offsets are too close (mod one site) for the exact point recovery; use cpu-reference.\");\n }\n }\n const params = new ArrayBuffer(4 * DISPLAY_VIEW_LATTICE.words), floats = new Float32Array(params), words = new Uint32Array(params);\n const [suH, suL] = dwSplit(scaleU), [svH, svL] = dwSplit(scaleV);\n floats[DISPLAY_VIEW_LATTICE.scaleUHi] = suH; floats[DISPLAY_VIEW_LATTICE.scaleULo] = suL; floats[DISPLAY_VIEW_LATTICE.scaleVHi] = svH; floats[DISPLAY_VIEW_LATTICE.scaleVLo] = svL;\n if (placement) {\n const [kH, kL] = dwSplit(placement.chartUnitsPerFrameUnit);\n if (!(Number.isFinite(kH) && kH > 0 && Number.isFinite(placement.aspect) && placement.aspect > 0)) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The chart placement is not finite.\");\n floats[DISPLAY_VIEW_LATTICE.chartKHi] = kH; floats[DISPLAY_VIEW_LATTICE.chartKLo] = kL; floats[DISPLAY_VIEW_LATTICE.chartAspect] = placement.aspect;\n }\n floats[DISPLAY_VIEW_LATTICE.tolerance] = tolerance;\n words[DISPLAY_VIEW_LATTICE.offsets] = offsets.length; words[DISPLAY_VIEW_LATTICE.placed] = placement ? 1 : 0; words[DISPLAY_VIEW_LATTICE.surround] = surroundMaterial;\n words[DISPLAY_VIEW_LATTICE.zero] = 0; // the rounding barrier's zero (`lattice.zero`)\n const table = new Float32Array(4 * offsets.length);\n for (let o = 0; o < offsets.length; o++) {\n const [uH, uL] = dwSplit(cu[o]!), [vH, vL] = dwSplit(cv[o]!);\n table[4 * o] = uH; table[4 * o + 1] = uL; table[4 * o + 2] = vH; table[4 * o + 3] = vL;\n }\n return { params, offsets: table, tolerance, scaleU, scaleV };\n}\n\n/** The resolved scene behind a GPU setup: the chart whose geometry is packed, the layers (or null) and the materials the kernel indexes. */\nfunction sceneOf(gpu: DisplayViewGpuSetup): { chart: ResolvedChartScene; layers: ResolvedChartMaterialLayers | null; materials: ResolvedChartScene[\"materials\"]; key: object } {\n if (gpu.layers) return { chart: gpu.layers.chart, layers: gpu.layers, materials: gpu.layers.materials, key: gpu.layers };\n if (gpu.chart) return { chart: gpu.chart, layers: null, materials: gpu.chart.materials, key: gpu.chart };\n throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The GPU material lookup covers chart presets, composite charts and declared material-layers scenes; a caller scene function runs on cpu-reference (describe it as material-layers).\");\n}\n\ninterface UploadedRecords { key: object; scope: GpuResourceScope; af: GPUBuffer; ai: GPUBuffer; packed: PackedChartScene }\n\n/**\n * The material modules of one display-view renderer (one device): the chart-preset module for the eight 2.0 presets filling\n * the frame, the composite module for everything else the chart kernels classify. The composite module's device probe runs\n * once per renderer (single flight; a refusal is retried on the next render), its scene records are uploaded once per scene\n * and the four most recent kept until `release()`.\n */\nexport interface DisplayViewMaterialModules {\n /** The module of a setup's scene, or an `unsupported-on-backend` error (a caller function scene, a failed probe, a materials mismatch). */\n moduleFor(gpu: DisplayViewGpuSetup, signal: AbortSignal): Promise<DisplayViewGpuMaterialModule>;\n /** Uploaded scenes (for tests). */\n readonly uploadedScenes: number;\n release(): void;\n}\n\nexport function createDisplayViewMaterialModules(runtime: WebGpuRuntime, pipelines: PipelineSource): DisplayViewMaterialModules {\n const presets = new WeakMap<ResolvedChartScene, DisplayViewGpuMaterialModule>(), uploads: UploadedRecords[] = [];\n let probed: Promise<ChartCompositeProbeRecord> | null = null;\n const wgsl = new Map<ChartCompositeProductPath, string>();\n const probe = (signal: AbortSignal): Promise<ChartCompositeProbeRecord> => {\n probed ??= probeChartCompositeDevice(runtime, pipelines, signal).catch((error: unknown) => { probed = null; throw error; });\n return probed;\n };\n const records = (scene: ReturnType<typeof sceneOf>): UploadedRecords => {\n const index = uploads.findIndex((e) => e.key === scene.key);\n if (index >= 0) { const [entry] = uploads.splice(index, 1); uploads.push(entry!); return entry!; }\n // packChartScene reads the plan's chart, layers and materials only.\n const packed = packChartScene({ chart: scene.chart, layers: scene.layers, materials: scene.materials } as unknown as ChartRadianceBatchPlan);\n if (Math.max(packed.af.byteLength, packed.ai.byteLength) > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"Chart records exceed the device's storage binding limit.\");\n const scope = new GpuResourceScope(runtime);\n const entry: UploadedRecords = { key: scene.key, scope, af: scope.upload(\"photivra-display-view-chart-floats\", packed.af, BUFFER_USAGE.STORAGE), ai: scope.upload(\"photivra-display-view-chart-records\", packed.ai, BUFFER_USAGE.STORAGE), packed };\n uploads.push(entry);\n while (uploads.length > KEPT_SCENES) uploads.shift()!.scope.release();\n return entry;\n };\n return {\n async moduleFor(gpu, signal): Promise<DisplayViewGpuMaterialModule> {\n const scene = sceneOf(gpu), placement = gpu.placement ?? null, signalInput = gpu.signalInput;\n const tableMaterials = signalInput.scene.kind === \"materials\" ? signalInput.scene.materials.length : -1;\n if (tableMaterials !== scene.materials.length + (placement ? 1 : 0))\n throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The expected signal's materials do not match the scene's (\" + tableMaterials + \" against \" + scene.materials.length + (placement ? \" plus the surround\" : \"\") + \"); use cpu-reference.\");\n if (!scene.layers && !placement && chartGpuPresetCode({ chart: scene.chart } as unknown as ChartRadianceBatchPlan) !== undefined) {\n let module = presets.get(scene.chart);\n if (!module) { module = chartPresetMaterialModule(scene.chart); presets.set(scene.chart, module); }\n return module;\n }\n const record = await probe(signal), path = record.productPath ?? \"dekker\";\n const lattice = displayViewLatticeConstants(signalInput, placement, scene.materials.length), uploaded = records(scene);\n let code = wgsl.get(path);\n if (code === undefined) { code = chartCompositeMaterialWgsl(path); wgsl.set(path, code); }\n return { id: chartCompositeMaterialModuleId(path), wgsl: code, compositeProbe: record, bindings: [\n { binding: 0, kind: \"storage\", buffer: uploaded.af }, { binding: 1, kind: \"storage\", buffer: uploaded.ai },\n { binding: 2, kind: \"uniform\", data: new Uint8Array(lattice.params) }, { binding: 3, kind: \"storage\", data: lattice.offsets }] };\n },\n get uploadedScenes(): number { return uploads.length; },\n release(): void { for (const entry of uploads.splice(0)) entry.scope.release(); }\n };\n}\n"]}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Host side of the display view's Quality path on the GPU (#479): the support check, the float64 tables (the lattice groups
3
+ * and their combined diffraction kernels per material and channel, as the reference forms them; the bilinear and AHD
4
+ * candidate kernels; the footprint geometry), the three passes per band of display rows (lattice materials, site moments,
5
+ * display pixels; `wgsl-display-view-quality-kernel.ts`), the low-signal correction states (computed on the host with the
6
+ * reference's own `computeDisplayViewCorrectionStates`, #447, uploaded as a sorted table and grown from the grid keys the
7
+ * kernel records), and the reference's own float64 finishing per display pixel (`finishDisplayViewPixel` of the Speed host:
8
+ * the quantization term, the seeded noise draw, the Cholesky factor, the clipped centre, the sRGB encode and the rounding),
9
+ * so the noise draw is bit-identical to the CPU's.
10
+ *
11
+ * Defocus (#492 step 2c): out of focus the expected signal carries the defocus disc (or each node's diffraction kernel
12
+ * convolved with it) as site kernels, up to radius 96. The site pass then reads a factored table, sums in blocks, and reads a
13
+ * lattice whose uncertain points the host resolved with the reference's own material lookup; a band's site pass is split over
14
+ * dispatches of at most `DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH` taps. A defocused Speed request runs these passes with
15
+ * its bilinear demosaic (docs/WEBGPU.md, "Defocus on the GPU").
16
+ *
17
+ * The float64 reference is `renderDisplayView` (facade/display-view.ts) on the exact-area-mean path with
18
+ * `createDisplayViewExpectedSignal` (facade/expected-signal.ts), chart materials, with or without diffraction kernels,
19
+ * the bilinear or the AHD candidates and the low-signal correction. Internal: not exported from the package.
20
+ *
21
+ * Flagged display pixels (an uncertain lattice material among the sites read, a missing correction state after the
22
+ * states were computed, a correction-threshold decision within its bound, a non-finite output) are recomputed with the
23
+ * reference, so they are exact. Every stage's wall-clock time is reported (`stageMilliseconds`); on a software adapter it
24
+ * is not GPU performance.
25
+ */
26
+ import { type DisplayViewRenderResult, type DisplayViewSetup } from "../facade/display-view.js";
27
+ import { type DisplayViewExpectedSignalInput } from "../facade/expected-signal.js";
28
+ import type { DisplayViewGpuSetup } from "../facade/display-view.js";
29
+ import { type DisplayViewMaterialModules } from "./display-view-composite-material-module.js";
30
+ import { type DisplayViewGpuConformanceCase, type DisplayViewGpuRenderer, type DisplayViewGpuRendererFactory, type DisplayViewGpuRequest } from "../facade/display-view-gpu.js";
31
+ import { type DisplayViewGpuMaterialModule } from "./display-view-speed-gpu.js";
32
+ import { type ImageKernelExecution, type ImageKernelRelativeErrorBound } from "./image-kernel-execution.js";
33
+ import { type WebGpuRuntime } from "./webgpu-runtime.js";
34
+ type PipelineSource = (label: string, code: string) => Promise<GPUComputePipeline>;
35
+ type YieldControl = ((signal: AbortSignal) => Promise<void>) | undefined;
36
+ /**
37
+ * Largest site-kernel radius the gather takes: the diffraction cap (32 sites) plus the defocus disc's (64 sites), the largest
38
+ * combined radius the reference builds (#492; `buildExpectedSignalInput`).
39
+ */
40
+ export declare const DISPLAY_VIEW_QUALITY_MAXIMUM_KERNEL_RADIUS = 96;
41
+ /**
42
+ * Default largest number of taps one site-pass dispatch gathers (sites × taps per site), so that a band of a large defocus
43
+ * kernel (up to 150 544 taps per site at radius 96) is split over several dispatches instead of one long one that an operating
44
+ * system's GPU watchdog could reset (Windows' default TDR delay is 2 s). A choice, not a measurement: 2³⁰ taps take 0.1 s at an
45
+ * assumed 10¹⁰ taps per second; at radius 94 it leaves about 7 100 sites (111 workgroups) per dispatch, fewer than a large GPU
46
+ * runs at once. In focus a band stays one dispatch (400 taps per site at f/8). The hardware run of #475 records the site pass's
47
+ * time and dispatch count (`stageMilliseconds.sites`, `siteDispatches`), which decide whether the budget or a gather split over
48
+ * several invocations per site is needed.
49
+ */
50
+ export declare const DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH: number;
51
+ /** What a GPU render of a Quality display view needs: the reference's own setup and expected-signal input, plus the material module. */
52
+ export interface DisplayViewQualityGpuInput {
53
+ setup: DisplayViewSetup;
54
+ signalInput: DisplayViewExpectedSignalInput;
55
+ material: DisplayViewGpuMaterialModule;
56
+ /** Recompute flagged display pixels with the reference. Default true. */
57
+ recomputeFlagged?: boolean;
58
+ /** The states this renderer holds for the setup's correction key (grown by the render); a fresh store when absent. */
59
+ states?: CorrectionStateStore;
60
+ /**
61
+ * #492: sum each site's taps in blocks (rows, groups; `DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL`), whose bound grows with the
62
+ * kernel's width rather than its tap count. Default: when the setup's expected signal carries the defocus disc
63
+ * (`setup.gpu.defocusRadius` > 0); in focus the tap-list pass runs as before.
64
+ */
65
+ blockedSum?: boolean;
66
+ /** Largest taps (sites × taps per site) per site-pass dispatch; default `DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH`. */
67
+ maximumSiteTapsPerDispatch?: number;
68
+ }
69
+ /** Wall-clock milliseconds per stage of one render (upload, dispatch and readback of each pass; host work apart). */
70
+ export interface DisplayViewQualityStageTimings {
71
+ /** Pass 1: the lattice materials (the material module per lattice point). */
72
+ lattice: number;
73
+ /** Pass 2: the site electrons through the combined kernels, the readout moments and the white clip. */
74
+ sites: number;
75
+ /** Pass 3: the display pixels (demosaic candidates, development, covariance, correction), every dispatch of it. */
76
+ pixels: number;
77
+ /** The low-signal correction states computed on the host (the reference's own draws) and the table uploads. */
78
+ states: number;
79
+ /** The host finishing (quantization term, noise draw, Cholesky, encode) of every display pixel. */
80
+ finishing: number;
81
+ /** The reference's recomputation of flagged display pixels. */
82
+ recompute: number;
83
+ /** Pass 3 dispatches that were repeated because a correction state was missing. */
84
+ pixelRedispatches: number;
85
+ /** Pass 2 dispatches (one per band, or more where a band's sites × taps exceed the tap budget per dispatch, #492). */
86
+ siteDispatches: number;
87
+ }
88
+ /** A GPU Quality display view of one region: the reference's result fields plus the execution label, the bounds, the flags and the timings. */
89
+ export interface DisplayViewQualityGpuResult extends DisplayViewRenderResult {
90
+ execution: ImageKernelExecution;
91
+ /** |E_gpu − E_cpu| ≤ relative·|E_cpu| + absolute for every component of every display pixel (linear light); `absolute` is the largest per-pixel term. */
92
+ expectedValueBound: ImageKernelRelativeErrorBound;
93
+ /** |V_gpu − V_cpu| ≤ relative·V_cpu + absolute for every covariance entry (linear light squared). */
94
+ varianceBound: ImageKernelRelativeErrorBound;
95
+ /** Per display pixel: the absolute terms of the two bounds (the kernel's running bounds, doubled). */
96
+ expectedValueAbsoluteBound: Float32Array;
97
+ varianceAbsoluteBound: Float32Array;
98
+ /** Display pixels (region row-major) the kernel flagged, and those the host recomputed with the reference. */
99
+ flaggedPixelIndices: Uint32Array;
100
+ hostRecomputedPixelIndices: Uint32Array;
101
+ /** The kernel's flag word per display pixel (`DISPLAY_VIEW_QUALITY_FLAG` bits). */
102
+ pixelFlags: Uint32Array;
103
+ /** Correction states the render computed on the host (new to the store), and the states the table held at the end. */
104
+ statesComputed: number;
105
+ statesHeld: number;
106
+ /** Taps per native site of the expected-signal gather (over every lattice group). */
107
+ tapsPerSite: number;
108
+ /** #492: whether the site pass summed in blocks (the defocus disc), and the relative bound of a site's electrons it used. */
109
+ blockedSum: boolean;
110
+ siteSumRelativeBound: number;
111
+ /**
112
+ * #492, blocked sum only: lattice points the material module flagged as uncertain that the host resolved with the reference's
113
+ * own float64 `materialAt` at the reference's lattice point (so no site of a defocused render carries material uncertainty).
114
+ */
115
+ latticePointsResolved: number;
116
+ stageMilliseconds: DisplayViewQualityStageTimings;
117
+ }
118
+ /**
119
+ * The low-signal correction states a renderer holds for one correction key, as the GPU table reads them: grid keys ascending
120
+ * with three float32 values each (the host keeps the float64 values; the table is their single rounding).
121
+ */
122
+ export declare class CorrectionStateStore {
123
+ #private;
124
+ get size(): number;
125
+ has(key: number): boolean;
126
+ add(gridKeys: Int32Array, values: Float64Array): void;
127
+ /** The sorted table (rebuilt when states were added since the last call). */
128
+ table(): {
129
+ keys: Int32Array<ArrayBuffer>;
130
+ values: Float32Array<ArrayBuffer>;
131
+ };
132
+ }
133
+ /**
134
+ * Why the Quality kernels do not cover a request, or null when they do: chart materials, the exact area mean (footprints of
135
+ * at most 25 real pixels and 6 per axis), the identity or positive Reinhard tone curve, demosaic candidates within 3 sites
136
+ * (1 or 2 per phase), at most 64 lattice groups, lattice constants exact in float32, no collect pass, and a raster at least
137
+ * 2·reach + 2 sites on each axis (so a reflected border site lies in the band's window).
138
+ */
139
+ export declare function displayViewQualityGpuUnsupportedReason(setup: DisplayViewSetup, signalInput: DisplayViewExpectedSignalInput): string | null;
140
+ /** Renders one region of a Quality display view on the GPU (see the module comment). */
141
+ export declare function renderDisplayViewQualityOnGpu(runtime: WebGpuRuntime, bandBytes: number, input: DisplayViewQualityGpuInput, abort: AbortController, yieldControl: YieldControl, pipelines: PipelineSource): Promise<DisplayViewQualityGpuResult>;
142
+ /**
143
+ * The GPU setup the material modules read for a Quality render (#491's `createDisplayViewMaterialModules`). The composite module
144
+ * recovers the exact lattice point from the kernel's float32 point with the request's query offsets and the scale of its own
145
+ * query points. With diffraction kernels the reference has no query points: it evaluates each lattice group's points with the
146
+ * lattice formula (p + 0.5 + f − W/2)·g, −(q + 0.5 + f − H/2)·g (`createDisplayViewExpectedSignal`). The lattice pass forms
147
+ * exactly that point per group, so the module is given the groups as its offsets and that formula as the query points; the
148
+ * module's own checks (the model at the frame's corners within 2⁻⁴⁸, the offsets' separation, at most 16 offsets) then apply to
149
+ * them as to a Speed lattice. Without diffraction the request's own offsets and query points are the lattice already (each
150
+ * group member's point is checked to be its offset's, `displayViewQualityGpuUnsupportedReason`).
151
+ */
152
+ export declare function qualityMaterialSetup(gpu: DisplayViewGpuSetup): DisplayViewGpuSetup;
153
+ /** The material module of a Quality render's scene (see `qualityMaterialSetup`). */
154
+ export declare function qualityMaterialModule(modules: DisplayViewMaterialModules, gpu: DisplayViewGpuSetup, signal: AbortSignal): Promise<DisplayViewGpuMaterialModule>;
155
+ /**
156
+ * Why the Quality renderer does not support a prepared request (`DisplayViewGpuRenderer.supports`), or true: engine charts and
157
+ * declared `material-layers` scenes (the material modules of `createDisplayViewMaterialModules`, #491), the identity or Reinhard
158
+ * tone curve and at most 64 query offsets (lattice groups). `lens.defocus` out of focus is supported (#492 step 2c: the site pass
159
+ * gathers the disc, or each node's diffraction kernel convolved with it, up to the reference's own caps of radius 64 and 96);
160
+ * whether a view's footprints take the exact area mean, whether the device passes the composite probe, and whether the kernel
161
+ * table fits the device's storage binding limit, is known only per render.
162
+ */
163
+ export declare function displayViewQualityGpuSupports(request: DisplayViewGpuRequest): true | string;
164
+ /** #479's self-check (see `runQualityConformance`). */
165
+ export declare const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE: DisplayViewGpuConformanceCase;
166
+ /** #492's self-check of the defocus disc's blocked site sum (see `runQualityConformance`). */
167
+ export declare const DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE: DisplayViewGpuConformanceCase;
168
+ /**
169
+ * The display view's GPU renderer on one runtime (#481's `DisplayViewGpuRenderer`): the Speed kernel for requests it covers
170
+ * (no diffraction, no AHD, no defocus disc, at most 16 offsets) and the Quality kernels for the rest (#492: a defocused Speed
171
+ * request too, with its bilinear demosaic). One pipeline cache per device, one set of
172
+ * material modules (#491: chart presets, composite charts, the 2.3 elements and material-layers scenes; `dispose` releases their
173
+ * uploaded scene records), one correction-state store per correction key. `render` renders the part's region (`setup.gpu`
174
+ * carries the expected signal's input and the chart; a setup without it, a coarse view or a frame the kernels do not cover
175
+ * throws `unsupported-on-backend`), and throws `aborted` and `device-lost` as the accelerator's operations do.
176
+ */
177
+ export declare function createDisplayViewGpuRenderer(runtime: WebGpuRuntime, options?: {
178
+ maximumBandBytes?: number;
179
+ yieldControl?: YieldControl;
180
+ }): DisplayViewGpuRenderer;
181
+ /** The factory `prepareDisplayView` creates the renderer with (one per device): the Speed and Quality kernels behind one renderer. */
182
+ export declare const displayViewGpuRendererFactory: DisplayViewGpuRendererFactory;
183
+ /** Registers the combined renderer as the display view's GPU renderer and returns the factory registered before. */
184
+ export declare function registerDisplayViewGpuRenderer(): DisplayViewGpuRendererFactory | null;
185
+ export {};