@photivra/engine 2.21.0 → 2.23.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 (172) hide show
  1. package/CHANGELOG.md +150 -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 +76 -0
  30. package/dist/color/neighbor-highlight-reconstruction.js +257 -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 +102 -0
  58. package/dist/facade/display-view-rendering.js +633 -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 +55 -240
  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 +62 -19
  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 +13 -0
  115. package/dist/gpu/wgsl-display-view-coupled-stub.js +29 -0
  116. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  117. package/dist/gpu/wgsl-display-view-coupled.d.ts +91 -0
  118. package/dist/gpu/wgsl-display-view-coupled.js +365 -0
  119. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  120. package/dist/gpu/wgsl-display-view-neighbors.d.ts +46 -0
  121. package/dist/gpu/wgsl-display-view-neighbors.js +114 -0
  122. package/dist/gpu/wgsl-display-view-neighbors.js.map +1 -0
  123. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +6 -9
  124. package/dist/gpu/wgsl-display-view-quality-kernel.js +113 -113
  125. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  126. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
  127. package/dist/gpu/wgsl-display-view-speed-kernel.js +73 -10
  128. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  129. package/dist/index.d.ts +11 -3
  130. package/dist/index.js +9 -1
  131. package/dist/index.js.map +1 -1
  132. package/dist/output/camera-channel-response.d.ts +50 -0
  133. package/dist/output/camera-channel-response.js +69 -0
  134. package/dist/output/camera-channel-response.js.map +1 -0
  135. package/dist/output/native-capture-sdr.js +5 -2
  136. package/dist/output/native-capture-sdr.js.map +1 -1
  137. package/dist/output/sdr-rendering.d.ts +18 -1
  138. package/dist/output/sdr-rendering.js +39 -2
  139. package/dist/output/sdr-rendering.js.map +1 -1
  140. package/dist/sensor/native-sensor-report.d.ts +10 -0
  141. package/dist/sensor/native-sensor-report.js +16 -0
  142. package/dist/sensor/native-sensor-report.js.map +1 -0
  143. package/dist/sensor/sensor-characteristics.d.ts +81 -13
  144. package/dist/sensor/sensor-characteristics.js +10 -6
  145. package/dist/sensor/sensor-characteristics.js.map +1 -1
  146. package/docs/API_REFERENCE.md +117 -4
  147. package/docs/QUICK_START.md +37 -8
  148. package/docs/USAGE.md +105 -22
  149. package/docs/api/capture-c2pa-embed.md +177 -0
  150. package/docs/api/capture-dng-import.md +86 -10
  151. package/docs/api/capture-dng-opcodes.md +93 -0
  152. package/docs/api/capture-dng-profile-maps.md +141 -0
  153. package/docs/api/capture-dng-stage-image.md +43 -0
  154. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  155. package/docs/api/capture-lossy-jpeg.md +41 -0
  156. package/docs/api/capture-native-raw-development.md +2 -1
  157. package/docs/api/core-execution-error.md +43 -0
  158. package/docs/api/core-version.md +1 -1
  159. package/docs/api/exports.json +255 -5
  160. package/docs/api/facade-display-view-gpu.md +5 -2
  161. package/docs/api/facade-display-view.md +11 -15
  162. package/docs/api/facade-fast-methods.md +38 -9
  163. package/docs/api/facade-quality-paths.md +24 -0
  164. package/docs/api/facade-scene-radiance.md +1 -1
  165. package/docs/api/facade-simulate-capture.md +24 -7
  166. package/docs/api/facade-simulate-display-view.md +10 -12
  167. package/docs/api/gpu-image-kernel-execution.md +0 -36
  168. package/docs/api/output-camera-channel-response.md +55 -0
  169. package/docs/api/output-sdr-rendering.md +6 -1
  170. package/docs/api/sensor-sensor-characteristics.md +54 -2
  171. package/llms.txt +1 -0
  172. 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,12 @@ 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";
72
74
  if (signalInput.offsets.length < 1 || signalInput.offsets.length > DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS)
73
75
  return "the kernel holds 1 to " + DISPLAY_VIEW_GPU_MAXIMUM_OFFSETS + " query offsets";
74
76
  if (signalInput.wavelengthsNm.length < 1 || signalInput.wavelengthsNm.length > DISPLAY_VIEW_SPEED_MAXIMUM_NODES)
@@ -168,6 +170,8 @@ export function axisRecord(geometry, at, start, end, limit, margin, scale) {
168
170
  const order = ["bEven", "bOdd", "ppEven", "ppOdd", "qqEven", "qqOdd", "pqEven", "pqOdd", "ww"];
169
171
  order.forEach((key, i) => { geometry[at + 8 + i] = sums[key] / scale2; });
170
172
  }
173
+ /** The kernel's tone code: identity 0, positive Reinhard 1, extended Reinhard (luminance) 2, Naka–Rushton (luminance) 3. */
174
+ const TONE_CODE = { "identity": 0, "positive-reinhard-per-channel": 1, "extended-reinhard-luminance": 2, "naka-rushton-luminance": 3 };
171
175
  /** The uniform parameters and the aux table of a render. */
172
176
  function constants(setup, signalInput) {
173
177
  const params = new ArrayBuffer(4 * DISPLAY_VIEW_PARAM_WORDS), words = new Uint32Array(params), floats = new Float32Array(params);
@@ -178,7 +182,7 @@ function constants(setup, signalInput) {
178
182
  words[6] = setup.nativeWidth;
179
183
  words[7] = setup.nativeHeight;
180
184
  words[8] = setup.region.width;
181
- words[9] = setup.toneCurve === "positive-reinhard-per-channel" ? 1 : 0;
185
+ words[9] = TONE_CODE[setup.toneCurve];
182
186
  words[10] = signalInput.offsets.length;
183
187
  words[11] = signalInput.wavelengthsNm.length;
184
188
  for (let c = 0; c < 3; c++) {
@@ -298,12 +302,21 @@ export async function renderDisplayViewSpeedOnGpu(runtime, bandBytes, input, abo
298
302
  const rows = Math.max(1, Math.min(region.height, Math.floor(bandBytes / (region.width * BYTES_PER_PIXEL)), material.bandRows?.(shape.sy) ?? Infinity));
299
303
  if (Math.max(rows * region.width * 4 * DISPLAY_VIEW_OUTPUT_FLOATS, geometry.byteLength, table.byteLength) > runtime.maximumStorageBindingBytes)
300
304
  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));
305
+ // #593, #601: the coupled steps' shader text and constants, loaded on demand for a view that asks for them.
306
+ const coupled = setup.rendering ? await import("./wgsl-display-view-coupled.js") : null;
307
+ const coupledConstants = coupled ? coupled.displayViewCoupledConstants(setup, SDR_LUMINANCE_WEIGHTS, gaussLegendre16()) : new Float32Array(1);
308
+ // #605: the neighbor reconstruction's ratios from the host, in a second dispatch per band (wgsl-display-view-neighbors.ts).
309
+ const neighbors = setup.coupled?.neighbors && coupled;
310
+ const pipeline = await pipelines("photivra-display-view-speed:" + material.id + (coupled ? "+coupled" : "") + (neighbors ? "+neighbors" : ""), assembleDisplayViewSpeedWgsl(material.wgsl, coupled?.displayViewCoupledWgsl(!!neighbors)));
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,
@@ -317,16 +330,21 @@ export async function renderDisplayViewSpeedOnGpu(runtime, bandBytes, input, abo
317
330
  const band = new GpuResourceScope(runtime);
318
331
  try {
319
332
  const out = band.buffer("photivra-display-view-moments", count * 4 * DISPLAY_VIEW_OUTPUT_FLOATS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
320
- const flagBuffer = band.buffer("photivra-display-view-flags", count * 4 * DISPLAY_VIEW_FLAG_WORDS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
321
333
  const uniform = band.upload("photivra-display-view-params", new Uint8Array(params), BUFFER_USAGE.UNIFORM);
322
- const bindGroup = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [
323
- { binding: 0, resource: { buffer: uniform } }, { binding: 1, resource: { buffer: auxBuffer } }, { binding: 2, resource: { buffer: geometryBuffer } },
324
- { binding: 3, resource: { buffer: tableBuffer } }, { binding: 4, resource: { buffer: out } }, { binding: 5, resource: { buffer: flagBuffer } }
325
- ] });
326
334
  // #581: a module that bands its own data (a large photo) gives group 1 per band, for the native rows the band reads (with margin).
327
- const [data, flagData] = await runComputePass(runtime, band, pipeline, [bindGroup, materialGroup ?? material.band(band, pipeline.getBindGroupLayout(1), Math.floor((region.y + row0) * shape.sy) + margin - 2, Math.ceil((region.y + row1) * shape.sy) + margin + 2)], linearDispatch(count, DISPLAY_VIEW_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), [{ buffer: out, bytes: count * 4 * DISPLAY_VIEW_OUTPUT_FLOATS }, { buffer: flagBuffer, bytes: count * 4 * DISPLAY_VIEW_FLAG_WORDS }], abort.signal);
335
+ const group1 = materialGroup ?? material.band(band, pipeline.getBindGroupLayout(1), Math.floor((region.y + row0) * shape.sy) + margin - 2, Math.ceil((region.y + row1) * shape.sy) + margin + 2);
336
+ let data, flagData;
337
+ const run = async (contents = new Uint32Array(count * DISPLAY_VIEW_FLAG_WORDS)) => {
338
+ const flagBuffer = band.upload("photivra-display-view-flags", contents, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
339
+ const bindGroup = runtime.device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [
340
+ { binding: 0, resource: { buffer: uniform } }, { binding: 1, resource: { buffer: auxBuffer } }, { binding: 2, resource: { buffer: geometryBuffer } },
341
+ { binding: 3, resource: { buffer: tableBuffer } }, { binding: 4, resource: { buffer: out } }, { binding: 5, resource: { buffer: flagBuffer } }
342
+ ] });
343
+ [data, flagData] = await runComputePass(runtime, band, pipeline, [bindGroup, group1], linearDispatch(count, DISPLAY_VIEW_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), [{ buffer: out, bytes: count * 4 * DISPLAY_VIEW_OUTPUT_FLOATS }, { buffer: flagBuffer, bytes: count * 4 * DISPLAY_VIEW_FLAG_WORDS }], abort.signal);
344
+ return new Uint32Array(flagData, 0, count * DISPLAY_VIEW_FLAG_WORDS);
345
+ };
346
+ const bandFlags = neighbors ? await neighbors.dispatchWithNeighborRatios(setup, geometry, first, count, runtime.maximumStorageBindingBytes, run) : await run();
328
347
  moments.set(new Float32Array(data, 0, count * DISPLAY_VIEW_OUTPUT_FLOATS), first * DISPLAY_VIEW_OUTPUT_FLOATS);
329
- const bandFlags = new Uint32Array(flagData, 0, count * DISPLAY_VIEW_FLAG_WORDS);
330
348
  flagWords.set(bandFlags, first * DISPLAY_VIEW_FLAG_WORDS);
331
349
  for (let i = 0; i < count; i++) {
332
350
  let flag = (bandFlags[DISPLAY_VIEW_FLAG_WORDS * i] & 1) !== 0;
@@ -409,7 +427,7 @@ export async function renderDisplayViewSpeedOnGpu(runtime, bandBytes, input, abo
409
427
  saturatedPixelCount += saturated[i];
410
428
  const expectedValueBound = { kind: "relative-to-reference", relative: relative.mean, absolute: largestMean };
411
429
  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,
430
+ return { samples, expected, noiseStd, sampleCount, siteCount, detailPixelCount: pixelCount, uniformPixelCount: 0, estimation: "exact-area-mean", saturated, saturatedPixelCount,
413
431
  execution: webGpuExecution(expectedValueBound, uncertain), expectedValueBound,
414
432
  varianceBound: { kind: "relative-to-reference", relative: relative.variance, absolute: largestVariance },
415
433
  expectedValueAbsoluteBound: meanBound, varianceAbsoluteBound: varianceBound,
@@ -434,12 +452,15 @@ export function displayViewSpeedGpuSupports(request) {
434
452
  export function sceneToneAndOffsetsReason(request, maximumOffsets) {
435
453
  if (request.scene !== "chart" && request.scene !== "material-layers" && request.scene !== "image-plane")
436
454
  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")
455
+ // #593: the identity, Reinhard and the two luminance tone curves (the luminance curves through the coupled steps).
456
+ if (!GPU_TONE_CURVES.includes(request.toneCurve))
438
457
  return "unsupported tone curve " + String(request.toneCurve);
439
458
  if (offsetCount(request) > maximumOffsets)
440
459
  return "more than " + maximumOffsets + " query offsets per site";
441
460
  return true;
442
461
  }
462
+ /** The tone curves the GPU kernels render (#593 adds the luminance curves). */
463
+ const GPU_TONE_CURVES = ["identity", "positive-reinhard-per-channel", "extended-reinhard-luminance", "naka-rushton-luminance"];
443
464
  /**
444
465
  * A self-check's verdict: every component of a GPU render inside its a-priori bounds against the reference, every sample within
445
466
  * one code, and (#533) the saturated-pixel count the reference's bit for bit; true, or a sentence saying where it failed.
@@ -548,7 +569,7 @@ export function createDisplayViewSpeedGpuRenderer(runtime, options = {}) {
548
569
  const material = await modules.moduleFor(setup.gpu, abort.signal);
549
570
  const result = await renderDisplayViewSpeedOnGpu(runtime, bandBytes, { setup, signalInput: setup.gpu.signalInput, material }, abort, options.yieldControl, source);
550
571
  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,
572
+ uniformPixelCount: result.uniformPixelCount, estimation: result.estimation, saturated: result.saturated,
552
573
  saturatedPixelCount: result.saturatedPixelCount, errorBound: result.expectedValueBound,
553
574
  diagnosticUncertaintySampleCount: result.execution.diagnosticUncertaintySampleCount, ...(material.compositeProbe ? { compositeProbe: material.compositeProbe } : {}) };
554
575
  }
@@ -568,4 +589,26 @@ export const displayViewSpeedGpuRendererFactory = (handle) => createDisplayViewS
568
589
  export function registerDisplayViewSpeedGpuRenderer() {
569
590
  return setDisplayViewGpuRendererFactory(displayViewSpeedGpuRendererFactory);
570
591
  }
592
+ // #594: shared with the on-demand Quality host (display-view-quality-gpu.ts), so the Speed path does not import it.
593
+ /** #564: the GPU refusal of a defocus kernel above the direct radius (the CPU convolves it by runs). */
594
+ export const LARGE_DEFOCUS_REASON = "a defocus kernel radius above 64 sites (convolved by runs on the CPU, #564) is not on the GPU";
595
+ /** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
596
+ export function footprintShape(setup) {
597
+ const sx = setup.frameWidth / setup.viewWidth, sy = setup.frameHeight / setup.viewHeight, area = sx * sy;
598
+ const exact = area <= Math.min(EXACT_AREA_MEAN_MAX_PIXELS, setup.samplesPerAxis * setup.samplesPerAxis);
599
+ let maximumColumns = 0, maximumRows = 0;
600
+ for (let i = 0; i < setup.region.width; i++)
601
+ maximumColumns = Math.max(maximumColumns, overlaps((setup.region.x + i) * sx, (setup.region.x + i + 1) * sx, setup.frameWidth).w.length);
602
+ for (let j = 0; j < setup.region.height; j++)
603
+ maximumRows = Math.max(maximumRows, overlaps((setup.region.y + j) * sy, (setup.region.y + j + 1) * sy, setup.frameHeight).w.length);
604
+ return { sx, sy, exact, maximumColumns, maximumRows };
605
+ }
606
+ /** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
607
+ export function offsetCount(request) { return request.boxSamples * request.boxSamples * (request.antiAliasing ? 4 : 1); }
608
+ /** The bilinear RGGB demosaic kernels per phase (phaseY·2 + phaseX) and channel, as `bilinearPhaseProfiles` gives them. */
609
+ export function bilinearKernels() {
610
+ 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]];
611
+ const average = (offsets) => offsets.map(([dx, dy]) => ({ dx: dx, dy: dy, weight: 1 / offsets.length })), self = [{ dx: 0, dy: 0, weight: 1 }];
612
+ return [[self, average(cross), average(diagonal)], [average(horizontal), self, average(vertical)], [average(vertical), self, average(horizontal)], [average(diagonal), average(cross), self]];
613
+ }
571
614
  //# sourceMappingURL=display-view-speed-gpu.js.map