@photivra/engine 2.21.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +118 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +11 -0
  6. package/dist/capture/c2pa-embed.d.ts +116 -0
  7. package/dist/capture/c2pa-embed.js +493 -0
  8. package/dist/capture/c2pa-embed.js.map +1 -0
  9. package/dist/capture/dng-import.d.ts +73 -8
  10. package/dist/capture/dng-import.js +315 -80
  11. package/dist/capture/dng-import.js.map +1 -1
  12. package/dist/capture/dng-opcodes.d.ts +96 -0
  13. package/dist/capture/dng-opcodes.js +391 -0
  14. package/dist/capture/dng-opcodes.js.map +1 -0
  15. package/dist/capture/dng-profile-maps.d.ts +99 -0
  16. package/dist/capture/dng-profile-maps.js +381 -0
  17. package/dist/capture/dng-profile-maps.js.map +1 -0
  18. package/dist/capture/dng-stage-image.d.ts +23 -0
  19. package/dist/capture/dng-stage-image.js +65 -0
  20. package/dist/capture/dng-stage-image.js.map +1 -0
  21. package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
  22. package/dist/capture/jpeg-xl-tiles.js +151 -0
  23. package/dist/capture/jpeg-xl-tiles.js.map +1 -0
  24. package/dist/capture/lossy-jpeg.d.ts +17 -0
  25. package/dist/capture/lossy-jpeg.js +369 -0
  26. package/dist/capture/lossy-jpeg.js.map +1 -0
  27. package/dist/capture/native-raw-development.d.ts +2 -1
  28. package/dist/capture/native-raw-development.js.map +1 -1
  29. package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
  30. package/dist/color/neighbor-highlight-reconstruction.js +234 -0
  31. package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
  32. package/dist/color/sensor-color-development.d.ts +5 -0
  33. package/dist/color/sensor-color-development.js +7 -1
  34. package/dist/color/sensor-color-development.js.map +1 -1
  35. package/dist/color/spectral-spikiness.d.ts +11 -0
  36. package/dist/color/spectral-spikiness.js +23 -0
  37. package/dist/color/spectral-spikiness.js.map +1 -0
  38. package/dist/core/execution-error.d.ts +21 -0
  39. package/dist/core/execution-error.js +12 -0
  40. package/dist/core/execution-error.js.map +1 -0
  41. package/dist/core/version.d.ts +1 -1
  42. package/dist/core/version.js +1 -1
  43. package/dist/core/version.js.map +1 -1
  44. package/dist/facade/catalogue-lens-signal.d.ts +26 -1
  45. package/dist/facade/catalogue-lens-signal.js +25 -7
  46. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  47. package/dist/facade/diffraction-provider.d.ts +1 -1
  48. package/dist/facade/diffraction-provider.js +4 -4
  49. package/dist/facade/diffraction-provider.js.map +1 -1
  50. package/dist/facade/display-view-gpu-session.js +38 -6
  51. package/dist/facade/display-view-gpu-session.js.map +1 -1
  52. package/dist/facade/display-view-gpu.d.ts +13 -5
  53. package/dist/facade/display-view-gpu.js.map +1 -1
  54. package/dist/facade/display-view-normal.d.ts +15 -0
  55. package/dist/facade/display-view-normal.js +44 -0
  56. package/dist/facade/display-view-normal.js.map +1 -0
  57. package/dist/facade/display-view-rendering.d.ts +97 -0
  58. package/dist/facade/display-view-rendering.js +598 -0
  59. package/dist/facade/display-view-rendering.js.map +1 -0
  60. package/dist/facade/display-view.d.ts +37 -65
  61. package/dist/facade/display-view.js +147 -476
  62. package/dist/facade/display-view.js.map +1 -1
  63. package/dist/facade/expected-signal-setup.js +2 -2
  64. package/dist/facade/expected-signal-setup.js.map +1 -1
  65. package/dist/facade/fast-methods.d.ts +1 -1
  66. package/dist/facade/fast-methods.js +37 -8
  67. package/dist/facade/fast-methods.js.map +1 -1
  68. package/dist/facade/generic-lens-capture.d.ts +1 -1
  69. package/dist/facade/generic-lens-capture.js +1 -1
  70. package/dist/facade/generic-lens-capture.js.map +1 -1
  71. package/dist/facade/image-plane.js +41 -10
  72. package/dist/facade/image-plane.js.map +1 -1
  73. package/dist/facade/on-demand-kits.d.ts +14 -0
  74. package/dist/facade/on-demand-kits.js +36 -0
  75. package/dist/facade/on-demand-kits.js.map +1 -0
  76. package/dist/facade/opt-in-kit.d.ts +9 -0
  77. package/dist/facade/opt-in-kit.js +11 -0
  78. package/dist/facade/opt-in-kit.js.map +1 -0
  79. package/dist/facade/quality-kit.d.ts +21 -0
  80. package/dist/facade/quality-kit.js +23 -0
  81. package/dist/facade/quality-kit.js.map +1 -0
  82. package/dist/facade/quality-paths.d.ts +22 -0
  83. package/dist/facade/quality-paths.js +44 -0
  84. package/dist/facade/quality-paths.js.map +1 -0
  85. package/dist/facade/scene-radiance.d.ts +18 -2
  86. package/dist/facade/scene-radiance.js +6 -4
  87. package/dist/facade/scene-radiance.js.map +1 -1
  88. package/dist/facade/simulate-capture.d.ts +32 -13
  89. package/dist/facade/simulate-capture.js +162 -69
  90. package/dist/facade/simulate-capture.js.map +1 -1
  91. package/dist/facade/simulate-display-view.d.ts +10 -12
  92. package/dist/facade/simulate-display-view.js +57 -48
  93. package/dist/facade/simulate-display-view.js.map +1 -1
  94. package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
  95. package/dist/gpu/display-view-gpu-renderer.js +134 -0
  96. package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
  97. package/dist/gpu/display-view-image-material-module.d.ts +3 -2
  98. package/dist/gpu/display-view-image-material-module.js +25 -3
  99. package/dist/gpu/display-view-image-material-module.js.map +1 -1
  100. package/dist/gpu/display-view-image-quality.d.ts +43 -1
  101. package/dist/gpu/display-view-image-quality.js +256 -11
  102. package/dist/gpu/display-view-image-quality.js.map +1 -1
  103. package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
  104. package/dist/gpu/display-view-quality-gpu.js +60 -249
  105. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  106. package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
  107. package/dist/gpu/display-view-speed-gpu.js +50 -12
  108. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  109. package/dist/gpu/image-kernel-accelerator.js +3 -0
  110. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  111. package/dist/gpu/image-kernel-execution.d.ts +2 -13
  112. package/dist/gpu/image-kernel-execution.js +2 -10
  113. package/dist/gpu/image-kernel-execution.js.map +1 -1
  114. package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
  115. package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
  116. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  117. package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
  118. package/dist/gpu/wgsl-display-view-coupled.js +353 -0
  119. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  120. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -9
  121. package/dist/gpu/wgsl-display-view-quality-kernel.js +109 -111
  122. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  123. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
  124. package/dist/gpu/wgsl-display-view-speed-kernel.js +71 -8
  125. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  126. package/dist/index.d.ts +11 -3
  127. package/dist/index.js +9 -1
  128. package/dist/index.js.map +1 -1
  129. package/dist/output/camera-channel-response.d.ts +50 -0
  130. package/dist/output/camera-channel-response.js +69 -0
  131. package/dist/output/camera-channel-response.js.map +1 -0
  132. package/dist/output/native-capture-sdr.js +5 -2
  133. package/dist/output/native-capture-sdr.js.map +1 -1
  134. package/dist/output/sdr-rendering.d.ts +18 -1
  135. package/dist/output/sdr-rendering.js +39 -2
  136. package/dist/output/sdr-rendering.js.map +1 -1
  137. package/dist/sensor/native-sensor-report.d.ts +10 -0
  138. package/dist/sensor/native-sensor-report.js +16 -0
  139. package/dist/sensor/native-sensor-report.js.map +1 -0
  140. package/dist/sensor/sensor-characteristics.d.ts +81 -13
  141. package/dist/sensor/sensor-characteristics.js +10 -6
  142. package/dist/sensor/sensor-characteristics.js.map +1 -1
  143. package/docs/API_REFERENCE.md +117 -4
  144. package/docs/QUICK_START.md +37 -8
  145. package/docs/USAGE.md +104 -22
  146. package/docs/api/capture-c2pa-embed.md +177 -0
  147. package/docs/api/capture-dng-import.md +86 -10
  148. package/docs/api/capture-dng-opcodes.md +93 -0
  149. package/docs/api/capture-dng-profile-maps.md +141 -0
  150. package/docs/api/capture-dng-stage-image.md +43 -0
  151. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  152. package/docs/api/capture-lossy-jpeg.md +41 -0
  153. package/docs/api/capture-native-raw-development.md +2 -1
  154. package/docs/api/core-execution-error.md +43 -0
  155. package/docs/api/core-version.md +1 -1
  156. package/docs/api/exports.json +255 -5
  157. package/docs/api/facade-display-view-gpu.md +5 -2
  158. package/docs/api/facade-display-view.md +11 -15
  159. package/docs/api/facade-fast-methods.md +38 -9
  160. package/docs/api/facade-quality-paths.md +24 -0
  161. package/docs/api/facade-scene-radiance.md +1 -1
  162. package/docs/api/facade-simulate-capture.md +24 -7
  163. package/docs/api/facade-simulate-display-view.md +10 -12
  164. package/docs/api/gpu-image-kernel-execution.md +0 -36
  165. package/docs/api/output-camera-channel-response.md +55 -0
  166. package/docs/api/output-sdr-rendering.md +6 -1
  167. package/docs/api/sensor-sensor-characteristics.md +54 -2
  168. package/llms.txt +1 -0
  169. package/package.json +6 -5
@@ -1,18 +1,4 @@
1
- /**
2
- * Host side of the display view's Speed path on the GPU (#478): the support check, the float64 tables (site table,
3
- * overlaps over the scale, axis sums over the squared scale, 1/Σw²), banded dispatch of
4
- * `DISPLAY_VIEW_SPEED_KERNEL_WGSL`, and the finishing the CPU reference does after its per-sample loop: the output
5
- * quantization term, the seeded noise draw, the Cholesky factor, the clipped-normal centre, the sRGB encode and the
6
- * rounding, all with the reference's own float64 functions. So the noise draw is bit-identical to the CPU's, and only
7
- * the expected value and the covariance carry the float32 bound.
8
- *
9
- * The float64 reference is `renderDisplayView` (facade/display-view.ts) on the exact-area-mean path without
10
- * diffraction, with the bilinear demosaic and chart materials. Internal: not exported from the package. The
11
- * material lookup per query point is a module the caller supplies (`DisplayViewGpuMaterialModule`, the seam of
12
- * #477); `display-view-chart-material-module.ts` provides one for the eight 2.0 chart presets and
13
- * `display-view-composite-material-module.ts` (#491) one for composite charts, the 2.3 elements and `material-layers` scenes.
14
- */
15
- import { type DisplayViewRenderResult, type DisplayViewSetup } from "../facade/display-view.js";
1
+ import { type DisplayViewKernelTap, type DisplayViewRenderResult, type DisplayViewSetup } from "../facade/display-view.js";
16
2
  import { type DisplayViewExpectedSignalInput } from "../facade/expected-signal.js";
17
3
  import { resolvePortableChartScene } from "../facade/chart-scenes.js";
18
4
  import { type DisplayViewGpuConformanceCase, type DisplayViewGpuRenderer, type DisplayViewGpuRendererFactory, type DisplayViewGpuRequest } from "../facade/display-view-gpu.js";
@@ -90,7 +76,7 @@ export interface DisplayViewSpeedGpuResult extends DisplayViewRenderResult {
90
76
  /**
91
77
  * Why the kernel does not cover a request, or null when it does. The kernel is the Speed path: the exact area mean
92
78
  * (views where a footprint holds at most 25 real pixels and at most K², and here at most 6 per axis), no diffraction
93
- * kernels, chart materials, the bilinear demosaic (no AHD candidates or correction), the identity or positive Reinhard
79
+ * kernels, chart materials, the bilinear demosaic (no AHD candidates), the identity or positive Reinhard
94
80
  * tone curve, and at most 16 query offsets.
95
81
  */
96
82
  export declare function displayViewSpeedGpuUnsupportedReason(setup: DisplayViewSetup, signalInput: DisplayViewExpectedSignalInput): string | null;
@@ -203,4 +189,18 @@ export declare const displayViewSpeedGpuRendererFactory: DisplayViewGpuRendererF
203
189
  * registered before. Nothing registers it on import: the integration of #481 decides where the engine registers it.
204
190
  */
205
191
  export declare function registerDisplayViewSpeedGpuRenderer(): DisplayViewGpuRendererFactory | null;
192
+ /** #564: the GPU refusal of a defocus kernel above the direct radius (the CPU convolves it by runs). */
193
+ export declare const LARGE_DEFOCUS_REASON = "a defocus kernel radius above 64 sites (convolved by runs on the CPU, #564) is not on the GPU";
194
+ /** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
195
+ export declare function footprintShape(setup: DisplayViewSetup): {
196
+ sx: number;
197
+ sy: number;
198
+ exact: boolean;
199
+ maximumColumns: number;
200
+ maximumRows: number;
201
+ };
202
+ /** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
203
+ export declare function offsetCount(request: DisplayViewGpuRequest): number;
204
+ /** The bilinear RGGB demosaic kernels per phase (phaseY·2 + phaseX) and channel, as `bilinearPhaseProfiles` gives them. */
205
+ export declare function bilinearKernels(): DisplayViewKernelTap[][][];
206
206
  export {};
@@ -13,7 +13,8 @@
13
13
  * #477); `display-view-chart-material-module.ts` provides one for the eight 2.0 chart presets and
14
14
  * `display-view-composite-material-module.ts` (#491) one for composite charts, the 2.3 elements and `material-layers` scenes.
15
15
  */
16
- import { DISPLAY_VIEW_NOISE_STREAM_ID, axisSums, clipCentre, createDisplayViewExpectedSignal, displayViewDecidedSaturation, overlaps, quantizationVariance, renderDisplayView } from "../facade/display-view.js";
16
+ import { SDR_LUMINANCE_WEIGHTS } from "../output/sdr-rendering.js";
17
+ import { DISPLAY_VIEW_NOISE_STREAM_ID, EXACT_AREA_MEAN_MAX_PIXELS, axisSums, clipCentre, createDisplayViewExpectedSignal, displayViewDecidedSaturation, overlaps, quantizationVariance, renderDisplayView } from "../facade/display-view.js";
17
18
  import { displayViewSignalTables } from "../facade/expected-signal.js";
18
19
  import { resolvePortableChartScene } from "../facade/chart-scenes.js";
19
20
  import { setDisplayViewGpuRendererFactory } from "../facade/display-view-gpu.js";
@@ -25,7 +26,6 @@ import { createHostYieldGate } from "../capture/host-yield-cadence.js";
25
26
  import { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF, webGpuExecution } from "./image-kernel-execution.js";
26
27
  import { BUFFER_USAGE, GpuResourceScope, abortedError, createComputePipeline, linearDispatch, runComputePass, throwIfAborted as checkAbort } from "./webgpu-runtime.js";
27
28
  import { DISPLAY_VIEW_AUX, DISPLAY_VIEW_FLAG_WORDS, DISPLAY_VIEW_SPEED_MAXIMUM_NODES, DISPLAY_VIEW_GEOMETRY_STRIDE, DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS, DISPLAY_VIEW_GPU_MAXIMUM_PIXELS_PER_AXIS, DISPLAY_VIEW_OUTPUT_FLOATS, DISPLAY_VIEW_PARAM_WORDS, DISPLAY_VIEW_REINHARD_MEAN_U, DISPLAY_VIEW_REINHARD_VARIANCE_U, DISPLAY_VIEW_WORKGROUP_SIZE, assembleDisplayViewSpeedWgsl } from "./wgsl-display-view-speed-kernel.js";
28
- import { footprintShape, bilinearKernels, offsetCount } from "./display-view-quality-gpu.js";
29
29
  /** One WGSL rounding of + − × in either direction (W3C WGSL "Floating Point Accuracy"; #471, #487): twice the binary32 unit roundoff. */
30
30
  const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
31
31
  /** Higham's γ_k with E in place of u. */
@@ -53,7 +53,7 @@ const BYTES_PER_PIXEL = 4 * DISPLAY_VIEW_OUTPUT_FLOATS + 4 * DISPLAY_VIEW_FLAG_W
53
53
  /**
54
54
  * Why the kernel does not cover a request, or null when it does. The kernel is the Speed path: the exact area mean
55
55
  * (views where a footprint holds at most 25 real pixels and at most K², and here at most 6 per axis), no diffraction
56
- * kernels, chart materials, the bilinear demosaic (no AHD candidates or correction), the identity or positive Reinhard
56
+ * kernels, chart materials, the bilinear demosaic (no AHD candidates), the identity or positive Reinhard
57
57
  * tone curve, and at most 16 query offsets.
58
58
  */
59
59
  export function displayViewSpeedGpuUnsupportedReason(setup, signalInput) {
@@ -65,10 +65,14 @@ export function displayViewSpeedGpuUnsupportedReason(setup, signalInput) {
65
65
  return "radiance scenes (caller functions) are not on the GPU";
66
66
  if (!signalInput.queryPoint)
67
67
  return "the expected signal has no query points";
68
- if (setup.candidates || setup.ahdCorrection || setup.ahdCollect)
69
- return "AHD candidates and the low-signal correction are not on the GPU";
70
- if (setup.toneCurve !== "identity" && setup.toneCurve !== "positive-reinhard-per-channel")
68
+ if (setup.candidates || setup.ahdChoice)
69
+ return "AHD candidates are not on the GPU";
70
+ if (!GPU_TONE_CURVES.includes(setup.toneCurve))
71
71
  return "unsupported tone curve " + String(setup.toneCurve);
72
+ if (setup.rendering && !setup.coupled)
73
+ return "the coupled rendering steps were not prepared";
74
+ if (setup.rendering?.highlightMode === "reconstruct-neighbors")
75
+ return "highlightMode reconstruct-neighbors (#602; its regions' ratios are found on the host) is not on the GPU in 2.22.0";
72
76
  if (signalInput.offsets.length < 1 || signalInput.offsets.length > DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS)
73
77
  return "the kernel holds 1 to " + DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS + " query offsets";
74
78
  if (signalInput.wavelengthsNm.length < 1 || signalInput.wavelengthsNm.length > DISPLAY_VIEW_SPEED_MAXIMUM_NODES)
@@ -168,6 +172,8 @@ export function axisRecord(geometry, at, start, end, limit, margin, scale) {
168
172
  const order = ["bEven", "bOdd", "ppEven", "ppOdd", "qqEven", "qqOdd", "pqEven", "pqOdd", "ww"];
169
173
  order.forEach((key, i) => { geometry[at + 8 + i] = sums[key] / scale2; });
170
174
  }
175
+ /** The kernel's tone code: identity 0, positive Reinhard 1, extended Reinhard (luminance) 2, Naka–Rushton (luminance) 3. */
176
+ const TONE_CODE = { "identity": 0, "positive-reinhard-per-channel": 1, "extended-reinhard-luminance": 2, "naka-rushton-luminance": 3 };
171
177
  /** The uniform parameters and the aux table of a render. */
172
178
  function constants(setup, signalInput) {
173
179
  const params = new ArrayBuffer(4 * DISPLAY_VIEW_PARAM_WORDS), words = new Uint32Array(params), floats = new Float32Array(params);
@@ -178,7 +184,7 @@ function constants(setup, signalInput) {
178
184
  words[6] = setup.nativeWidth;
179
185
  words[7] = setup.nativeHeight;
180
186
  words[8] = setup.region.width;
181
- words[9] = setup.toneCurve === "positive-reinhard-per-channel" ? 1 : 0;
187
+ words[9] = TONE_CODE[setup.toneCurve];
182
188
  words[10] = signalInput.offsets.length;
183
189
  words[11] = signalInput.wavelengthsNm.length;
184
190
  for (let c = 0; c < 3; c++) {
@@ -298,12 +304,19 @@ export async function renderDisplayViewSpeedOnGpu(runtime, bandBytes, input, abo
298
304
  const rows = Math.max(1, Math.min(region.height, Math.floor(bandBytes / (region.width * BYTES_PER_PIXEL)), material.bandRows?.(shape.sy) ?? Infinity));
299
305
  if (Math.max(rows * region.width * 4 * DISPLAY_VIEW_OUTPUT_FLOATS, geometry.byteLength, table.byteLength) > runtime.maximumStorageBindingBytes)
300
306
  throw new ImageKernelExecutionError("insufficient-limits", "One display-view band exceeds the device's storage binding limit.");
301
- const pipeline = await pipelines("photivra-display-view-speed:" + material.id, assembleDisplayViewSpeedWgsl(material.wgsl));
307
+ // #593, #601: the coupled steps' shader text and constants, loaded on demand for a view that asks for them.
308
+ const coupled = setup.rendering ? await import("./wgsl-display-view-coupled.js") : null;
309
+ const coupledConstants = coupled ? coupled.displayViewCoupledConstants(setup, SDR_LUMINANCE_WEIGHTS, gaussLegendre16()) : new Float32Array(1);
310
+ const pipeline = await pipelines("photivra-display-view-speed:" + material.id + (coupled ? "+coupled" : ""), assembleDisplayViewSpeedWgsl(material.wgsl, coupled?.DISPLAY_VIEW_COUPLED_WGSL));
302
311
  const moments = new Float32Array(pixelCount * DISPLAY_VIEW_OUTPUT_FLOATS), flagged = [], flagWords = new Uint32Array(pixelCount * DISPLAY_VIEW_FLAG_WORDS);
303
312
  const words = new Uint32Array(params);
304
313
  const scope = new GpuResourceScope(runtime);
305
314
  try {
306
- const auxBuffer = scope.upload("photivra-display-view-aux", aux, BUFFER_USAGE.STORAGE);
315
+ // #593, #601: the coupled steps' constants follow the aux table's own entries (one zero word without them).
316
+ const auxWithCoupled = new Float32Array(aux.length + coupledConstants.length);
317
+ auxWithCoupled.set(aux);
318
+ auxWithCoupled.set(coupledConstants, aux.length);
319
+ const auxBuffer = scope.upload("photivra-display-view-aux", auxWithCoupled, BUFFER_USAGE.STORAGE);
307
320
  const geometryBuffer = scope.upload("photivra-display-view-geometry", geometry, BUFFER_USAGE.STORAGE);
308
321
  const tableBuffer = scope.upload("photivra-display-view-table", table, BUFFER_USAGE.STORAGE);
309
322
  const materialGroup = material.band ? null : runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(1), entries: material.bindings.map((b) => ({ binding: b.binding,
@@ -409,7 +422,7 @@ export async function renderDisplayViewSpeedOnGpu(runtime, bandBytes, input, abo
409
422
  saturatedPixelCount += saturated[i];
410
423
  const expectedValueBound = { kind: "relative-to-reference", relative: relative.mean, absolute: largestMean };
411
424
  const uncertain = flagged.length - recomputed.length + undecided.length - highlights.length;
412
- return { samples, expected, noiseStd, sampleCount, siteCount, detailPixelCount: pixelCount, uniformPixelCount: 0, ahdCorrectedPixelCount: 0, estimation: "exact-area-mean", saturated, saturatedPixelCount,
425
+ return { samples, expected, noiseStd, sampleCount, siteCount, detailPixelCount: pixelCount, uniformPixelCount: 0, estimation: "exact-area-mean", saturated, saturatedPixelCount,
413
426
  execution: webGpuExecution(expectedValueBound, uncertain), expectedValueBound,
414
427
  varianceBound: { kind: "relative-to-reference", relative: relative.variance, absolute: largestVariance },
415
428
  expectedValueAbsoluteBound: meanBound, varianceAbsoluteBound: varianceBound,
@@ -434,12 +447,15 @@ export function displayViewSpeedGpuSupports(request) {
434
447
  export function sceneToneAndOffsetsReason(request, maximumOffsets) {
435
448
  if (request.scene !== "chart" && request.scene !== "material-layers" && request.scene !== "image-plane")
436
449
  return "the scene is a caller " + request.scene + "; the GPU material lookup covers engine charts and declared material-layers scenes (describe the scene as material-layers)";
437
- if (request.toneCurve !== "identity" && request.toneCurve !== "positive-reinhard-per-channel")
450
+ // #593: the identity, Reinhard and the two luminance tone curves (the luminance curves through the coupled steps).
451
+ if (!GPU_TONE_CURVES.includes(request.toneCurve))
438
452
  return "unsupported tone curve " + String(request.toneCurve);
439
453
  if (offsetCount(request) > maximumOffsets)
440
454
  return "more than " + maximumOffsets + " query offsets per site";
441
455
  return true;
442
456
  }
457
+ /** The tone curves the GPU kernels render (#593 adds the luminance curves). */
458
+ const GPU_TONE_CURVES = ["identity", "positive-reinhard-per-channel", "extended-reinhard-luminance", "naka-rushton-luminance"];
443
459
  /**
444
460
  * A self-check's verdict: every component of a GPU render inside its a-priori bounds against the reference, every sample within
445
461
  * one code, and (#533) the saturated-pixel count the reference's bit for bit; true, or a sentence saying where it failed.
@@ -548,7 +564,7 @@ export function createDisplayViewSpeedGpuRenderer(runtime, options = {}) {
548
564
  const material = await modules.moduleFor(setup.gpu, abort.signal);
549
565
  const result = await renderDisplayViewSpeedOnGpu(runtime, bandBytes, { setup, signalInput: setup.gpu.signalInput, material }, abort, options.yieldControl, source);
550
566
  return { samples: result.samples, expected: result.expected, noiseStd: result.noiseStd, sampleCount: result.sampleCount, siteCount: result.siteCount, detailPixelCount: result.detailPixelCount,
551
- uniformPixelCount: result.uniformPixelCount, ahdCorrectedPixelCount: result.ahdCorrectedPixelCount, estimation: result.estimation, saturated: result.saturated,
567
+ uniformPixelCount: result.uniformPixelCount, estimation: result.estimation, saturated: result.saturated,
552
568
  saturatedPixelCount: result.saturatedPixelCount, errorBound: result.expectedValueBound,
553
569
  diagnosticUncertaintySampleCount: result.execution.diagnosticUncertaintySampleCount, ...(material.compositeProbe ? { compositeProbe: material.compositeProbe } : {}) };
554
570
  }
@@ -568,4 +584,26 @@ export const displayViewSpeedGpuRendererFactory = (handle) => createDisplayViewS
568
584
  export function registerDisplayViewSpeedGpuRenderer() {
569
585
  return setDisplayViewGpuRendererFactory(displayViewSpeedGpuRendererFactory);
570
586
  }
587
+ // #594: shared with the on-demand Quality host (display-view-quality-gpu.ts), so the Speed path does not import it.
588
+ /** #564: the GPU refusal of a defocus kernel above the direct radius (the CPU convolves it by runs). */
589
+ export const LARGE_DEFOCUS_REASON = "a defocus kernel radius above 64 sites (convolved by runs on the CPU, #564) is not on the GPU";
590
+ /** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
591
+ export function footprintShape(setup) {
592
+ const sx = setup.frameWidth / setup.viewWidth, sy = setup.frameHeight / setup.viewHeight, area = sx * sy;
593
+ const exact = area <= Math.min(EXACT_AREA_MEAN_MAX_PIXELS, setup.samplesPerAxis * setup.samplesPerAxis);
594
+ let maximumColumns = 0, maximumRows = 0;
595
+ for (let i = 0; i < setup.region.width; i++)
596
+ maximumColumns = Math.max(maximumColumns, overlaps((setup.region.x + i) * sx, (setup.region.x + i + 1) * sx, setup.frameWidth).w.length);
597
+ for (let j = 0; j < setup.region.height; j++)
598
+ maximumRows = Math.max(maximumRows, overlaps((setup.region.y + j) * sy, (setup.region.y + j + 1) * sy, setup.frameHeight).w.length);
599
+ return { sx, sy, exact, maximumColumns, maximumRows };
600
+ }
601
+ /** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
602
+ export function offsetCount(request) { return request.boxSamples * request.boxSamples * (request.antiAliasing ? 4 : 1); }
603
+ /** The bilinear RGGB demosaic kernels per phase (phaseY·2 + phaseX) and channel, as `bilinearPhaseProfiles` gives them. */
604
+ export function bilinearKernels() {
605
+ const cross = [[1, 0], [-1, 0], [0, 1], [0, -1]], diagonal = [[1, 1], [1, -1], [-1, 1], [-1, -1]], horizontal = [[1, 0], [-1, 0]], vertical = [[0, 1], [0, -1]];
606
+ const average = (offsets) => offsets.map(([dx, dy]) => ({ dx: dx, dy: dy, weight: 1 / offsets.length })), self = [{ dx: 0, dy: 0, weight: 1 }];
607
+ return [[self, average(cross), average(diagonal)], [average(horizontal), self, average(vertical)], [average(vertical), self, average(horizontal)], [average(diagonal), average(cross), self]];
608
+ }
571
609
  //# sourceMappingURL=display-view-speed-gpu.js.map