@photivra/engine 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +11 -0
- package/dist/capture/c2pa-embed.d.ts +116 -0
- package/dist/capture/c2pa-embed.js +493 -0
- package/dist/capture/c2pa-embed.js.map +1 -0
- package/dist/capture/dng-import.d.ts +73 -8
- package/dist/capture/dng-import.js +315 -80
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/dng-opcodes.d.ts +96 -0
- package/dist/capture/dng-opcodes.js +391 -0
- package/dist/capture/dng-opcodes.js.map +1 -0
- package/dist/capture/dng-profile-maps.d.ts +99 -0
- package/dist/capture/dng-profile-maps.js +381 -0
- package/dist/capture/dng-profile-maps.js.map +1 -0
- package/dist/capture/dng-stage-image.d.ts +23 -0
- package/dist/capture/dng-stage-image.js +65 -0
- package/dist/capture/dng-stage-image.js.map +1 -0
- package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
- package/dist/capture/jpeg-xl-tiles.js +151 -0
- package/dist/capture/jpeg-xl-tiles.js.map +1 -0
- package/dist/capture/lossy-jpeg.d.ts +17 -0
- package/dist/capture/lossy-jpeg.js +369 -0
- package/dist/capture/lossy-jpeg.js.map +1 -0
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
- package/dist/color/neighbor-highlight-reconstruction.js +234 -0
- package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +5 -0
- package/dist/color/sensor-color-development.js +7 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/color/spectral-spikiness.d.ts +11 -0
- package/dist/color/spectral-spikiness.js +23 -0
- package/dist/color/spectral-spikiness.js.map +1 -0
- package/dist/core/execution-error.d.ts +21 -0
- package/dist/core/execution-error.js +12 -0
- package/dist/core/execution-error.js.map +1 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +26 -1
- package/dist/facade/catalogue-lens-signal.js +25 -7
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +1 -1
- package/dist/facade/diffraction-provider.js +4 -4
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +38 -6
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-normal.d.ts +15 -0
- package/dist/facade/display-view-normal.js +44 -0
- package/dist/facade/display-view-normal.js.map +1 -0
- package/dist/facade/display-view-rendering.d.ts +97 -0
- package/dist/facade/display-view-rendering.js +598 -0
- package/dist/facade/display-view-rendering.js.map +1 -0
- package/dist/facade/display-view.d.ts +37 -65
- package/dist/facade/display-view.js +147 -476
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.js +2 -2
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +37 -8
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +1 -1
- package/dist/facade/generic-lens-capture.js +1 -1
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane.js +41 -10
- package/dist/facade/image-plane.js.map +1 -1
- package/dist/facade/on-demand-kits.d.ts +14 -0
- package/dist/facade/on-demand-kits.js +36 -0
- package/dist/facade/on-demand-kits.js.map +1 -0
- package/dist/facade/opt-in-kit.d.ts +9 -0
- package/dist/facade/opt-in-kit.js +11 -0
- package/dist/facade/opt-in-kit.js.map +1 -0
- package/dist/facade/quality-kit.d.ts +21 -0
- package/dist/facade/quality-kit.js +23 -0
- package/dist/facade/quality-kit.js.map +1 -0
- package/dist/facade/quality-paths.d.ts +22 -0
- package/dist/facade/quality-paths.js +44 -0
- package/dist/facade/quality-paths.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +18 -2
- package/dist/facade/scene-radiance.js +6 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +32 -13
- package/dist/facade/simulate-capture.js +162 -69
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +10 -12
- package/dist/facade/simulate-display-view.js +57 -48
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
- package/dist/gpu/display-view-gpu-renderer.js +134 -0
- package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
- package/dist/gpu/display-view-image-material-module.d.ts +3 -2
- package/dist/gpu/display-view-image-material-module.js +25 -3
- package/dist/gpu/display-view-image-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-quality.d.ts +43 -1
- package/dist/gpu/display-view-image-quality.js +256 -11
- package/dist/gpu/display-view-image-quality.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
- package/dist/gpu/display-view-quality-gpu.js +60 -249
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
- package/dist/gpu/display-view-speed-gpu.js +50 -12
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +2 -13
- package/dist/gpu/image-kernel-execution.js +2 -10
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
- package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
- package/dist/gpu/wgsl-display-view-coupled.js +353 -0
- package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -9
- package/dist/gpu/wgsl-display-view-quality-kernel.js +109 -111
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
- package/dist/gpu/wgsl-display-view-speed-kernel.js +71 -8
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/output/camera-channel-response.d.ts +50 -0
- package/dist/output/camera-channel-response.js +69 -0
- package/dist/output/camera-channel-response.js.map +1 -0
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/sdr-rendering.d.ts +18 -1
- package/dist/output/sdr-rendering.js +39 -2
- package/dist/output/sdr-rendering.js.map +1 -1
- package/dist/sensor/native-sensor-report.d.ts +10 -0
- package/dist/sensor/native-sensor-report.js +16 -0
- package/dist/sensor/native-sensor-report.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +81 -13
- package/dist/sensor/sensor-characteristics.js +10 -6
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/API_REFERENCE.md +117 -4
- package/docs/QUICK_START.md +37 -8
- package/docs/USAGE.md +104 -22
- package/docs/api/capture-c2pa-embed.md +177 -0
- package/docs/api/capture-dng-import.md +86 -10
- package/docs/api/capture-dng-opcodes.md +93 -0
- package/docs/api/capture-dng-profile-maps.md +141 -0
- package/docs/api/capture-dng-stage-image.md +43 -0
- package/docs/api/capture-jpeg-xl-tiles.md +33 -0
- package/docs/api/capture-lossy-jpeg.md +41 -0
- package/docs/api/capture-native-raw-development.md +2 -1
- package/docs/api/core-execution-error.md +43 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +255 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-fast-methods.md +38 -9
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-scene-radiance.md +1 -1
- package/docs/api/facade-simulate-capture.md +24 -7
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +1 -0
- package/package.json +6 -5
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
* Host side of the display view's Quality path on the GPU (#479): the support check, the float64 tables (the lattice groups
|
|
3
3
|
* and their combined diffraction kernels per material and channel, as the reference forms them; the bilinear and AHD
|
|
4
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
|
|
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:
|
|
5
|
+
* display pixels; `wgsl-display-view-quality-kernel.ts`), and the reference's own float64 finishing per display pixel (`finishDisplayViewPixel` of the Speed host:
|
|
8
6
|
* the quantization term, the seeded noise draw, the Cholesky factor, the clipped centre, the sRGB encode and the rounding),
|
|
9
7
|
* so the noise draw is bit-identical to the CPU's.
|
|
10
8
|
*
|
|
@@ -16,19 +14,21 @@
|
|
|
16
14
|
*
|
|
17
15
|
* The float64 reference is `renderDisplayView` (facade/display-view.ts) on the exact-area-mean path with
|
|
18
16
|
* `createDisplayViewExpectedSignal` (facade/expected-signal.ts), chart materials, with or without diffraction kernels,
|
|
19
|
-
* the bilinear or the AHD candidates
|
|
17
|
+
* the bilinear or the AHD candidates. Internal: not exported from the package.
|
|
20
18
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* #594: this module and its WGSL load on demand (`prepareDisplayViewQualityGpu` in `display-view-gpu-renderer.ts`, a dynamic
|
|
20
|
+
* import), at the first render that takes the Quality passes; the renderer and the Speed path never import it statically.
|
|
21
|
+
*
|
|
22
|
+
* Flagged display pixels (an uncertain lattice material among the sites read, AHD's choice on the noise-free mosaic able to move
|
|
23
23
|
* the pixel by more than DISPLAY_VIEW_QUALITY_CHOICE_SIGMA of its noise (#457), a non-finite output) are recomputed with the
|
|
24
24
|
* reference, so they are exact. Every stage's wall-clock time is reported (`stageMilliseconds`); on a software adapter it
|
|
25
25
|
* is not GPU performance.
|
|
26
26
|
*/
|
|
27
|
-
import { type
|
|
27
|
+
import { type DisplayViewRenderResult, type DisplayViewSetup } from "../facade/display-view.js";
|
|
28
28
|
import { type DisplayViewExpectedSignalInput } from "../facade/expected-signal.js";
|
|
29
29
|
import type { DisplayViewGpuSetup } from "../facade/display-view.js";
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
30
|
+
import type { DisplayViewMaterialModules } from "./display-view-composite-material-module.js";
|
|
31
|
+
import type { DisplayViewGpuConformanceCase } from "../facade/display-view-gpu.js";
|
|
32
32
|
import { type DisplayViewGpuMaterialModule } from "./display-view-speed-gpu.js";
|
|
33
33
|
import { type ImageKernelExecution, type ImageKernelRelativeErrorBound } from "./image-kernel-execution.js";
|
|
34
34
|
import { GpuResourceScope, type WebGpuRuntime } from "./webgpu-runtime.js";
|
|
@@ -56,8 +56,6 @@ export interface DisplayViewQualityGpuInput {
|
|
|
56
56
|
material: DisplayViewGpuMaterialModule;
|
|
57
57
|
/** Recompute flagged display pixels with the reference. Default true. */
|
|
58
58
|
recomputeFlagged?: boolean;
|
|
59
|
-
/** The states this renderer holds for the setup's correction key (grown by the render); a fresh store when absent. */
|
|
60
|
-
states?: CorrectionStateStore;
|
|
61
59
|
/**
|
|
62
60
|
* #492: sum each site's taps in blocks (rows, groups; `DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL`), whose bound grows with the
|
|
63
61
|
* kernel's width rather than its tap count. Default: when the setup's expected signal carries the defocus disc
|
|
@@ -92,16 +90,12 @@ export interface DisplayViewQualityStageTimings {
|
|
|
92
90
|
lattice: number;
|
|
93
91
|
/** Pass 2: the site electrons through the combined kernels, the readout moments and the white clip. */
|
|
94
92
|
sites: number;
|
|
95
|
-
/** Pass 3: the display pixels (demosaic candidates, development, covariance
|
|
93
|
+
/** Pass 3: the display pixels (demosaic candidates, development, covariance), every dispatch of it. */
|
|
96
94
|
pixels: number;
|
|
97
|
-
/** The low-signal correction states computed on the host (the reference's own draws) and the table uploads. */
|
|
98
|
-
states: number;
|
|
99
95
|
/** The host finishing (quantization term, noise draw, Cholesky, encode) of every display pixel. */
|
|
100
96
|
finishing: number;
|
|
101
97
|
/** The reference's recomputation of flagged display pixels. */
|
|
102
98
|
recompute: number;
|
|
103
|
-
/** Pass 3 dispatches that were repeated because a correction state was missing. */
|
|
104
|
-
pixelRedispatches: number;
|
|
105
99
|
/** Pass 2 dispatches (one per band, or more where a band's sites × taps exceed the tap budget per dispatch, #492). */
|
|
106
100
|
siteDispatches: number;
|
|
107
101
|
}
|
|
@@ -122,9 +116,6 @@ export interface DisplayViewQualityGpuResult extends DisplayViewRenderResult {
|
|
|
122
116
|
pixelFlags: Uint32Array;
|
|
123
117
|
/** #533: display pixels (not flagged) with a real pixel whose saturation the kernel left undecided; their count is the host reference's. */
|
|
124
118
|
highlightRecomputedPixelIndices: Uint32Array;
|
|
125
|
-
/** Correction states the render computed on the host (new to the store), and the states the table held at the end. */
|
|
126
|
-
statesComputed: number;
|
|
127
|
-
statesHeld: number;
|
|
128
119
|
/** Taps per native site of the expected-signal gather (over every lattice group). */
|
|
129
120
|
tapsPerSite: number;
|
|
130
121
|
/** #492: whether the site pass summed in blocks (the defocus disc), and the relative bound of a site's electrons it used. */
|
|
@@ -137,33 +128,10 @@ export interface DisplayViewQualityGpuResult extends DisplayViewRenderResult {
|
|
|
137
128
|
latticePointsResolved: number;
|
|
138
129
|
stageMilliseconds: DisplayViewQualityStageTimings;
|
|
139
130
|
}
|
|
140
|
-
/**
|
|
141
|
-
* The low-signal correction states a renderer holds for one correction key, as the GPU table reads them: grid keys ascending
|
|
142
|
-
* with three float32 values each (the host keeps the float64 values; the table is their single rounding).
|
|
143
|
-
*/
|
|
144
|
-
export declare class CorrectionStateStore {
|
|
145
|
-
#private;
|
|
146
|
-
get size(): number;
|
|
147
|
-
has(key: number): boolean;
|
|
148
|
-
add(gridKeys: Int32Array, values: Float64Array): void;
|
|
149
|
-
/** The sorted table (rebuilt when states were added since the last call). */
|
|
150
|
-
table(): {
|
|
151
|
-
keys: Int32Array<ArrayBuffer>;
|
|
152
|
-
values: Float32Array<ArrayBuffer>;
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
|
|
156
|
-
export declare function footprintShape(setup: DisplayViewSetup): {
|
|
157
|
-
sx: number;
|
|
158
|
-
sy: number;
|
|
159
|
-
exact: boolean;
|
|
160
|
-
maximumColumns: number;
|
|
161
|
-
maximumRows: number;
|
|
162
|
-
};
|
|
163
131
|
/**
|
|
164
132
|
* Why the Quality kernels do not cover a request, or null when they do: chart materials, the exact area mean (footprints of
|
|
165
133
|
* at most 25 real pixels and 6 per axis), the identity or positive Reinhard tone curve, demosaic candidates within 3 sites
|
|
166
|
-
* (1 or 2 per phase), at most 64 lattice groups, lattice constants exact in float32,
|
|
134
|
+
* (1 or 2 per phase), at most 64 lattice groups, lattice constants exact in float32, and a raster at least
|
|
167
135
|
* 2·reach + 2 sites on each axis (so a reflected border site lies in the band's window).
|
|
168
136
|
*/
|
|
169
137
|
export declare function displayViewQualityGpuUnsupportedReason(setup: DisplayViewSetup, signalInput: DisplayViewExpectedSignalInput): string | null;
|
|
@@ -229,29 +197,10 @@ export declare function renderDisplayViewQualityOnGpu(runtime: WebGpuRuntime, ba
|
|
|
229
197
|
export declare function qualityMaterialSetup(gpu: DisplayViewGpuSetup): DisplayViewGpuSetup;
|
|
230
198
|
/** The material module of a Quality render's scene (see `qualityMaterialSetup`). */
|
|
231
199
|
export declare function qualityMaterialModule(modules: DisplayViewMaterialModules, gpu: DisplayViewGpuSetup, signal: AbortSignal): Promise<DisplayViewGpuMaterialModule>;
|
|
232
|
-
/** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
|
|
233
|
-
export declare function offsetCount(request: DisplayViewGpuRequest): number;
|
|
234
|
-
/**
|
|
235
|
-
* Why the Quality renderer does not support a prepared request (`DisplayViewGpuRenderer.supports`), or true: engine charts and
|
|
236
|
-
* declared `material-layers` scenes (the material modules of `createDisplayViewMaterialModules`, #491), the identity or Reinhard
|
|
237
|
-
* tone curve and at most 64 query offsets (lattice groups). `lens.defocus` out of focus is supported (#492 step 2c: the site pass
|
|
238
|
-
* gathers the disc, or each node's diffraction kernel convolved with it, up to the reference's own caps of radius 64 and 96);
|
|
239
|
-
* whether a view's footprints take the exact area mean, whether the device passes the composite probe, and whether the kernel
|
|
240
|
-
* table fits the device's storage binding limit, is known only per render.
|
|
241
|
-
*/
|
|
242
|
-
export declare function displayViewQualityGpuSupports(request: DisplayViewGpuRequest): true | string;
|
|
243
200
|
/** #479's self-check (see `runQualityConformance`). */
|
|
244
201
|
export declare const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE: DisplayViewGpuConformanceCase;
|
|
245
202
|
/** #492's self-check of the defocus disc's blocked site sum (see `runQualityConformance`). */
|
|
246
203
|
export declare const DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE: DisplayViewGpuConformanceCase;
|
|
247
|
-
/**
|
|
248
|
-
export declare
|
|
249
|
-
export declare function createDisplayViewGpuRenderer(runtime: WebGpuRuntime, options?: {
|
|
250
|
-
maximumBandBytes?: number;
|
|
251
|
-
yieldControl?: YieldControl;
|
|
252
|
-
}): DisplayViewGpuRenderer;
|
|
253
|
-
/** The factory `prepareDisplayView` creates the renderer with (one per device): the Speed and Quality kernels behind one renderer. */
|
|
254
|
-
export declare const displayViewGpuRendererFactory: DisplayViewGpuRendererFactory;
|
|
255
|
-
/** Registers the combined renderer as the display view's GPU renderer and returns the factory registered before. */
|
|
256
|
-
export declare function registerDisplayViewGpuRenderer(): DisplayViewGpuRendererFactory | null;
|
|
204
|
+
/** #594: the Quality kernels' self-check cases, run before the first render that takes the Quality passes (`onDemandConformanceCases`). */
|
|
205
|
+
export declare const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASES: readonly DisplayViewGpuConformanceCase[];
|
|
257
206
|
export {};
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
* Host side of the display view's Quality path on the GPU (#479): the support check, the float64 tables (the lattice groups
|
|
4
4
|
* and their combined diffraction kernels per material and channel, as the reference forms them; the bilinear and AHD
|
|
5
5
|
* candidate kernels; the footprint geometry), the three passes per band of display rows (lattice materials, site moments,
|
|
6
|
-
* display pixels; `wgsl-display-view-quality-kernel.ts`), the
|
|
7
|
-
* reference's own `computeDisplayViewCorrectionStates`, #447, uploaded as a sorted table and grown from the grid keys the
|
|
8
|
-
* kernel records), and the reference's own float64 finishing per display pixel (`finishDisplayViewPixel` of the Speed host:
|
|
6
|
+
* display pixels; `wgsl-display-view-quality-kernel.ts`), and the reference's own float64 finishing per display pixel (`finishDisplayViewPixel` of the Speed host:
|
|
9
7
|
* the quantization term, the seeded noise draw, the Cholesky factor, the clipped centre, the sRGB encode and the rounding),
|
|
10
8
|
* so the noise draw is bit-identical to the CPU's.
|
|
11
9
|
*
|
|
@@ -17,25 +15,26 @@
|
|
|
17
15
|
*
|
|
18
16
|
* The float64 reference is `renderDisplayView` (facade/display-view.ts) on the exact-area-mean path with
|
|
19
17
|
* `createDisplayViewExpectedSignal` (facade/expected-signal.ts), chart materials, with or without diffraction kernels,
|
|
20
|
-
* the bilinear or the AHD candidates
|
|
18
|
+
* the bilinear or the AHD candidates. Internal: not exported from the package.
|
|
21
19
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* #594: this module and its WGSL load on demand (`prepareDisplayViewQualityGpu` in `display-view-gpu-renderer.ts`, a dynamic
|
|
21
|
+
* import), at the first render that takes the Quality passes; the renderer and the Speed path never import it statically.
|
|
22
|
+
*
|
|
23
|
+
* Flagged display pixels (an uncertain lattice material among the sites read, AHD's choice on the noise-free mosaic able to move
|
|
24
24
|
* the pixel by more than DISPLAY_VIEW_QUALITY_CHOICE_SIGMA of its noise (#457), a non-finite output) are recomputed with the
|
|
25
25
|
* reference, so they are exact. Every stage's wall-clock time is reported (`stageMilliseconds`); on a software adapter it
|
|
26
26
|
* is not GPU performance.
|
|
27
27
|
*/
|
|
28
|
-
import {
|
|
28
|
+
import { axisSums, displayViewDecidedSaturation, overlaps, renderDisplayView, ahdCandidateKernels } from "../facade/display-view.js";
|
|
29
29
|
import { displayViewSignalTables } from "../facade/expected-signal.js";
|
|
30
|
-
import { createDisplayViewMaterialModules } from "./display-view-composite-material-module.js";
|
|
31
|
-
import { setDisplayViewGpuRendererFactory } from "../facade/display-view-gpu.js";
|
|
32
30
|
import { chartPresetMaterialModule } from "./display-view-chart-material-module.js";
|
|
33
|
-
import {
|
|
31
|
+
import { CONFORMANCE_FRAME, LARGE_DEFOCUS_REASON, recomputeRuns, footprintShape, conformanceChannelAt, conformanceChart, conformanceResponses, conformanceSetup, conformanceVerdict, withConformanceAbort, axisRecord, finishDisplayViewPixel, gaussLegendre16, queryGeometry } from "./display-view-speed-gpu.js";
|
|
34
32
|
import { createHostYieldGate } from "../capture/host-yield-cadence.js";
|
|
35
33
|
import { createDefocusDiscSiteKernel } from "../optics/defocus-disc-kernel.js";
|
|
36
34
|
import { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF, webGpuExecution } from "./image-kernel-execution.js";
|
|
37
|
-
import { BUFFER_USAGE, GpuResourceScope,
|
|
38
|
-
import { DISPLAY_VIEW_QUALITY_AUX,
|
|
35
|
+
import { BUFFER_USAGE, GpuResourceScope, linearDispatch, runComputePass, throwIfAborted as checkAbort } from "./webgpu-runtime.js";
|
|
36
|
+
import { DISPLAY_VIEW_QUALITY_AUX, DISPLAY_VIEW_QUALITY_CANDIDATE_ENTRIES, DISPLAY_VIEW_QUALITY_CANDIDATE_FLOATS, DISPLAY_VIEW_QUALITY_FLAG, DISPLAY_VIEW_QUALITY_FLAG_WORDS, DISPLAY_VIEW_QUALITY_GEOMETRY_STRIDE, DISPLAY_VIEW_QUALITY_GROUP_WORDS, DISPLAY_VIEW_QUALITY_MAXIMUM_NODES, DISPLAY_VIEW_QUALITY_MAXIMUM_PIXELS_PER_AXIS, DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS, DISPLAY_VIEW_QUALITY_PARAM_WORDS, DISPLAY_VIEW_QUALITY_PIXELS_WGSL, DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL, DISPLAY_VIEW_QUALITY_SITES_WGSL, DISPLAY_VIEW_QUALITY_SITE_FLOATS, DISPLAY_VIEW_QUALITY_TAP_WORDS, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, assembleDisplayViewQualityLatticeWgsl, assembleDisplayViewQualityPixelsWgsl } from "./wgsl-display-view-quality-kernel.js";
|
|
37
|
+
import { SDR_LUMINANCE_WEIGHTS } from "../output/sdr-rendering.js";
|
|
39
38
|
/** One WGSL rounding of + − × in either direction (W3C WGSL "Floating Point Accuracy"; #471, #487): twice the binary32 unit roundoff. */
|
|
40
39
|
const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
|
|
41
40
|
/** Every reported bound is doubled: the kernel's running bounds are first order, and their own float32 arithmetic rounds. */
|
|
@@ -75,37 +74,6 @@ const QUANTIZATION_SLOPE = 6.04, QUANTIZATION_JOIN_JUMP = 2.1e-4, QUANTIZATION_J
|
|
|
75
74
|
/** Bytes per display pixel in the output buffers (moments plus the flag word). */
|
|
76
75
|
const OUTPUT_BYTES_PER_PIXEL = 4 * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS + 4 * DISPLAY_VIEW_QUALITY_FLAG_WORDS;
|
|
77
76
|
const SITE_BYTES = 4 * DISPLAY_VIEW_QUALITY_SITE_FLOATS;
|
|
78
|
-
/**
|
|
79
|
-
* The low-signal correction states a renderer holds for one correction key, as the GPU table reads them: grid keys ascending
|
|
80
|
-
* with three float32 values each (the host keeps the float64 values; the table is their single rounding).
|
|
81
|
-
*/
|
|
82
|
-
export class CorrectionStateStore {
|
|
83
|
-
#known = new Map();
|
|
84
|
-
#keys = new Int32Array(0);
|
|
85
|
-
#values = new Float32Array(0);
|
|
86
|
-
#dirty = false;
|
|
87
|
-
get size() { return this.#known.size; }
|
|
88
|
-
has(key) { return this.#known.has(key); }
|
|
89
|
-
add(gridKeys, values) {
|
|
90
|
-
for (let i = 0; i < gridKeys.length; i++) {
|
|
91
|
-
if (this.#known.has(gridKeys[i]))
|
|
92
|
-
continue;
|
|
93
|
-
this.#known.set(gridKeys[i], [values[3 * i], values[3 * i + 1], values[3 * i + 2]]);
|
|
94
|
-
this.#dirty = true;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/** The sorted table (rebuilt when states were added since the last call). */
|
|
98
|
-
table() {
|
|
99
|
-
if (this.#dirty) {
|
|
100
|
-
const keys = Int32Array.from(this.#known.keys()).sort(), values = new Float32Array(3 * keys.length);
|
|
101
|
-
keys.forEach((key, i) => { const v = this.#known.get(key); values[3 * i] = v[0]; values[3 * i + 1] = v[1]; values[3 * i + 2] = v[2]; });
|
|
102
|
-
this.#keys = keys;
|
|
103
|
-
this.#values = values;
|
|
104
|
-
this.#dirty = false;
|
|
105
|
-
}
|
|
106
|
-
return { keys: this.#keys, values: this.#values };
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
77
|
/**
|
|
110
78
|
* Waits until the queue has finished the work submitted so far (`GPUQueue.onSubmittedWorkDone`), so a pass that reads nothing
|
|
111
79
|
* back is timed with its own execution, not the next pass's wait. The passes depend on each other in order anyway.
|
|
@@ -115,17 +83,6 @@ async function queueIdle(runtime, signal) {
|
|
|
115
83
|
if (typeof queue.onSubmittedWorkDone === "function")
|
|
116
84
|
await runtime.guard(queue.onSubmittedWorkDone(), signal);
|
|
117
85
|
}
|
|
118
|
-
/** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
|
|
119
|
-
export function footprintShape(setup) {
|
|
120
|
-
const sx = setup.frameWidth / setup.viewWidth, sy = setup.frameHeight / setup.viewHeight, area = sx * sy;
|
|
121
|
-
const exact = area <= Math.min(EXACT_AREA_MEAN_MAX_PIXELS, setup.samplesPerAxis * setup.samplesPerAxis);
|
|
122
|
-
let maximumColumns = 0, maximumRows = 0;
|
|
123
|
-
for (let i = 0; i < setup.region.width; i++)
|
|
124
|
-
maximumColumns = Math.max(maximumColumns, overlaps((setup.region.x + i) * sx, (setup.region.x + i + 1) * sx, setup.frameWidth).w.length);
|
|
125
|
-
for (let j = 0; j < setup.region.height; j++)
|
|
126
|
-
maximumRows = Math.max(maximumRows, overlaps((setup.region.y + j) * sy, (setup.region.y + j + 1) * sy, setup.frameHeight).w.length);
|
|
127
|
-
return { sx, sy, exact, maximumColumns, maximumRows };
|
|
128
|
-
}
|
|
129
86
|
/** The demosaic candidates of a setup (the bilinear kernels when it has none) and their reach in sites. */
|
|
130
87
|
function candidatesOf(setup) {
|
|
131
88
|
const candidates = setup.candidates ?? setup.demosaic.map((phase) => [phase]), count = candidates[0]?.length ?? 0;
|
|
@@ -147,7 +104,7 @@ function exactLatticeConstant(c, span) {
|
|
|
147
104
|
/**
|
|
148
105
|
* Why the Quality kernels do not cover a request, or null when they do: chart materials, the exact area mean (footprints of
|
|
149
106
|
* at most 25 real pixels and 6 per axis), the identity or positive Reinhard tone curve, demosaic candidates within 3 sites
|
|
150
|
-
* (1 or 2 per phase), at most 64 lattice groups, lattice constants exact in float32,
|
|
107
|
+
* (1 or 2 per phase), at most 64 lattice groups, lattice constants exact in float32, and a raster at least
|
|
151
108
|
* 2·reach + 2 sites on each axis (so a reflected border site lies in the band's window).
|
|
152
109
|
*/
|
|
153
110
|
export function displayViewQualityGpuUnsupportedReason(setup, signalInput) {
|
|
@@ -157,10 +114,12 @@ export function displayViewQualityGpuUnsupportedReason(setup, signalInput) {
|
|
|
157
114
|
return "radiance scenes (caller functions) are not on the GPU";
|
|
158
115
|
if (!signalInput.kernels && !signalInput.queryPoint)
|
|
159
116
|
return "the expected signal has no query points";
|
|
160
|
-
if (setup.
|
|
161
|
-
return "a collect pass of the correction states runs on the CPU";
|
|
162
|
-
if (setup.toneCurve !== "identity" && setup.toneCurve !== "positive-reinhard-per-channel")
|
|
117
|
+
if (!["identity", "positive-reinhard-per-channel", "extended-reinhard-luminance", "naka-rushton-luminance"].includes(setup.toneCurve))
|
|
163
118
|
return "unsupported tone curve " + String(setup.toneCurve);
|
|
119
|
+
if (setup.rendering && !setup.coupled)
|
|
120
|
+
return "the coupled rendering steps were not prepared";
|
|
121
|
+
if (setup.rendering?.highlightMode === "reconstruct-neighbors")
|
|
122
|
+
return "highlightMode reconstruct-neighbors (#602; its regions' ratios are found on the host) is not on the GPU in 2.22.0";
|
|
164
123
|
if (setup.region.width < 1 || setup.region.height < 1)
|
|
165
124
|
return "an empty region";
|
|
166
125
|
if (signalInput.wavelengthsNm.length < 1 || signalInput.wavelengthsNm.length > DISPLAY_VIEW_QUALITY_MAXIMUM_NODES)
|
|
@@ -406,7 +365,6 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
406
365
|
words[9] = setup.toneCurve === "positive-reinhard-per-channel" ? 1 : 0;
|
|
407
366
|
words[10] = reach;
|
|
408
367
|
words[11] = count;
|
|
409
|
-
words[16] = setup.ahdCorrection ? 1 : 0;
|
|
410
368
|
words[18] = gather.tapCount;
|
|
411
369
|
words[19] = gather.groups.length;
|
|
412
370
|
for (let c = 0; c < 3; c++)
|
|
@@ -419,7 +377,6 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
419
377
|
}
|
|
420
378
|
floats[27] = 8.5 * 8.5;
|
|
421
379
|
floats[31] = setup.clipLevel;
|
|
422
|
-
floats[35] = setup.ahdCorrection ? setup.ahdCorrection.thresholdVariance : 0;
|
|
423
380
|
for (let r = 0; r < 3; r++)
|
|
424
381
|
for (let c = 0; c < 3; c++)
|
|
425
382
|
floats[40 + r * 3 + c] = setup.matrix[r][c];
|
|
@@ -441,10 +398,6 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
441
398
|
floats[52] = Math.max(FLOAT32_MINIMUM_NORMAL, Math.fround(gather.tinyMass * (1 + 4 * E)));
|
|
442
399
|
return { params, words, ints, floats };
|
|
443
400
|
}
|
|
444
|
-
/** The reference's correction setup of a display-view setup with the AHD correction. */
|
|
445
|
-
function correctionSetup(setup) {
|
|
446
|
-
return { seed: setup.seed, readout: setup.readout, gains: setup.gains, clipLevel: setup.clipLevel, matrix: setup.matrix, toneCurve: setup.toneCurve, ahdCorrection: setup.ahdCorrection };
|
|
447
|
-
}
|
|
448
401
|
/** How far the host's quantization term Σ(w/A)²·q(m) can move with a mean bound Δm (see QUANTIZATION_SLOPE), doubled. */
|
|
449
402
|
function quantizationBound(moments, o, meanBound, pixels, step) {
|
|
450
403
|
let join = 0;
|
|
@@ -462,7 +415,7 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
462
415
|
if (reason)
|
|
463
416
|
throw new ImageKernelExecutionError("unsupported-on-backend", "webgpu-f32 display view (Quality): " + reason + "; use cpu-reference.");
|
|
464
417
|
const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
|
|
465
|
-
const timings = { lattice: 0, sites: 0, pixels: 0,
|
|
418
|
+
const timings = { lattice: 0, sites: 0, pixels: 0, finishing: 0, recompute: 0, siteDispatches: 0 };
|
|
466
419
|
const { region, margin } = setup, shape = footprintShape(setup), { reach, count } = candidatesOf(setup);
|
|
467
420
|
// #492: the defocus disc's kernels are summed in blocks (their dense rectangles), in focus the tap list runs as before.
|
|
468
421
|
const blocked = !!image || (input.blockedSum ?? (setup.gpu?.defocusRadius ?? 0) > 0);
|
|
@@ -506,19 +459,26 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
506
459
|
if (gather.table.byteLength > runtime.maximumStorageBindingBytes)
|
|
507
460
|
throw new ImageKernelExecutionError("insufficient-limits", "The display view's kernel table (" + gather.table.byteLength +
|
|
508
461
|
" bytes: 3 channels × " + gather.tapCount + " taps × " + gather.nodes + " spectral nodes, and the material spectra) exceeds the device's storage binding limit of " + runtime.maximumStorageBindingBytes + " bytes.");
|
|
509
|
-
const largestBinding = Math.max(bandBytesOf(rows), geometry.byteLength, gather.table.byteLength
|
|
462
|
+
const largestBinding = Math.max(bandBytesOf(rows), geometry.byteLength, gather.table.byteLength);
|
|
510
463
|
if (largestBinding > runtime.maximumStorageBindingBytes)
|
|
511
464
|
throw new ImageKernelExecutionError("insufficient-limits", "One display-view band exceeds the device's storage binding limit.");
|
|
512
465
|
const latticePipeline = image ? await pipelines(...image.lattice) : await pipelines("photivra-display-view-quality-lattice:" + material.id, assembleDisplayViewQualityLatticeWgsl(material.wgsl));
|
|
513
466
|
const sitesPipeline = image ? await pipelines(...image.sites) : blocked ? await pipelines("photivra-display-view-quality-sites-blocked", DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL) :
|
|
514
467
|
await pipelines("photivra-display-view-quality-sites", DISPLAY_VIEW_QUALITY_SITES_WGSL);
|
|
515
|
-
|
|
468
|
+
// #593, #601: the coupled steps' text and constants, loaded on demand for a view that asks for them.
|
|
469
|
+
const coupled = setup.rendering ? await import("./wgsl-display-view-coupled.js") : null;
|
|
470
|
+
const coupledConstants = coupled ? coupled.displayViewCoupledConstants(setup, SDR_LUMINANCE_WEIGHTS, gaussLegendre16()) : new Float32Array(1);
|
|
471
|
+
const pixelsPipeline = coupled ? await pipelines("photivra-display-view-quality-pixels+coupled", assembleDisplayViewQualityPixelsWgsl(coupled.DISPLAY_VIEW_COUPLED_WGSL))
|
|
472
|
+
: await pipelines("photivra-display-view-quality-pixels", DISPLAY_VIEW_QUALITY_PIXELS_WGSL);
|
|
516
473
|
const moments = new Float32Array(pixelCount * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS), pixelFlags = new Uint32Array(pixelCount), masks = new Uint32Array(2 * pixelCount);
|
|
517
|
-
|
|
518
|
-
let statesComputed = 0, latticePointsResolved = 0;
|
|
474
|
+
let latticePointsResolved = 0;
|
|
519
475
|
const scope = new GpuResourceScope(runtime);
|
|
520
476
|
try {
|
|
521
|
-
|
|
477
|
+
// #593, #601: the coupled steps' constants follow the aux table's own entries (one zero word without them).
|
|
478
|
+
const auxWithCoupled = new Float32Array(aux.length + coupledConstants.length);
|
|
479
|
+
auxWithCoupled.set(aux);
|
|
480
|
+
auxWithCoupled.set(coupledConstants, aux.length);
|
|
481
|
+
const auxBuffer = scope.upload("photivra-display-view-aux", auxWithCoupled, BUFFER_USAGE.STORAGE);
|
|
522
482
|
const geometryBuffer = scope.upload("photivra-display-view-geometry", geometry, BUFFER_USAGE.STORAGE);
|
|
523
483
|
// The blocked pass reads no tap records (its layout has no binding 2).
|
|
524
484
|
const tapsBuffer = blocked ? null : scope.upload("photivra-display-view-taps", gather.taps, BUFFER_USAGE.STORAGE);
|
|
@@ -611,71 +571,32 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
611
571
|
}
|
|
612
572
|
words[51] = 0;
|
|
613
573
|
timings.sites += now() - t0;
|
|
614
|
-
// Pass 3: display pixels
|
|
574
|
+
// Pass 3: display pixels.
|
|
615
575
|
words[0] = count;
|
|
616
576
|
const out = band.buffer("photivra-display-view-moments", count * 4 * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
617
577
|
const flagBuffer = band.buffer("photivra-display-view-flags", count * 4 * DISPLAY_VIEW_QUALITY_FLAG_WORDS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
timings.pixels += now() - t0;
|
|
641
|
-
const flagWords = new Uint32Array(flagData, 0, count * DISPLAY_VIEW_QUALITY_FLAG_WORDS), bandFlags = new Uint32Array(count);
|
|
642
|
-
for (let i = 0; i < count; i++)
|
|
643
|
-
bandFlags[i] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i];
|
|
644
|
-
let missing = false;
|
|
645
|
-
for (let i = 0; i < count && !missing; i++)
|
|
646
|
-
if ((bandFlags[i] & DISPLAY_VIEW_QUALITY_FLAG.stateMissing) !== 0)
|
|
647
|
-
missing = true;
|
|
648
|
-
if (missing && correction && attempt === 0) {
|
|
649
|
-
// The grid keys the band needs that the store lacks: compute them with the reference (the realm's cache serves known ones), then dispatch again.
|
|
650
|
-
t0 = now();
|
|
651
|
-
const bitmap = new Uint32Array(bitmapData, 0, DISPLAY_VIEW_QUALITY_BITMAP_WORDS), needed = [];
|
|
652
|
-
for (let w = 0; w < DISPLAY_VIEW_QUALITY_BITMAP_WORDS; w++) {
|
|
653
|
-
let bitsWord = bitmap[w];
|
|
654
|
-
while (bitsWord !== 0) {
|
|
655
|
-
const bit = 31 - Math.clz32(bitsWord);
|
|
656
|
-
bitsWord &= ~(1 << bit);
|
|
657
|
-
const key = w * 32 + bit;
|
|
658
|
-
if (key < AHD_KEY_LEVELS ** 3 && !states.has(key))
|
|
659
|
-
needed.push(key);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
if (needed.length > 0) {
|
|
663
|
-
const computed = await computeDisplayViewCorrectionStates({ ...correction, yieldControl: async () => { if (yieldControl)
|
|
664
|
-
await yieldControl(abort.signal); }, checkAbort: () => checkAbort(abort.signal) }, Int32Array.from(needed));
|
|
665
|
-
states.add(computed.gridKeys, computed.values);
|
|
666
|
-
statesComputed += needed.length;
|
|
667
|
-
}
|
|
668
|
-
timings.states += now() - t0;
|
|
669
|
-
timings.pixelRedispatches++;
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
moments.set(new Float32Array(data, 0, count * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS), first * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS);
|
|
673
|
-
pixelFlags.set(bandFlags, first);
|
|
674
|
-
for (let i = 0; i < count; i++) {
|
|
675
|
-
masks[2 * (first + i)] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 1];
|
|
676
|
-
masks[2 * (first + i) + 1] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 2];
|
|
677
|
-
}
|
|
678
|
-
break;
|
|
578
|
+
checkAbort(abort.signal);
|
|
579
|
+
t0 = now();
|
|
580
|
+
const pass = new GpuResourceScope(runtime);
|
|
581
|
+
let data, flagData;
|
|
582
|
+
try {
|
|
583
|
+
const pixelParams = pass.upload("photivra-display-view-params-pixels", new Uint8Array(params.slice(0)), BUFFER_USAGE.UNIFORM);
|
|
584
|
+
const pixelGroup = runtime.device.createBindGroup({ layout: pixelsPipeline.getBindGroupLayout(0), entries: [
|
|
585
|
+
{ binding: 0, resource: { buffer: pixelParams } }, { binding: 1, resource: { buffer: auxBuffer } }, { binding: 2, resource: { buffer: geometryBuffer } },
|
|
586
|
+
{ binding: 3, resource: { buffer: siteBuffer } }, { binding: 4, resource: { buffer: out } }, { binding: 5, resource: { buffer: flagBuffer } }
|
|
587
|
+
] });
|
|
588
|
+
[data, flagData] = await runComputePass(runtime, pass, pixelsPipeline, pixelGroup, linearDispatch(count, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), [{ buffer: out, bytes: count * 4 * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS }, { buffer: flagBuffer, bytes: count * 4 * DISPLAY_VIEW_QUALITY_FLAG_WORDS }], abort.signal);
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
pass.release();
|
|
592
|
+
}
|
|
593
|
+
timings.pixels += now() - t0;
|
|
594
|
+
const flagWords = new Uint32Array(flagData, 0, count * DISPLAY_VIEW_QUALITY_FLAG_WORDS);
|
|
595
|
+
moments.set(new Float32Array(data, 0, count * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS), first * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS);
|
|
596
|
+
for (let i = 0; i < count; i++) {
|
|
597
|
+
pixelFlags[first + i] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i];
|
|
598
|
+
masks[2 * (first + i)] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 1];
|
|
599
|
+
masks[2 * (first + i) + 1] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 2];
|
|
679
600
|
}
|
|
680
601
|
}
|
|
681
602
|
finally {
|
|
@@ -698,7 +619,7 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
698
619
|
const expected = new Float64Array(pixelCount * 3), noiseStd = new Float64Array(pixelCount * 3), saturated = new Float64Array(pixelCount);
|
|
699
620
|
const meanBound = new Float32Array(pixelCount), varianceBound = new Float32Array(pixelCount);
|
|
700
621
|
const flagged = [];
|
|
701
|
-
let largestMean = 0, largestVariance = 0, sampleCount = 0, siteCount = 0
|
|
622
|
+
let largestMean = 0, largestVariance = 0, sampleCount = 0, siteCount = 0;
|
|
702
623
|
const counts = new Int32Array(geometry.buffer);
|
|
703
624
|
// #533: each display pixel's saturated-pixel share from the kernel's mask (the reference's arithmetic, exact where it decided).
|
|
704
625
|
const columnOverlaps = Array.from({ length: region.width }, (_, i) => overlaps((region.x + i) * shape.sx, (region.x + i + 1) * shape.sx, setup.frameWidth));
|
|
@@ -724,8 +645,6 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
724
645
|
varianceBound[i] = MARGIN * moments[o + 10] + quantizationBound(moments, o, meanBound[i], pixels, step);
|
|
725
646
|
largestMean = Math.max(largestMean, meanBound[i]);
|
|
726
647
|
largestVariance = Math.max(largestVariance, varianceBound[i]);
|
|
727
|
-
if ((pixelFlags[i] & DISPLAY_VIEW_QUALITY_FLAG.corrected) !== 0)
|
|
728
|
-
corrected++;
|
|
729
648
|
const nx = counts[rx * stride + 1];
|
|
730
649
|
sampleCount += nx * ny;
|
|
731
650
|
siteCount += (nx + 2 * reach) * (ny + 2 * reach);
|
|
@@ -776,7 +695,6 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
776
695
|
varianceBound[first + k] = 0;
|
|
777
696
|
recomputed.push(first + k);
|
|
778
697
|
}
|
|
779
|
-
corrected += part.ahdCorrectedPixelCount;
|
|
780
698
|
sampleCount += part.sampleCount;
|
|
781
699
|
siteCount += part.siteCount;
|
|
782
700
|
});
|
|
@@ -788,10 +706,10 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
788
706
|
let saturatedPixelCount = 0;
|
|
789
707
|
for (let i = 0; i < pixelCount; i++)
|
|
790
708
|
saturatedPixelCount += saturated[i];
|
|
791
|
-
return { samples, expected, noiseStd, sampleCount, siteCount, detailPixelCount: pixelCount, uniformPixelCount: 0,
|
|
709
|
+
return { samples, expected, noiseStd, sampleCount, siteCount, detailPixelCount: pixelCount, uniformPixelCount: 0, estimation: "exact-area-mean", saturated, saturatedPixelCount,
|
|
792
710
|
execution: webGpuExecution(expectedValueBound, uncertain), expectedValueBound, varianceBound: { kind: "relative-to-reference", relative, absolute: largestVariance },
|
|
793
711
|
expectedValueAbsoluteBound: meanBound, varianceAbsoluteBound: varianceBound, flaggedPixelIndices: Uint32Array.from(flagged), hostRecomputedPixelIndices: Uint32Array.from(recomputed),
|
|
794
|
-
pixelFlags, highlightRecomputedPixelIndices: Uint32Array.from(highlights),
|
|
712
|
+
pixelFlags, highlightRecomputedPixelIndices: Uint32Array.from(highlights), tapsPerSite: gather.tapCount, blockedSum: blocked, siteSumRelativeBound: floats[50], latticePointsResolved, stageMilliseconds: timings };
|
|
795
713
|
}
|
|
796
714
|
/**
|
|
797
715
|
* The GPU setup the material modules read for a Quality render (#491's `createDisplayViewMaterialModules`). The composite module
|
|
@@ -822,19 +740,6 @@ export function qualityMaterialSetup(gpu) {
|
|
|
822
740
|
export function qualityMaterialModule(modules, gpu, signal) {
|
|
823
741
|
return modules.moduleFor(qualityMaterialSetup(gpu), signal);
|
|
824
742
|
}
|
|
825
|
-
/** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
|
|
826
|
-
export function offsetCount(request) { return request.boxSamples * request.boxSamples * (request.antiAliasing ? 4 : 1); }
|
|
827
|
-
/**
|
|
828
|
-
* Why the Quality renderer does not support a prepared request (`DisplayViewGpuRenderer.supports`), or true: engine charts and
|
|
829
|
-
* declared `material-layers` scenes (the material modules of `createDisplayViewMaterialModules`, #491), the identity or Reinhard
|
|
830
|
-
* tone curve and at most 64 query offsets (lattice groups). `lens.defocus` out of focus is supported (#492 step 2c: the site pass
|
|
831
|
-
* gathers the disc, or each node's diffraction kernel convolved with it, up to the reference's own caps of radius 64 and 96);
|
|
832
|
-
* whether a view's footprints take the exact area mean, whether the device passes the composite probe, and whether the kernel
|
|
833
|
-
* table fits the device's storage binding limit, is known only per render.
|
|
834
|
-
*/
|
|
835
|
-
export function displayViewQualityGpuSupports(request) {
|
|
836
|
-
return sceneToneAndOffsetsReason(request, MAXIMUM_GROUPS);
|
|
837
|
-
}
|
|
838
743
|
/** A normalized, symmetric site kernel of radius R for the self-check (a binned Gaussian of width `sigma` sites). */
|
|
839
744
|
function syntheticKernel(radius, sigma) {
|
|
840
745
|
const size = 2 * radius + 1, kernel = new Float64Array(size * size);
|
|
@@ -855,8 +760,7 @@ function syntheticKernel(radius, sigma) {
|
|
|
855
760
|
* the GPU against `renderDisplayView` within the a-priori bounds, every flagged pixel recomputed:
|
|
856
761
|
*
|
|
857
762
|
* - `display-view:quality:checkerboard-13x9-diffraction-ahd` (#479): synthetic radius-1 diffraction kernels at three spectral
|
|
858
|
-
* nodes, the AHD candidates
|
|
859
|
-
* state keep the check short), the tap-list site pass;
|
|
763
|
+
* nodes, the AHD candidates at few electrons per site, the tap-list site pass;
|
|
860
764
|
* - `display-view:quality:checkerboard-13x9-defocus-blocked` (#492): the engine's own defocus disc of 6.3 sites (radius 3) at
|
|
861
765
|
* every node, the bilinear demosaic (a defocused Speed request's route), the blocked site sum, and a tap budget that splits
|
|
862
766
|
* each band's site pass over several dispatches.
|
|
@@ -872,13 +776,11 @@ async function runQualityConformance(handle, signal, defocus) {
|
|
|
872
776
|
const signalInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: conformanceResponses(defocus ? 1 : 0.1),
|
|
873
777
|
offsets, kernels, scene, channelAt: conformanceChannelAt };
|
|
874
778
|
const setup = conformanceSetup(signalInput, { perElectron: defocus ? 1 / 40000 : 1 / 4000, darkElectrons: 0.07, constantVariance: 2.5e-7 }, 479, 5 + (disc ? disc.radius : 0), signal, defocus ? {} : { candidates: [0, 1, 2, 3].map((p) => ahdCandidateKernels(p % 2, Math.floor(p / 2))),
|
|
875
|
-
|
|
779
|
+
ahdChoice: { labMatrix: [[0.4124, 0.3576, 0.1805], [0.2126, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]].map((row) => row.map((v, c) => v * [1.1, 1, 1.6][c])) } });
|
|
876
780
|
const reference = await renderDisplayView(setup);
|
|
877
781
|
return await withConformanceAbort(runtime, signal, async (abort, pipelines) => {
|
|
878
782
|
const gather = defocus ? { blockedSum: true, maximumSiteTapsPerDispatch: 64 * 4 * (2 * 3 + 2) ** 2 } : {};
|
|
879
783
|
const gpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup, signalInput, material: chartPresetMaterialModule(chart), ...gather }, abort, undefined, pipelines);
|
|
880
|
-
if (!defocus && (gpu.ahdCorrectedPixelCount === 0 || reference.ahdCorrectedPixelCount === 0))
|
|
881
|
-
return "the self-check did not exercise the low-signal correction.";
|
|
882
784
|
if (defocus && (!gpu.blockedSum || gpu.stageMilliseconds.siteDispatches < 2))
|
|
883
785
|
return "the self-check did not exercise the blocked site sum over several dispatches.";
|
|
884
786
|
// #533: the verdict includes the saturated-pixel count (the light squares sit at the white clip).
|
|
@@ -891,97 +793,6 @@ export const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE = { id: "display-view:quality
|
|
|
891
793
|
/** #492's self-check of the defocus disc's blocked site sum (see `runQualityConformance`). */
|
|
892
794
|
export const DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE = { id: "display-view:quality:checkerboard-13x9-defocus-blocked",
|
|
893
795
|
run: (handle, signal) => runQualityConformance(handle, signal, true) };
|
|
894
|
-
/**
|
|
895
|
-
export
|
|
896
|
-
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]];
|
|
897
|
-
const average = (offsets) => offsets.map(([dx, dy]) => ({ dx: dx, dy: dy, weight: 1 / offsets.length })), self = [{ dx: 0, dy: 0, weight: 1 }];
|
|
898
|
-
return [[self, average(cross), average(diagonal)], [average(horizontal), self, average(vertical)], [average(vertical), self, average(horizontal)], [average(diagonal), average(cross), self]];
|
|
899
|
-
}
|
|
900
|
-
/**
|
|
901
|
-
* The display view's GPU renderer on one runtime (#481's `DisplayViewGpuRenderer`): the Speed kernel for requests it covers
|
|
902
|
-
* (no diffraction, no AHD, no defocus disc, at most 16 offsets) and the Quality kernels for the rest (#492: a defocused Speed
|
|
903
|
-
* request too, with its bilinear demosaic). One pipeline cache per device, one set of
|
|
904
|
-
* material modules (#491: chart presets, composite charts, the 2.3 elements and material-layers scenes; `dispose` releases their
|
|
905
|
-
* uploaded scene records), one correction-state store per correction key. `render` renders the part's region (`setup.gpu`
|
|
906
|
-
* carries the expected signal's input and the chart; a setup without it, a coarse view or a frame the kernels do not cover
|
|
907
|
-
* throws `unsupported-on-backend`), and throws `aborted` and `device-lost` as the accelerator's operations do.
|
|
908
|
-
*/
|
|
909
|
-
/** #564: the GPU refusal of a defocus kernel above the direct radius (the CPU convolves it by runs). */
|
|
910
|
-
const LARGE_DEFOCUS_REASON = "a defocus kernel radius above 64 sites (convolved by runs on the CPU, #564) is not on the GPU";
|
|
911
|
-
export function createDisplayViewGpuRenderer(runtime, options = {}) {
|
|
912
|
-
const pipelines = new Map(), stores = new Map();
|
|
913
|
-
const bandBytes = options.maximumBandBytes ?? Math.min(64 * 1024 * 1024, runtime.maximumStorageBindingBytes);
|
|
914
|
-
const source = (label, code) => {
|
|
915
|
-
let pending = pipelines.get(label);
|
|
916
|
-
if (!pending) {
|
|
917
|
-
pending = createComputePipeline(runtime, label, code, undefined);
|
|
918
|
-
pipelines.set(label, pending);
|
|
919
|
-
pending.catch(() => pipelines.delete(label));
|
|
920
|
-
}
|
|
921
|
-
return pending;
|
|
922
|
-
};
|
|
923
|
-
// #491: one set of material modules per device for both kernels (the preset module per chart; the composite module, its probe
|
|
924
|
-
// and its uploaded scene records), released by `dispose`.
|
|
925
|
-
const modules = createDisplayViewMaterialModules(runtime, source);
|
|
926
|
-
return {
|
|
927
|
-
id: "photivra-display-view",
|
|
928
|
-
supports(request) {
|
|
929
|
-
if (request.largeDefocus)
|
|
930
|
-
return LARGE_DEFOCUS_REASON;
|
|
931
|
-
// #492: out of focus the expected signal carries the defocus disc as site kernels, which only the Quality site pass gathers;
|
|
932
|
-
// a Speed request then runs the Quality passes with its own bilinear demosaic (one candidate) and finishing.
|
|
933
|
-
if (request.defocus)
|
|
934
|
-
return displayViewQualityGpuSupports(request);
|
|
935
|
-
const speed = displayViewSpeedGpuSupports(request);
|
|
936
|
-
if (speed === true)
|
|
937
|
-
return true;
|
|
938
|
-
return displayViewQualityGpuSupports(request);
|
|
939
|
-
},
|
|
940
|
-
conformanceCases: [DISPLAY_VIEW_SPEED_CONFORMANCE_CASE, DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE, DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE],
|
|
941
|
-
async render(part) {
|
|
942
|
-
const { setup, signal } = part;
|
|
943
|
-
if (!setup.gpu)
|
|
944
|
-
throw new ImageKernelExecutionError("unsupported-on-backend", "The display-view setup carries no GPU inputs (setup.gpu); use cpu-reference.");
|
|
945
|
-
const abort = new AbortController(), onAbort = () => abort.abort();
|
|
946
|
-
if (signal?.aborted)
|
|
947
|
-
throw abortedError();
|
|
948
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
949
|
-
try {
|
|
950
|
-
const signalInput = setup.gpu.signalInput;
|
|
951
|
-
let result;
|
|
952
|
-
let material;
|
|
953
|
-
if (displayViewSpeedGpuUnsupportedReason(setup, signalInput) === null) {
|
|
954
|
-
material = await modules.moduleFor(setup.gpu, abort.signal);
|
|
955
|
-
result = await renderDisplayViewSpeedOnGpu(runtime, bandBytes, { setup, signalInput, material }, abort, options.yieldControl, source);
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
// #581: an image-plane print brings its own passes 1 and 2.
|
|
959
|
-
const image = signalInput.scene.image ? await modules.imagePasses(setup.gpu, abort.signal) : undefined;
|
|
960
|
-
material = image ? { id: "image-plane", wgsl: "", bindings: [] } : await qualityMaterialModule(modules, setup.gpu, abort.signal);
|
|
961
|
-
const key = displayViewCorrectionKey(setup);
|
|
962
|
-
let states = key === null ? undefined : stores.get(key);
|
|
963
|
-
if (key !== null && !states) {
|
|
964
|
-
states = new CorrectionStateStore();
|
|
965
|
-
stores.set(key, states);
|
|
966
|
-
}
|
|
967
|
-
result = await renderDisplayViewQualityOnGpu(runtime, bandBytes, { setup, signalInput, material, ...(image ? { image } : {}), ...(states ? { states } : {}) }, abort, options.yieldControl, source);
|
|
968
|
-
}
|
|
969
|
-
return { samples: result.samples, expected: result.expected, noiseStd: result.noiseStd, sampleCount: result.sampleCount, siteCount: result.siteCount, detailPixelCount: result.detailPixelCount,
|
|
970
|
-
uniformPixelCount: result.uniformPixelCount, ahdCorrectedPixelCount: result.ahdCorrectedPixelCount, estimation: result.estimation, saturated: result.saturated,
|
|
971
|
-
saturatedPixelCount: result.saturatedPixelCount, errorBound: result.expectedValueBound,
|
|
972
|
-
diagnosticUncertaintySampleCount: result.execution.diagnosticUncertaintySampleCount, ...(material.compositeProbe ? { compositeProbe: material.compositeProbe } : {}) };
|
|
973
|
-
}
|
|
974
|
-
finally {
|
|
975
|
-
signal?.removeEventListener("abort", onAbort);
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
dispose() { modules.release(); stores.clear(); }
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
/** The factory `prepareDisplayView` creates the renderer with (one per device): the Speed and Quality kernels behind one renderer. */
|
|
982
|
-
export const displayViewGpuRendererFactory = (handle) => createDisplayViewGpuRenderer(handle);
|
|
983
|
-
/** Registers the combined renderer as the display view's GPU renderer and returns the factory registered before. */
|
|
984
|
-
export function registerDisplayViewGpuRenderer() {
|
|
985
|
-
return setDisplayViewGpuRendererFactory(displayViewGpuRendererFactory);
|
|
986
|
-
}
|
|
796
|
+
/** #594: the Quality kernels' self-check cases, run before the first render that takes the Quality passes (`onDemandConformanceCases`). */
|
|
797
|
+
export const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASES = Object.freeze([DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE, DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE]);
|
|
987
798
|
//# sourceMappingURL=display-view-quality-gpu.js.map
|