@photivra/engine 2.20.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 +295 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +18 -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-lens-correction-opcodes.d.ts +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
- package/dist/capture/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/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/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-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +41 -73
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/capture/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +12 -3
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/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/equipment/generic-lens-catalogue.d.ts +3 -2
- package/dist/equipment/generic-lens-catalogue.js +18 -5
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +16 -11
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +28 -3
- package/dist/facade/catalogue-lens-signal.js +83 -78
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/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 +39 -5
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +19 -6
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.js +25 -23
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view-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 +152 -499
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +3 -3
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +46 -9
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +3 -3
- package/dist/facade/generic-lens-capture.js +3 -3
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +384 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/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/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +145 -4
- package/dist/facade/scene-radiance.js +50 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +68 -20
- package/dist/facade/simulate-capture.js +399 -146
- 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 +70 -53
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-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 +87 -0
- package/dist/gpu/display-view-image-material-module.js +361 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +87 -0
- package/dist/gpu/display-view-image-quality.js +625 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +80 -65
- package/dist/gpu/display-view-quality-gpu.js +187 -445
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +67 -19
- package/dist/gpu/display-view-speed-gpu.js +181 -105
- 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 +33 -24
- package/dist/gpu/wgsl-display-view-quality-kernel.js +166 -146
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +19 -44
- package/dist/gpu/wgsl-display-view-speed-kernel.js +116 -18
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +22 -6
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.js +54 -46
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/output/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/developed-lens-corrections.d.ts +19 -5
- package/dist/output/developed-lens-corrections.js +28 -9
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -5
- package/dist/output/generic-lens-corrections.js +52 -8
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/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/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/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 +91 -13
- package/dist/sensor/sensor-characteristics.js +30 -15
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +214 -4
- package/docs/QUICK_START.md +162 -8
- package/docs/USAGE.md +195 -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/equipment-generic-lens-catalogue.md +10 -5
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +460 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +47 -10
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +48 -11
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -4
- package/docs/api/output-print-plan.md +35 -1
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +3 -0
- package/package.json +6 -5
|
@@ -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,26 +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 { resolvePortableChartScene } from "../facade/chart-scenes.js";
|
|
31
|
-
import { createDisplayViewMaterialModules } from "./display-view-composite-material-module.js";
|
|
32
|
-
import { setDisplayViewGpuRendererFactory } from "../facade/display-view-gpu.js";
|
|
33
30
|
import { chartPresetMaterialModule } from "./display-view-chart-material-module.js";
|
|
34
|
-
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";
|
|
35
32
|
import { createHostYieldGate } from "../capture/host-yield-cadence.js";
|
|
36
33
|
import { createDefocusDiscSiteKernel } from "../optics/defocus-disc-kernel.js";
|
|
37
34
|
import { ImageKernelExecutionError, WEBGPU_F32_UNIT_ROUNDOFF, webGpuExecution } from "./image-kernel-execution.js";
|
|
38
|
-
import { BUFFER_USAGE, GpuResourceScope,
|
|
39
|
-
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";
|
|
40
38
|
/** One WGSL rounding of + − × in either direction (W3C WGSL "Floating Point Accuracy"; #471, #487): twice the binary32 unit roundoff. */
|
|
41
39
|
const E = 2 * WEBGPU_F32_UNIT_ROUNDOFF;
|
|
42
40
|
/** Every reported bound is doubled: the kernel's running bounds are first order, and their own float32 arithmetic rounds. */
|
|
@@ -62,7 +60,7 @@ export const DISPLAY_VIEW_QUALITY_MAXIMUM_KERNEL_RADIUS = 96;
|
|
|
62
60
|
* several invocations per site is needed.
|
|
63
61
|
*/
|
|
64
62
|
export const DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH = 2 ** 30;
|
|
65
|
-
/** The smallest positive normal float32:
|
|
63
|
+
/** The smallest positive normal float32: values below it may be flushed to zero by a device (WGSL allows it). */
|
|
66
64
|
const FLOAT32_MINIMUM_NORMAL = 2 ** -126;
|
|
67
65
|
/** Candidate table entries per phase: the (2·reach + 1)² window. */
|
|
68
66
|
const WINDOW_SLOTS = (2 * MAXIMUM_REACH + 1) ** 2;
|
|
@@ -76,37 +74,6 @@ const QUANTIZATION_SLOPE = 6.04, QUANTIZATION_JOIN_JUMP = 2.1e-4, QUANTIZATION_J
|
|
|
76
74
|
/** Bytes per display pixel in the output buffers (moments plus the flag word). */
|
|
77
75
|
const OUTPUT_BYTES_PER_PIXEL = 4 * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS + 4 * DISPLAY_VIEW_QUALITY_FLAG_WORDS;
|
|
78
76
|
const SITE_BYTES = 4 * DISPLAY_VIEW_QUALITY_SITE_FLOATS;
|
|
79
|
-
/**
|
|
80
|
-
* The low-signal correction states a renderer holds for one correction key, as the GPU table reads them: grid keys ascending
|
|
81
|
-
* with three float32 values each (the host keeps the float64 values; the table is their single rounding).
|
|
82
|
-
*/
|
|
83
|
-
export class CorrectionStateStore {
|
|
84
|
-
#known = new Map();
|
|
85
|
-
#keys = new Int32Array(0);
|
|
86
|
-
#values = new Float32Array(0);
|
|
87
|
-
#dirty = false;
|
|
88
|
-
get size() { return this.#known.size; }
|
|
89
|
-
has(key) { return this.#known.has(key); }
|
|
90
|
-
add(gridKeys, values) {
|
|
91
|
-
for (let i = 0; i < gridKeys.length; i++) {
|
|
92
|
-
if (this.#known.has(gridKeys[i]))
|
|
93
|
-
continue;
|
|
94
|
-
this.#known.set(gridKeys[i], [values[3 * i], values[3 * i + 1], values[3 * i + 2]]);
|
|
95
|
-
this.#dirty = true;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/** The sorted table (rebuilt when states were added since the last call). */
|
|
99
|
-
table() {
|
|
100
|
-
if (this.#dirty) {
|
|
101
|
-
const keys = Int32Array.from(this.#known.keys()).sort(), values = new Float32Array(3 * keys.length);
|
|
102
|
-
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]; });
|
|
103
|
-
this.#keys = keys;
|
|
104
|
-
this.#values = values;
|
|
105
|
-
this.#dirty = false;
|
|
106
|
-
}
|
|
107
|
-
return { keys: this.#keys, values: this.#values };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
77
|
/**
|
|
111
78
|
* Waits until the queue has finished the work submitted so far (`GPUQueue.onSubmittedWorkDone`), so a pass that reads nothing
|
|
112
79
|
* back is timed with its own execution, not the next pass's wait. The passes depend on each other in order anyway.
|
|
@@ -116,17 +83,6 @@ async function queueIdle(runtime, signal) {
|
|
|
116
83
|
if (typeof queue.onSubmittedWorkDone === "function")
|
|
117
84
|
await runtime.guard(queue.onSubmittedWorkDone(), signal);
|
|
118
85
|
}
|
|
119
|
-
/** The footprint shape of a view: real pixels per display pixel along each axis, and whether the exact area mean applies. */
|
|
120
|
-
export function footprintShape(setup) {
|
|
121
|
-
const sx = setup.frameWidth / setup.viewWidth, sy = setup.frameHeight / setup.viewHeight, area = sx * sy;
|
|
122
|
-
const exact = area <= Math.min(EXACT_AREA_MEAN_MAX_PIXELS, setup.samplesPerAxis * setup.samplesPerAxis);
|
|
123
|
-
let maximumColumns = 0, maximumRows = 0;
|
|
124
|
-
for (let i = 0; i < setup.region.width; i++)
|
|
125
|
-
maximumColumns = Math.max(maximumColumns, overlaps((setup.region.x + i) * sx, (setup.region.x + i + 1) * sx, setup.frameWidth).w.length);
|
|
126
|
-
for (let j = 0; j < setup.region.height; j++)
|
|
127
|
-
maximumRows = Math.max(maximumRows, overlaps((setup.region.y + j) * sy, (setup.region.y + j + 1) * sy, setup.frameHeight).w.length);
|
|
128
|
-
return { sx, sy, exact, maximumColumns, maximumRows };
|
|
129
|
-
}
|
|
130
86
|
/** The demosaic candidates of a setup (the bilinear kernels when it has none) and their reach in sites. */
|
|
131
87
|
function candidatesOf(setup) {
|
|
132
88
|
const candidates = setup.candidates ?? setup.demosaic.map((phase) => [phase]), count = candidates[0]?.length ?? 0;
|
|
@@ -148,22 +104,26 @@ function exactLatticeConstant(c, span) {
|
|
|
148
104
|
/**
|
|
149
105
|
* Why the Quality kernels do not cover a request, or null when they do: chart materials, the exact area mean (footprints of
|
|
150
106
|
* at most 25 real pixels and 6 per axis), the identity or positive Reinhard tone curve, demosaic candidates within 3 sites
|
|
151
|
-
* (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
|
|
152
108
|
* 2·reach + 2 sites on each axis (so a reflected border site lies in the band's window).
|
|
153
109
|
*/
|
|
154
110
|
export function displayViewQualityGpuUnsupportedReason(setup, signalInput) {
|
|
155
111
|
if (signalInput.largeKernel)
|
|
156
112
|
return LARGE_DEFOCUS_REASON;
|
|
157
|
-
if (signalInput.scene.kind !== "materials")
|
|
113
|
+
if (signalInput.scene.kind !== "materials" && !signalInput.scene.image)
|
|
158
114
|
return "radiance scenes (caller functions) are not on the GPU";
|
|
159
115
|
if (!signalInput.kernels && !signalInput.queryPoint)
|
|
160
116
|
return "the expected signal has no query points";
|
|
161
|
-
if (setup.
|
|
162
|
-
return "a collect pass of the correction states runs on the CPU";
|
|
163
|
-
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))
|
|
164
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";
|
|
165
123
|
if (setup.region.width < 1 || setup.region.height < 1)
|
|
166
124
|
return "an empty region";
|
|
125
|
+
if (signalInput.wavelengthsNm.length < 1 || signalInput.wavelengthsNm.length > DISPLAY_VIEW_QUALITY_MAXIMUM_NODES)
|
|
126
|
+
return "the site passes integrate 1 to " + DISPLAY_VIEW_QUALITY_MAXIMUM_NODES + " spectral nodes per site";
|
|
167
127
|
if (signalInput.kernels && (signalInput.kernels.radius < 0 || signalInput.kernels.radius > DISPLAY_VIEW_QUALITY_MAXIMUM_KERNEL_RADIUS))
|
|
168
128
|
return "a site kernel radius above " + DISPLAY_VIEW_QUALITY_MAXIMUM_KERNEL_RADIUS + " (the diffraction and defocus caps)";
|
|
169
129
|
const shape = footprintShape(setup);
|
|
@@ -209,9 +169,9 @@ export function displayViewQualityGpuUnsupportedReason(setup, signalInput) {
|
|
|
209
169
|
}
|
|
210
170
|
return null;
|
|
211
171
|
}
|
|
212
|
-
function signalGather(signalInput
|
|
213
|
-
const tables = displayViewSignalTables(signalInput),
|
|
214
|
-
const W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0, materials =
|
|
172
|
+
function signalGather(signalInput) {
|
|
173
|
+
const tables = displayViewSignalTables(signalInput), A = signalInput.electronsPerRadiance, N = signalInput.wavelengthsNm.length, spectra = tables.spectra;
|
|
174
|
+
const W = signalInput.nativeWidth, H = signalInput.nativeHeight, R = signalInput.kernels ? signalInput.kernels.radius : 0, materials = spectra.length;
|
|
215
175
|
// Taps in order: per group, with site kernels the dense rectangle row-major (tap (dx, dy) reads lattice offset (minIx − R + dx,
|
|
216
176
|
// minIy − R + dy)), without them one tap per member. Plain loops: a radius-96 kernel has up to 150 544 taps.
|
|
217
177
|
const tapGroup = [], tapDp = [], tapDq = [], tapIndex = [];
|
|
@@ -239,7 +199,6 @@ function signalGather(signalInput, factored) {
|
|
|
239
199
|
groups.push({ dpMin, dqMin, dpSpan: dpMax - dpMin, dqSpan: dqMax - dqMin, cu: 0.5 + group.fx - W / 2, cv: 0.5 + group.fy - H / 2, firstTap: first,
|
|
240
200
|
cw: signalInput.kernels ? group.cw : 0, ch: signalInput.kernels ? group.ch : 0 });
|
|
241
201
|
});
|
|
242
|
-
const direct = (m, c) => { const si = tables.spectralIndex[m]; return si < 0 ? tables.reflectance[m] * tables.neutralDirect[c] : tables.spectralDirect[c][si]; };
|
|
243
202
|
const T = tapGroup.length, taps = new Uint32Array(T * DISPLAY_VIEW_QUALITY_TAP_WORDS), spread = new Float32Array(3 * T);
|
|
244
203
|
for (let t = 0; t < T; t++) {
|
|
245
204
|
const g = tapGroup[t], group = groups[g];
|
|
@@ -247,91 +206,64 @@ function signalGather(signalInput, factored) {
|
|
|
247
206
|
taps[t * DISPLAY_VIEW_QUALITY_TAP_WORDS + 1] = tapDp[t] - group.dpMin;
|
|
248
207
|
taps[t * DISPLAY_VIEW_QUALITY_TAP_WORDS + 2] = tapDq[t] - group.dqMin;
|
|
249
208
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const r = check(tables.reflectance[m]);
|
|
266
|
-
materialRows[2 * m] = 0;
|
|
267
|
-
reflectanceBits[2 * m + 1] = r;
|
|
268
|
-
neutralLow = Math.min(neutralLow, r);
|
|
269
|
-
neutralHigh = Math.max(neutralHigh, r);
|
|
270
|
-
if (r > 0)
|
|
271
|
-
neutralSmallest = Math.min(neutralSmallest, r);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
materialRows[2 * m] = 1 + si;
|
|
275
|
-
reflectanceBits[2 * m + 1] = 1;
|
|
276
|
-
}
|
|
209
|
+
// Every value finite and non-negative, every spectrum value normal (a device may flush the others) and every sum in range.
|
|
210
|
+
const domain = () => { throw new ImageKernelExecutionError("unsupported-float32-domain", "A site's spectral terms are outside the float32 domain."); };
|
|
211
|
+
// The spectra and their envelope per node.
|
|
212
|
+
const base = 3 * T * N, table = new Float32Array(base + materials * N), low = new Float64Array(N).fill(Infinity), high = new Float64Array(N).fill(-Infinity);
|
|
213
|
+
let smallestL = Infinity;
|
|
214
|
+
spectra.forEach((L, m) => {
|
|
215
|
+
for (let n = 0; n < N; n++) {
|
|
216
|
+
const l = L[n];
|
|
217
|
+
if (!(l >= 0 && l <= FLOAT32_SAFE_MAGNITUDE))
|
|
218
|
+
domain();
|
|
219
|
+
table[base + m * N + n] = l;
|
|
220
|
+
low[n] = Math.min(low[n], l);
|
|
221
|
+
high[n] = Math.max(high[n], l);
|
|
222
|
+
if (l > 0)
|
|
223
|
+
smallestL = Math.min(smallestL, l);
|
|
277
224
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const white = check(source.neutral[c][index]);
|
|
283
|
-
table[c * T + t] = white;
|
|
284
|
-
if (neutralHigh >= neutralLow) {
|
|
285
|
-
low = neutralLow * white;
|
|
286
|
-
high = neutralHigh * white;
|
|
287
|
-
// A neutral term r·N below the float32 normal range may be flushed: at most min(r_max·N, 2⁻¹²⁶).
|
|
288
|
-
if (neutralSmallest * white < FLOAT32_MINIMUM_NORMAL && white > 0)
|
|
289
|
-
tiny[t] = Math.max(tiny[t], Math.min(neutralHigh * white, FLOAT32_MINIMUM_NORMAL));
|
|
290
|
-
}
|
|
291
|
-
for (let si = 0; si < spectralCount; si++) {
|
|
292
|
-
const value = check(source.spectral[c][si][index]);
|
|
293
|
-
table[((1 + si) * 3 + c) * T + t] = value;
|
|
294
|
-
low = Math.min(low, value);
|
|
295
|
-
high = Math.max(high, value);
|
|
296
|
-
if (value > 0 && value < FLOAT32_MINIMUM_NORMAL)
|
|
297
|
-
tiny[t] = Math.max(tiny[t], value);
|
|
298
|
-
}
|
|
299
|
-
spread[c * T + t] = Math.fround((high - low) * (1 + 4 * E));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
let tinyMass = 0;
|
|
303
|
-
for (let t = 0; t < T; t++)
|
|
304
|
-
tinyMass += tiny[t];
|
|
305
|
-
return { groups, taps, tapCount: T, table, materialRows, spread, materials, tinyMass };
|
|
306
|
-
}
|
|
307
|
-
const table = new Float32Array(materials * 3 * T);
|
|
225
|
+
});
|
|
226
|
+
if (smallestL < FLOAT32_MINIMUM_NORMAL)
|
|
227
|
+
domain();
|
|
228
|
+
let largestSum = 0, tinyMass = (T + 2) * N * FLOAT32_MINIMUM_NORMAL;
|
|
308
229
|
for (let t = 0; t < T; t++) {
|
|
309
230
|
const source = tables.groups[tapGroup[t]], index = tapIndex[t];
|
|
231
|
+
let tiny = 0;
|
|
310
232
|
for (let c = 0; c < 3; c++) {
|
|
311
|
-
let
|
|
312
|
-
for (let
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
233
|
+
let range = 0, mass = 0, tinyHere = 0;
|
|
234
|
+
for (let n = 0; n < N; n++) {
|
|
235
|
+
const value = signalInput.kernels ? source.nodeKernels[c][index * N + n] : source.members[index].weight * A[c][n];
|
|
236
|
+
if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE))
|
|
237
|
+
domain();
|
|
238
|
+
table[(c * T + t) * N + n] = value;
|
|
239
|
+
if (materials > 0) {
|
|
240
|
+
const top = value * high[n];
|
|
241
|
+
range += value * (high[n] - low[n]);
|
|
242
|
+
mass += top;
|
|
243
|
+
// A kernel value or product below the normal range may be flushed: at most the product with the largest spectrum.
|
|
244
|
+
if (value > 0 && (value < FLOAT32_MINIMUM_NORMAL || top < 2 * FLOAT32_MINIMUM_NORMAL))
|
|
245
|
+
tinyHere += top;
|
|
246
|
+
}
|
|
320
247
|
}
|
|
321
|
-
// The spread rounded up (
|
|
322
|
-
spread[c * T + t] = Math.fround(
|
|
248
|
+
// The spread rounded up (the float32 roundings of the operands, the products and the sum).
|
|
249
|
+
spread[c * T + t] = Math.fround(range * (1 + (N + 4) * E));
|
|
250
|
+
largestSum = Math.max(largestSum, mass);
|
|
251
|
+
tiny = Math.max(tiny, tinyHere);
|
|
323
252
|
}
|
|
253
|
+
tinyMass += tiny;
|
|
324
254
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return { groups, taps, tapCount: T, table, materialRows: null, spread, materials, tinyMass };
|
|
255
|
+
if (!(largestSum * T <= FLOAT32_SAFE_MAGNITUDE))
|
|
256
|
+
domain();
|
|
257
|
+
return { groups, taps, tapCount: T, nodes: N, table, spread, materials, tinyMass };
|
|
329
258
|
}
|
|
330
259
|
/**
|
|
331
260
|
* The relative bound of a site's electrons (`params.site_gamma`): (T + 3)·E for the tap-list sum (#479), and with the blocked sum
|
|
332
|
-
* (#492) (cw_max + ch_max + G + 3)·E
|
|
333
|
-
*
|
|
334
|
-
*
|
|
261
|
+
* (#492) (cw_max + ch_max + G + 3)·E, both unchanged by 2.21.0. Every term C_c(t, n)·L_m(λ_n) is non-negative and carries two host
|
|
262
|
+
* roundings (u = E/2 each: the kernel value and the spectrum) and its product (E); the tap-list pass adds it into its node's
|
|
263
|
+
* partial over the T taps (at most T − 1 inexact additions), the blocked pass into a row, group and total partial per node (at
|
|
264
|
+
* most cw − 1 + ch − 1 + G − 1). The N node partials and the dark electrons (host-rounded, E/2) are then summed in integer
|
|
265
|
+
* arithmetic and rounded once (E, `EXACT_SUM_WGSL`). So a site carries at most (T + 2.5)·E, or (cw + ch + G + 0.5)·E, inside the
|
|
266
|
+
* bounds (docs/WEBGPU.md).
|
|
335
267
|
*/
|
|
336
268
|
function siteSumRelativeBound(gather, blocked) {
|
|
337
269
|
if (!blocked)
|
|
@@ -406,8 +338,7 @@ function resolveUncertainLattice(lattice, groupWords, signalInput) {
|
|
|
406
338
|
if ((raw >>> 31) === 0)
|
|
407
339
|
continue;
|
|
408
340
|
const p = lx0 + (local % lw), q = ly0 + Math.floor(local / lw);
|
|
409
|
-
const
|
|
410
|
-
const m = scene.materialAt(u, v);
|
|
341
|
+
const m = scene.materialAt(latticeCoordinate(p, group.fx, W, g), -latticeCoordinate(q, group.fy, H, g));
|
|
411
342
|
if (!(Number.isInteger(m) && m >= 0 && m < scene.materials.length))
|
|
412
343
|
throw new ImageKernelExecutionError("unsupported-on-backend", "webgpu-f32 display view (Quality): a lattice point has no scene material; use cpu-reference.");
|
|
413
344
|
lattice[first + local] = m;
|
|
@@ -416,6 +347,11 @@ function resolveUncertainLattice(lattice, groupWords, signalInput) {
|
|
|
416
347
|
});
|
|
417
348
|
return resolved;
|
|
418
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* One axis of the reference's lattice point (createDisplayViewExpectedSignal's latticeU / latticeV, written as it writes them):
|
|
352
|
+
* (i + 0.5 − size/2)·g with no shift, (i + 0.5 + f − size/2)·g otherwise; v is its negative.
|
|
353
|
+
*/
|
|
354
|
+
export const latticeCoordinate = (i, f, size, g) => (f === 0 ? i + 0.5 - size / 2 : i + 0.5 + f - size / 2) * g;
|
|
419
355
|
/** The uniform parameters shared by the three passes (per-pass words are set per dispatch). */
|
|
420
356
|
function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
421
357
|
const params = new ArrayBuffer(4 * DISPLAY_VIEW_QUALITY_PARAM_WORDS), words = new Uint32Array(params), ints = new Int32Array(params), floats = new Float32Array(params);
|
|
@@ -429,7 +365,6 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
429
365
|
words[9] = setup.toneCurve === "positive-reinhard-per-channel" ? 1 : 0;
|
|
430
366
|
words[10] = reach;
|
|
431
367
|
words[11] = count;
|
|
432
|
-
words[16] = setup.ahdCorrection ? 1 : 0;
|
|
433
368
|
words[18] = gather.tapCount;
|
|
434
369
|
words[19] = gather.groups.length;
|
|
435
370
|
for (let c = 0; c < 3; c++)
|
|
@@ -442,7 +377,6 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
442
377
|
}
|
|
443
378
|
floats[27] = 8.5 * 8.5;
|
|
444
379
|
floats[31] = setup.clipLevel;
|
|
445
|
-
floats[35] = setup.ahdCorrection ? setup.ahdCorrection.thresholdVariance : 0;
|
|
446
380
|
for (let r = 0; r < 3; r++)
|
|
447
381
|
for (let c = 0; c < 3; c++)
|
|
448
382
|
floats[40 + r * 3 + c] = setup.matrix[r][c];
|
|
@@ -458,15 +392,12 @@ function constants(setup, signalInput, gather, reach, count, blocked) {
|
|
|
458
392
|
floats[49] = geometry.scaleV;
|
|
459
393
|
}
|
|
460
394
|
floats[50] = siteSumRelativeBound(gather, blocked);
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
395
|
+
// 2.21.0: the spectral nodes the site passes integrate.
|
|
396
|
+
words[53] = gather.nodes;
|
|
397
|
+
// What a flushing device can drop (both site passes), rounded up and kept normal so the uniform is not flushed either.
|
|
398
|
+
floats[52] = Math.max(FLOAT32_MINIMUM_NORMAL, Math.fround(gather.tinyMass * (1 + 4 * E)));
|
|
464
399
|
return { params, words, ints, floats };
|
|
465
400
|
}
|
|
466
|
-
/** The reference's correction setup of a display-view setup with the AHD correction. */
|
|
467
|
-
function correctionSetup(setup) {
|
|
468
|
-
return { seed: setup.seed, readout: setup.readout, gains: setup.gains, clipLevel: setup.clipLevel, matrix: setup.matrix, toneCurve: setup.toneCurve, ahdCorrection: setup.ahdCorrection };
|
|
469
|
-
}
|
|
470
401
|
/** How far the host's quantization term Σ(w/A)²·q(m) can move with a mean bound Δm (see QUANTIZATION_SLOPE), doubled. */
|
|
471
402
|
function quantizationBound(moments, o, meanBound, pixels, step) {
|
|
472
403
|
let join = 0;
|
|
@@ -479,19 +410,24 @@ function quantizationBound(moments, o, meanBound, pixels, step) {
|
|
|
479
410
|
}
|
|
480
411
|
/** Renders one region of a Quality display view on the GPU (see the module comment). */
|
|
481
412
|
export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, abort, yieldControl, pipelines) {
|
|
482
|
-
const { setup, signalInput, material } = input;
|
|
413
|
+
const { setup, signalInput, material, image } = input;
|
|
483
414
|
const reason = displayViewQualityGpuUnsupportedReason(setup, signalInput);
|
|
484
415
|
if (reason)
|
|
485
416
|
throw new ImageKernelExecutionError("unsupported-on-backend", "webgpu-f32 display view (Quality): " + reason + "; use cpu-reference.");
|
|
486
417
|
const now = () => (typeof performance !== "undefined" ? performance.now() : Date.now());
|
|
487
|
-
const timings = { lattice: 0, sites: 0, pixels: 0,
|
|
418
|
+
const timings = { lattice: 0, sites: 0, pixels: 0, finishing: 0, recompute: 0, siteDispatches: 0 };
|
|
488
419
|
const { region, margin } = setup, shape = footprintShape(setup), { reach, count } = candidatesOf(setup);
|
|
489
420
|
// #492: the defocus disc's kernels are summed in blocks (their dense rectangles), in focus the tap list runs as before.
|
|
490
|
-
const blocked = input.blockedSum ?? (setup.gpu?.defocusRadius ?? 0) > 0;
|
|
491
|
-
if (blocked && !signalInput.kernels)
|
|
421
|
+
const blocked = !!image || (input.blockedSum ?? (setup.gpu?.defocusRadius ?? 0) > 0);
|
|
422
|
+
if (blocked && !signalInput.kernels && !image)
|
|
492
423
|
throw new ImageKernelExecutionError("unsupported-on-backend", "webgpu-f32 display view (Quality): the blocked site sum needs site kernels; use cpu-reference.");
|
|
493
424
|
const tapBudget = input.maximumSiteTapsPerDispatch ?? DISPLAY_VIEW_QUALITY_SITE_TAPS_PER_DISPATCH;
|
|
494
|
-
const gather = signalGather(signalInput
|
|
425
|
+
const gather = image?.gather ?? signalGather(signalInput), aux = auxTable(setup), { params, words, ints, floats } = constants(setup, signalInput, gather, reach, count, blocked);
|
|
426
|
+
// #581: the print's site bound and its radiance values per lattice point (the site pass's node count in the last word).
|
|
427
|
+
if (image) {
|
|
428
|
+
floats[50] = image.siteGamma;
|
|
429
|
+
words[55] = image.pointFloats;
|
|
430
|
+
}
|
|
495
431
|
const stride = DISPLAY_VIEW_QUALITY_GEOMETRY_STRIDE, geometry = new Float32Array((region.width + region.height) * stride);
|
|
496
432
|
const columnSums = [], rowSums = [];
|
|
497
433
|
for (let i = 0; i < region.width; i++) {
|
|
@@ -512,7 +448,7 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
512
448
|
const { y0, y1 } = siteRows(0, rows), siteH = y1 - y0 + 1;
|
|
513
449
|
let lattice = 0;
|
|
514
450
|
for (const g of gather.groups)
|
|
515
|
-
lattice += (siteW + g.dpSpan) * (siteH + g.dqSpan) * 4;
|
|
451
|
+
lattice += (siteW + g.dpSpan) * (siteH + g.dqSpan) * 4 * (image ? image.pointFloats + 1 : 1);
|
|
516
452
|
return lattice + siteW * siteH * SITE_BYTES + rows * region.width * OUTPUT_BYTES_PER_PIXEL;
|
|
517
453
|
};
|
|
518
454
|
let rows = region.height;
|
|
@@ -521,32 +457,36 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
521
457
|
// The value table (materials × 3 channels × taps) is one binding: with a large defocus kernel and many materials it can pass the
|
|
522
458
|
// device's limit, which is then refused by name (never clipped).
|
|
523
459
|
if (gather.table.byteLength > runtime.maximumStorageBindingBytes)
|
|
524
|
-
throw new ImageKernelExecutionError("insufficient-limits", "The display view's kernel table (" +
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const largestBinding = Math.max(bandBytesOf(rows), geometry.byteLength, gather.table.byteLength, 4 * DISPLAY_VIEW_QUALITY_BITMAP_WORDS);
|
|
460
|
+
throw new ImageKernelExecutionError("insufficient-limits", "The display view's kernel table (" + gather.table.byteLength +
|
|
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.");
|
|
462
|
+
const largestBinding = Math.max(bandBytesOf(rows), geometry.byteLength, gather.table.byteLength);
|
|
528
463
|
if (largestBinding > runtime.maximumStorageBindingBytes)
|
|
529
464
|
throw new ImageKernelExecutionError("insufficient-limits", "One display-view band exceeds the device's storage binding limit.");
|
|
530
|
-
const latticePipeline = await pipelines("photivra-display-view-quality-lattice:" + material.id, assembleDisplayViewQualityLatticeWgsl(material.wgsl));
|
|
531
|
-
const sitesPipeline = blocked ? await pipelines("photivra-display-view-quality-sites-blocked", DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL) :
|
|
465
|
+
const latticePipeline = image ? await pipelines(...image.lattice) : await pipelines("photivra-display-view-quality-lattice:" + material.id, assembleDisplayViewQualityLatticeWgsl(material.wgsl));
|
|
466
|
+
const sitesPipeline = image ? await pipelines(...image.sites) : blocked ? await pipelines("photivra-display-view-quality-sites-blocked", DISPLAY_VIEW_QUALITY_SITES_BLOCKED_WGSL) :
|
|
532
467
|
await pipelines("photivra-display-view-quality-sites", DISPLAY_VIEW_QUALITY_SITES_WGSL);
|
|
533
|
-
|
|
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);
|
|
534
473
|
const moments = new Float32Array(pixelCount * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS), pixelFlags = new Uint32Array(pixelCount), masks = new Uint32Array(2 * pixelCount);
|
|
535
|
-
|
|
536
|
-
let statesComputed = 0, latticePointsResolved = 0;
|
|
474
|
+
let latticePointsResolved = 0;
|
|
537
475
|
const scope = new GpuResourceScope(runtime);
|
|
538
476
|
try {
|
|
539
|
-
|
|
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);
|
|
540
482
|
const geometryBuffer = scope.upload("photivra-display-view-geometry", geometry, BUFFER_USAGE.STORAGE);
|
|
541
483
|
// The blocked pass reads no tap records (its layout has no binding 2).
|
|
542
484
|
const tapsBuffer = blocked ? null : scope.upload("photivra-display-view-taps", gather.taps, BUFFER_USAGE.STORAGE);
|
|
543
485
|
const tableBuffer = scope.upload("photivra-display-view-table", gather.table, BUFFER_USAGE.STORAGE);
|
|
544
486
|
const spreadBuffer = scope.upload("photivra-display-view-spread", gather.spread, BUFFER_USAGE.STORAGE);
|
|
545
|
-
const materialRowsBuffer = gather.materialRows ? scope.upload("photivra-display-view-material-rows", gather.materialRows, BUFFER_USAGE.STORAGE) : null;
|
|
546
487
|
// A module's binding is data uploaded per render or a buffer the module already holds on the device (#491: scene records).
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
const materialGroup = runtime.device.createBindGroup({ layout: latticePipeline.getBindGroupLayout(1), entries: materialEntries });
|
|
488
|
+
const materialGroup = image ? null : runtime.device.createBindGroup({ layout: latticePipeline.getBindGroupLayout(1), entries: material.bindings.map((b) => ({ binding: b.binding, resource: { buffer: b.buffer ??
|
|
489
|
+
scope.upload("photivra-display-view-material-" + b.binding, b.data, b.kind === "uniform" ? BUFFER_USAGE.UNIFORM : BUFFER_USAGE.STORAGE) } })) });
|
|
550
490
|
const gate = createHostYieldGate();
|
|
551
491
|
for (let row0 = 0; row0 < region.height; row0 += rows) {
|
|
552
492
|
checkAbort(abort.signal);
|
|
@@ -576,26 +516,35 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
576
516
|
const band = new GpuResourceScope(runtime);
|
|
577
517
|
try {
|
|
578
518
|
const groupBuffer = band.upload("photivra-display-view-groups", groupWords, BUFFER_USAGE.STORAGE);
|
|
579
|
-
let latticeBuffer = band.buffer("photivra-display-view-lattice", latticeTotal * 4, BUFFER_USAGE.STORAGE | (blocked ? BUFFER_USAGE.COPY_SRC : 0));
|
|
580
519
|
const siteBuffer = band.buffer("photivra-display-view-sites", siteW * siteH * SITE_BYTES, BUFFER_USAGE.STORAGE);
|
|
581
|
-
// Pass 1: lattice materials.
|
|
520
|
+
// Pass 1: lattice materials (#581: an image-plane print's radiance per node, with its flags, and their host resolution).
|
|
582
521
|
let t0 = now();
|
|
583
522
|
words[0] = latticeTotal;
|
|
584
523
|
const latticeParams = band.upload("photivra-display-view-params-lattice", new Uint8Array(params.slice(0)), BUFFER_USAGE.UNIFORM);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
524
|
+
let latticeBuffer, pointFlags = null;
|
|
525
|
+
if (image) {
|
|
526
|
+
const pass = await image.runLattice(band, latticePipeline, latticeParams, groupBuffer, groupWords, latticeTotal, abort.signal);
|
|
527
|
+
latticeBuffer = pass.values;
|
|
528
|
+
pointFlags = pass.flags;
|
|
529
|
+
latticePointsResolved += pass.resolved;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
latticeBuffer = band.buffer("photivra-display-view-lattice", latticeTotal * 4, BUFFER_USAGE.STORAGE | (blocked ? BUFFER_USAGE.COPY_SRC : 0));
|
|
533
|
+
const latticeGroup = runtime.device.createBindGroup({ layout: latticePipeline.getBindGroupLayout(0), entries: [
|
|
534
|
+
{ binding: 0, resource: { buffer: latticeParams } }, { binding: 1, resource: { buffer: groupBuffer } }, { binding: 2, resource: { buffer: latticeBuffer } }
|
|
535
|
+
] });
|
|
536
|
+
const [latticeData] = await runComputePass(runtime, band, latticePipeline, [latticeGroup, materialGroup], linearDispatch(latticeTotal, DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), blocked ? [{ buffer: latticeBuffer, bytes: latticeTotal * 4 }] : [], abort.signal);
|
|
537
|
+
await queueIdle(runtime, abort.signal);
|
|
538
|
+
if (blocked) {
|
|
539
|
+
// #492: with a wide disc nearly every site reads some uncertain lattice point, and its spread over every material would flag
|
|
540
|
+
// it. The host resolves each uncertain point with the reference's own material lookup at the reference's lattice point
|
|
541
|
+
// (createDisplayViewExpectedSignal's latticeU/latticeV), so the sites read exactly the reference's materials.
|
|
542
|
+
const lattice = new Uint32Array(latticeData, 0, latticeTotal);
|
|
543
|
+
const resolved = resolveUncertainLattice(lattice, groupWords, signalInput);
|
|
544
|
+
if (resolved > 0)
|
|
545
|
+
latticeBuffer = band.upload("photivra-display-view-lattice-resolved", lattice, BUFFER_USAGE.STORAGE);
|
|
546
|
+
latticePointsResolved += resolved;
|
|
547
|
+
}
|
|
599
548
|
}
|
|
600
549
|
timings.lattice += now() - t0;
|
|
601
550
|
// Pass 2: site moments, in chunks of whole workgroups of sites whose taps stay within the budget per dispatch (#492).
|
|
@@ -610,8 +559,7 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
610
559
|
const siteParams = chunk.upload("photivra-display-view-params-sites", new Uint8Array(params.slice(0)), BUFFER_USAGE.UNIFORM);
|
|
611
560
|
const siteGroup = runtime.device.createBindGroup({ layout: sitesPipeline.getBindGroupLayout(0), entries: [
|
|
612
561
|
{ binding: 0, resource: { buffer: siteParams } }, { binding: 1, resource: { buffer: groupBuffer } }, ...(tapsBuffer ? [{ binding: 2, resource: { buffer: tapsBuffer } }] : []),
|
|
613
|
-
{ binding: 3, resource: { buffer: tableBuffer } }, { binding: 4, resource: { buffer: latticeBuffer } }, { binding: 5, resource: { buffer: siteBuffer } }, { binding: 6, resource: { buffer: spreadBuffer } },
|
|
614
|
-
...(materialRowsBuffer ? [{ binding: 7, resource: { buffer: materialRowsBuffer } }] : [])
|
|
562
|
+
{ binding: 3, resource: { buffer: tableBuffer } }, { binding: 4, resource: { buffer: latticeBuffer } }, { binding: 5, resource: { buffer: siteBuffer } }, { binding: 6, resource: { buffer: pointFlags ?? spreadBuffer } },
|
|
615
563
|
] });
|
|
616
564
|
await runComputePass(runtime, chunk, sitesPipeline, siteGroup, linearDispatch(words[0], DISPLAY_VIEW_QUALITY_WORKGROUP_SIZE, runtime.maximumWorkgroupsPerDimension), [], abort.signal);
|
|
617
565
|
await queueIdle(runtime, abort.signal);
|
|
@@ -623,71 +571,32 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
623
571
|
}
|
|
624
572
|
words[51] = 0;
|
|
625
573
|
timings.sites += now() - t0;
|
|
626
|
-
// Pass 3: display pixels
|
|
574
|
+
// Pass 3: display pixels.
|
|
627
575
|
words[0] = count;
|
|
628
576
|
const out = band.buffer("photivra-display-view-moments", count * 4 * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
629
577
|
const flagBuffer = band.buffer("photivra-display-view-flags", count * 4 * DISPLAY_VIEW_QUALITY_FLAG_WORDS, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_SRC);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
timings.pixels += now() - t0;
|
|
653
|
-
const flagWords = new Uint32Array(flagData, 0, count * DISPLAY_VIEW_QUALITY_FLAG_WORDS), bandFlags = new Uint32Array(count);
|
|
654
|
-
for (let i = 0; i < count; i++)
|
|
655
|
-
bandFlags[i] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i];
|
|
656
|
-
let missing = false;
|
|
657
|
-
for (let i = 0; i < count && !missing; i++)
|
|
658
|
-
if ((bandFlags[i] & DISPLAY_VIEW_QUALITY_FLAG.stateMissing) !== 0)
|
|
659
|
-
missing = true;
|
|
660
|
-
if (missing && correction && attempt === 0) {
|
|
661
|
-
// 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.
|
|
662
|
-
t0 = now();
|
|
663
|
-
const bitmap = new Uint32Array(bitmapData, 0, DISPLAY_VIEW_QUALITY_BITMAP_WORDS), needed = [];
|
|
664
|
-
for (let w = 0; w < DISPLAY_VIEW_QUALITY_BITMAP_WORDS; w++) {
|
|
665
|
-
let bitsWord = bitmap[w];
|
|
666
|
-
while (bitsWord !== 0) {
|
|
667
|
-
const bit = 31 - Math.clz32(bitsWord);
|
|
668
|
-
bitsWord &= ~(1 << bit);
|
|
669
|
-
const key = w * 32 + bit;
|
|
670
|
-
if (key < AHD_KEY_LEVELS ** 3 && !states.has(key))
|
|
671
|
-
needed.push(key);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
if (needed.length > 0) {
|
|
675
|
-
const computed = await computeDisplayViewCorrectionStates({ ...correction, yieldControl: async () => { if (yieldControl)
|
|
676
|
-
await yieldControl(abort.signal); }, checkAbort: () => checkAbort(abort.signal) }, Int32Array.from(needed));
|
|
677
|
-
states.add(computed.gridKeys, computed.values);
|
|
678
|
-
statesComputed += needed.length;
|
|
679
|
-
}
|
|
680
|
-
timings.states += now() - t0;
|
|
681
|
-
timings.pixelRedispatches++;
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
moments.set(new Float32Array(data, 0, count * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS), first * DISPLAY_VIEW_QUALITY_OUTPUT_FLOATS);
|
|
685
|
-
pixelFlags.set(bandFlags, first);
|
|
686
|
-
for (let i = 0; i < count; i++) {
|
|
687
|
-
masks[2 * (first + i)] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 1];
|
|
688
|
-
masks[2 * (first + i) + 1] = flagWords[DISPLAY_VIEW_QUALITY_FLAG_WORDS * i + 2];
|
|
689
|
-
}
|
|
690
|
-
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];
|
|
691
600
|
}
|
|
692
601
|
}
|
|
693
602
|
finally {
|
|
@@ -710,7 +619,7 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
710
619
|
const expected = new Float64Array(pixelCount * 3), noiseStd = new Float64Array(pixelCount * 3), saturated = new Float64Array(pixelCount);
|
|
711
620
|
const meanBound = new Float32Array(pixelCount), varianceBound = new Float32Array(pixelCount);
|
|
712
621
|
const flagged = [];
|
|
713
|
-
let largestMean = 0, largestVariance = 0, sampleCount = 0, siteCount = 0
|
|
622
|
+
let largestMean = 0, largestVariance = 0, sampleCount = 0, siteCount = 0;
|
|
714
623
|
const counts = new Int32Array(geometry.buffer);
|
|
715
624
|
// #533: each display pixel's saturated-pixel share from the kernel's mask (the reference's arithmetic, exact where it decided).
|
|
716
625
|
const columnOverlaps = Array.from({ length: region.width }, (_, i) => overlaps((region.x + i) * shape.sx, (region.x + i + 1) * shape.sx, setup.frameWidth));
|
|
@@ -736,8 +645,6 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
736
645
|
varianceBound[i] = MARGIN * moments[o + 10] + quantizationBound(moments, o, meanBound[i], pixels, step);
|
|
737
646
|
largestMean = Math.max(largestMean, meanBound[i]);
|
|
738
647
|
largestVariance = Math.max(largestVariance, varianceBound[i]);
|
|
739
|
-
if ((pixelFlags[i] & DISPLAY_VIEW_QUALITY_FLAG.corrected) !== 0)
|
|
740
|
-
corrected++;
|
|
741
648
|
const nx = counts[rx * stride + 1];
|
|
742
649
|
sampleCount += nx * ny;
|
|
743
650
|
siteCount += (nx + 2 * reach) * (ny + 2 * reach);
|
|
@@ -754,22 +661,11 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
754
661
|
for (let i = 0; i < pixelCount; i++)
|
|
755
662
|
if (isFlagged[i] === 0 && (pixelFlags[i] & DISPLAY_VIEW_QUALITY_FLAG.highlightUndecided) !== 0)
|
|
756
663
|
undecided.push(i);
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
let run = 1;
|
|
763
|
-
while (r + run < undecided.length && undecided[r + run] === first + run && rx + run < region.width)
|
|
764
|
-
run++;
|
|
765
|
-
const part = await renderDisplayView({ ...setup, signal, region: { x: region.x + rx, y: region.y + ry, width: run, height: 1 } });
|
|
766
|
-
for (let k = 0; k < run; k++) {
|
|
767
|
-
saturated[first + k] = part.saturated[k];
|
|
768
|
-
highlights.push(first + k);
|
|
769
|
-
}
|
|
770
|
-
r += run;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
664
|
+
if (input.recomputeFlagged !== false)
|
|
665
|
+
await recomputeRuns(setup, signalInput, undecided, abort, (first, run, part) => { for (let k = 0; k < run; k++) {
|
|
666
|
+
saturated[first + k] = part.saturated[k];
|
|
667
|
+
highlights.push(first + k);
|
|
668
|
+
} });
|
|
773
669
|
if (flagged.length > 0) {
|
|
774
670
|
if (input.recomputeFlagged === false) {
|
|
775
671
|
// Without recomputation a flagged pixel keeps the kernel's values where they are finite (its bound then does not hold).
|
|
@@ -787,18 +683,9 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
787
683
|
varianceBound[i] = MARGIN * moments[o + 10] + quantizationBound(moments, o, meanBound[i], pixels, step);
|
|
788
684
|
}
|
|
789
685
|
}
|
|
790
|
-
else
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
// edges); a region render is byte-identical to the same pixels of the whole view, so each pixel equals its one-pixel
|
|
794
|
-
// render, and the run's counts are the sums of its pixels' counts.
|
|
795
|
-
for (let r = 0; r < flagged.length;) {
|
|
796
|
-
checkAbort(abort.signal);
|
|
797
|
-
const first = flagged[r], rx = first % region.width, ry = (first - rx) / region.width;
|
|
798
|
-
let run = 1;
|
|
799
|
-
while (r + run < flagged.length && flagged[r + run] === first + run && rx + run < region.width)
|
|
800
|
-
run++;
|
|
801
|
-
const part = await renderDisplayView({ ...setup, signal, region: { x: region.x + rx, y: region.y + ry, width: run, height: 1 } });
|
|
686
|
+
else
|
|
687
|
+
await recomputeRuns(setup, signalInput, flagged, abort, (first, run, part) => {
|
|
688
|
+
// The run's counts are the sums of its pixels' counts.
|
|
802
689
|
samples.set(part.samples, first * 3);
|
|
803
690
|
expected.set(part.expected, first * 3);
|
|
804
691
|
noiseStd.set(part.noiseStd, first * 3);
|
|
@@ -808,12 +695,9 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
808
695
|
varianceBound[first + k] = 0;
|
|
809
696
|
recomputed.push(first + k);
|
|
810
697
|
}
|
|
811
|
-
corrected += part.ahdCorrectedPixelCount;
|
|
812
698
|
sampleCount += part.sampleCount;
|
|
813
699
|
siteCount += part.siteCount;
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
}
|
|
700
|
+
});
|
|
817
701
|
}
|
|
818
702
|
timings.recompute = now() - t0;
|
|
819
703
|
const relative = MARGIN * CPU_RELATIVE;
|
|
@@ -822,10 +706,10 @@ export async function renderDisplayViewQualityOnGpu(runtime, bandBytes, input, a
|
|
|
822
706
|
let saturatedPixelCount = 0;
|
|
823
707
|
for (let i = 0; i < pixelCount; i++)
|
|
824
708
|
saturatedPixelCount += saturated[i];
|
|
825
|
-
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,
|
|
826
710
|
execution: webGpuExecution(expectedValueBound, uncertain), expectedValueBound, varianceBound: { kind: "relative-to-reference", relative, absolute: largestVariance },
|
|
827
711
|
expectedValueAbsoluteBound: meanBound, varianceAbsoluteBound: varianceBound, flaggedPixelIndices: Uint32Array.from(flagged), hostRecomputedPixelIndices: Uint32Array.from(recomputed),
|
|
828
|
-
pixelFlags, highlightRecomputedPixelIndices: Uint32Array.from(highlights),
|
|
712
|
+
pixelFlags, highlightRecomputedPixelIndices: Uint32Array.from(highlights), tapsPerSite: gather.tapCount, blockedSum: blocked, siteSumRelativeBound: floats[50], latticePointsResolved, stageMilliseconds: timings };
|
|
829
713
|
}
|
|
830
714
|
/**
|
|
831
715
|
* The GPU setup the material modules read for a Quality render (#491's `createDisplayViewMaterialModules`). The composite module
|
|
@@ -845,8 +729,8 @@ export function qualityMaterialSetup(gpu) {
|
|
|
845
729
|
const offsets = groups.map((group) => ({ x: group.fx, y: group.fy, weight: 1 / groups.length }));
|
|
846
730
|
const queryPoint = (x, y, o, out) => {
|
|
847
731
|
const group = groups[o];
|
|
848
|
-
out[0] = (
|
|
849
|
-
out[1] = -(
|
|
732
|
+
out[0] = latticeCoordinate(x, group.fx, W, g);
|
|
733
|
+
out[1] = -latticeCoordinate(y, group.fy, H, g);
|
|
850
734
|
};
|
|
851
735
|
const { kernels: _kernels, ...rest } = input;
|
|
852
736
|
void _kernels;
|
|
@@ -856,25 +740,6 @@ export function qualityMaterialSetup(gpu) {
|
|
|
856
740
|
export function qualityMaterialModule(modules, gpu, signal) {
|
|
857
741
|
return modules.moduleFor(qualityMaterialSetup(gpu), signal);
|
|
858
742
|
}
|
|
859
|
-
/** The pixel-aperture samples per axis and anti-aliasing spots of a request. */
|
|
860
|
-
export function offsetCount(request) { return request.boxSamples * request.boxSamples * (request.antiAliasing ? 4 : 1); }
|
|
861
|
-
/**
|
|
862
|
-
* Why the Quality renderer does not support a prepared request (`DisplayViewGpuRenderer.supports`), or true: engine charts and
|
|
863
|
-
* declared `material-layers` scenes (the material modules of `createDisplayViewMaterialModules`, #491), the identity or Reinhard
|
|
864
|
-
* tone curve and at most 64 query offsets (lattice groups). `lens.defocus` out of focus is supported (#492 step 2c: the site pass
|
|
865
|
-
* gathers the disc, or each node's diffraction kernel convolved with it, up to the reference's own caps of radius 64 and 96);
|
|
866
|
-
* whether a view's footprints take the exact area mean, whether the device passes the composite probe, and whether the kernel
|
|
867
|
-
* table fits the device's storage binding limit, is known only per render.
|
|
868
|
-
*/
|
|
869
|
-
export function displayViewQualityGpuSupports(request) {
|
|
870
|
-
if (request.scene !== "chart" && request.scene !== "material-layers")
|
|
871
|
-
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)";
|
|
872
|
-
if (request.toneCurve !== "identity" && request.toneCurve !== "positive-reinhard-per-channel")
|
|
873
|
-
return "unsupported tone curve " + String(request.toneCurve);
|
|
874
|
-
if (offsetCount(request) > MAXIMUM_GROUPS)
|
|
875
|
-
return "more than " + MAXIMUM_GROUPS + " query offsets per site";
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
743
|
/** A normalized, symmetric site kernel of radius R for the self-check (a binned Gaussian of width `sigma` sites). */
|
|
879
744
|
function syntheticKernel(radius, sigma) {
|
|
880
745
|
const size = 2 * radius + 1, kernel = new Float64Array(size * size);
|
|
@@ -895,66 +760,32 @@ function syntheticKernel(radius, sigma) {
|
|
|
895
760
|
* the GPU against `renderDisplayView` within the a-priori bounds, every flagged pixel recomputed:
|
|
896
761
|
*
|
|
897
762
|
* - `display-view:quality:checkerboard-13x9-diffraction-ahd` (#479): synthetic radius-1 diffraction kernels at three spectral
|
|
898
|
-
* nodes, the AHD candidates
|
|
899
|
-
* 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;
|
|
900
764
|
* - `display-view:quality:checkerboard-13x9-defocus-blocked` (#492): the engine's own defocus disc of 6.3 sites (radius 3) at
|
|
901
765
|
* every node, the bilinear demosaic (a defocused Speed request's route), the blocked site sum, and a tap budget that splits
|
|
902
766
|
* each band's site pass over several dispatches.
|
|
903
767
|
*/
|
|
904
768
|
async function runQualityConformance(handle, signal, defocus) {
|
|
905
|
-
const runtime = handle;
|
|
906
|
-
const nativeWidth = 40, nativeHeight = 28, margin = 1, frameWidth = 38, frameHeight = 26, nodes = [450, 550, 650];
|
|
907
|
-
const chart = resolvePortableChartScene({ scene: { preset: "checkerboard", squaresAcross: 6 }, aspect: frameHeight / frameWidth });
|
|
769
|
+
const runtime = handle, { nativeWidth, nativeHeight, frameWidth, nodes } = CONFORMANCE_FRAME, { chart, scene } = conformanceChart();
|
|
908
770
|
const offsets = [];
|
|
909
771
|
for (const [nx, ny] of [[-0.25, -0.25], [0.25, -0.25], [-0.25, 0.25], [0.25, 0.25]])
|
|
910
772
|
for (const [ax, ay] of [[-0.5, -0.5], [0.5, -0.5], [-0.5, 0.5], [0.5, 0.5]])
|
|
911
773
|
offsets.push({ x: nx - ax, y: ny - ay, weight: 1 / 16 });
|
|
912
774
|
const disc = defocus ? createDefocusDiscSiteKernel(6.3) : null;
|
|
913
775
|
const kernels = disc ? { radius: disc.radius, perNode: nodes.map(() => disc.weights) } : { radius: 1, perNode: [syntheticKernel(1, 0.6), syntheticKernel(1, 0.7), syntheticKernel(1, 0.8)] };
|
|
914
|
-
const signalInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
channelAt: (x, y) => ((y & 1) === 0 ? ((x & 1) === 0 ? 0 : 1) : ((x & 1) === 0 ? 1 : 2)) };
|
|
919
|
-
const readout = { perElectron: defocus ? 1 / 40000 : 1 / 4000, darkElectrons: 0.07, constantVariance: 2.5e-7 };
|
|
920
|
-
const bilinear = bilinearKernels();
|
|
921
|
-
const setup = { frameWidth, frameHeight, margin, nativeWidth, nativeHeight, viewWidth: 13, viewHeight: 9, region: { x: 0, y: 0, width: 13, height: 9 },
|
|
922
|
-
samplesPerAxis: 16, reachSites: 5 + (disc ? disc.radius : 0), bitDepth: 8, seed: 479, signal: createDisplayViewExpectedSignal(signalInput), readout: [readout, readout, readout], demosaic: bilinear,
|
|
923
|
-
...(defocus ? {} : { candidates: [0, 1, 2, 3].map((p) => ahdCandidateKernels(p % 2, Math.floor(p / 2))),
|
|
924
|
-
ahdCorrection: { 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])), drawsPerState: 64, thresholdVariance: 1e-4 } }),
|
|
925
|
-
gains: [1.1, 1, 1.6], clipLevel: 1, matrix: [[3.08, -1.54, -0.54], [-0.92, 1.88, 0.045], [0.053, -0.2, 1.15]], toneCurve: "identity",
|
|
926
|
-
yieldControl: async () => undefined, checkAbort: () => { if (signal?.aborted)
|
|
927
|
-
throw abortedError(); } };
|
|
776
|
+
const signalInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: conformanceResponses(defocus ? 1 : 0.1),
|
|
777
|
+
offsets, kernels, scene, channelAt: conformanceChannelAt };
|
|
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))),
|
|
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])) } });
|
|
928
780
|
const reference = await renderDisplayView(setup);
|
|
929
|
-
|
|
930
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
931
|
-
try {
|
|
932
|
-
if (signal?.aborted)
|
|
933
|
-
abort.abort();
|
|
781
|
+
return await withConformanceAbort(runtime, signal, async (abort, pipelines) => {
|
|
934
782
|
const gather = defocus ? { blockedSum: true, maximumSiteTapsPerDispatch: 64 * 4 * (2 * 3 + 2) ** 2 } : {};
|
|
935
|
-
const gpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup, signalInput, material: chartPresetMaterialModule(chart), ...gather }, abort, undefined,
|
|
936
|
-
if (!defocus && (gpu.ahdCorrectedPixelCount === 0 || reference.ahdCorrectedPixelCount === 0))
|
|
937
|
-
return "the self-check did not exercise the low-signal correction.";
|
|
783
|
+
const gpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup, signalInput, material: chartPresetMaterialModule(chart), ...gather }, abort, undefined, pipelines);
|
|
938
784
|
if (defocus && (!gpu.blockedSum || gpu.stageMilliseconds.siteDispatches < 2))
|
|
939
785
|
return "the self-check did not exercise the blocked site sum over several dispatches.";
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (!(Math.abs(e1 - e0) <= expectedLimit))
|
|
944
|
-
return "component " + i + ": expected value " + e1 + " differs from the reference " + e0 + " by more than the a-priori bound " + expectedLimit + ".";
|
|
945
|
-
if (!(Math.abs(v1 - v0) <= varianceLimit))
|
|
946
|
-
return "component " + i + ": variance " + v1 + " differs from the reference " + v0 + " by more than the a-priori bound " + varianceLimit + ".";
|
|
947
|
-
if (Math.abs(gpu.samples[i] - reference.samples[i]) > 1)
|
|
948
|
-
return "component " + i + ": sample " + gpu.samples[i] + " differs from the reference " + reference.samples[i] + " by more than one code.";
|
|
949
|
-
}
|
|
950
|
-
// #533: the saturated-pixel count is the reference's, bit for bit (the light squares sit at the white clip).
|
|
951
|
-
if (gpu.saturatedPixelCount !== reference.saturatedPixelCount)
|
|
952
|
-
return "the saturated-pixel count " + gpu.saturatedPixelCount + " differs from the reference's " + reference.saturatedPixelCount + ".";
|
|
953
|
-
return true;
|
|
954
|
-
}
|
|
955
|
-
finally {
|
|
956
|
-
signal?.removeEventListener("abort", onAbort);
|
|
957
|
-
}
|
|
786
|
+
// #533: the verdict includes the saturated-pixel count (the light squares sit at the white clip).
|
|
787
|
+
return conformanceVerdict(reference, gpu);
|
|
788
|
+
});
|
|
958
789
|
}
|
|
959
790
|
/** #479's self-check (see `runQualityConformance`). */
|
|
960
791
|
export const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE = { id: "display-view:quality:checkerboard-13x9-diffraction-ahd",
|
|
@@ -962,95 +793,6 @@ export const DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE = { id: "display-view:quality
|
|
|
962
793
|
/** #492's self-check of the defocus disc's blocked site sum (see `runQualityConformance`). */
|
|
963
794
|
export const DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE = { id: "display-view:quality:checkerboard-13x9-defocus-blocked",
|
|
964
795
|
run: (handle, signal) => runQualityConformance(handle, signal, true) };
|
|
965
|
-
/**
|
|
966
|
-
export
|
|
967
|
-
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]];
|
|
968
|
-
const average = (offsets) => offsets.map(([dx, dy]) => ({ dx: dx, dy: dy, weight: 1 / offsets.length })), self = [{ dx: 0, dy: 0, weight: 1 }];
|
|
969
|
-
return [[self, average(cross), average(diagonal)], [average(horizontal), self, average(vertical)], [average(vertical), self, average(horizontal)], [average(diagonal), average(cross), self]];
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* The display view's GPU renderer on one runtime (#481's `DisplayViewGpuRenderer`): the Speed kernel for requests it covers
|
|
973
|
-
* (no diffraction, no AHD, no defocus disc, at most 16 offsets) and the Quality kernels for the rest (#492: a defocused Speed
|
|
974
|
-
* request too, with its bilinear demosaic). One pipeline cache per device, one set of
|
|
975
|
-
* material modules (#491: chart presets, composite charts, the 2.3 elements and material-layers scenes; `dispose` releases their
|
|
976
|
-
* uploaded scene records), one correction-state store per correction key. `render` renders the part's region (`setup.gpu`
|
|
977
|
-
* carries the expected signal's input and the chart; a setup without it, a coarse view or a frame the kernels do not cover
|
|
978
|
-
* throws `unsupported-on-backend`), and throws `aborted` and `device-lost` as the accelerator's operations do.
|
|
979
|
-
*/
|
|
980
|
-
/** #564: the GPU refusal of a defocus kernel above the direct radius (the CPU convolves it by runs). */
|
|
981
|
-
const LARGE_DEFOCUS_REASON = "a defocus kernel radius above 64 sites (convolved by runs on the CPU, #564) is not on the GPU";
|
|
982
|
-
export function createDisplayViewGpuRenderer(runtime, options = {}) {
|
|
983
|
-
const pipelines = new Map(), stores = new Map();
|
|
984
|
-
const bandBytes = options.maximumBandBytes ?? Math.min(64 * 1024 * 1024, runtime.maximumStorageBindingBytes);
|
|
985
|
-
const source = (label, code) => {
|
|
986
|
-
let pending = pipelines.get(label);
|
|
987
|
-
if (!pending) {
|
|
988
|
-
pending = createComputePipeline(runtime, label, code, undefined);
|
|
989
|
-
pipelines.set(label, pending);
|
|
990
|
-
pending.catch(() => pipelines.delete(label));
|
|
991
|
-
}
|
|
992
|
-
return pending;
|
|
993
|
-
};
|
|
994
|
-
// #491: one set of material modules per device for both kernels (the preset module per chart; the composite module, its probe
|
|
995
|
-
// and its uploaded scene records), released by `dispose`.
|
|
996
|
-
const modules = createDisplayViewMaterialModules(runtime, source);
|
|
997
|
-
return {
|
|
998
|
-
id: "photivra-display-view",
|
|
999
|
-
supports(request) {
|
|
1000
|
-
if (request.largeDefocus)
|
|
1001
|
-
return LARGE_DEFOCUS_REASON;
|
|
1002
|
-
// #492: out of focus the expected signal carries the defocus disc as site kernels, which only the Quality site pass gathers;
|
|
1003
|
-
// a Speed request then runs the Quality passes with its own bilinear demosaic (one candidate) and finishing.
|
|
1004
|
-
if (request.defocus)
|
|
1005
|
-
return displayViewQualityGpuSupports(request);
|
|
1006
|
-
const speed = displayViewSpeedGpuSupports(request);
|
|
1007
|
-
if (speed === true)
|
|
1008
|
-
return true;
|
|
1009
|
-
return displayViewQualityGpuSupports(request);
|
|
1010
|
-
},
|
|
1011
|
-
conformanceCases: [DISPLAY_VIEW_SPEED_CONFORMANCE_CASE, DISPLAY_VIEW_QUALITY_CONFORMANCE_CASE, DISPLAY_VIEW_QUALITY_DEFOCUS_CONFORMANCE_CASE],
|
|
1012
|
-
async render(part) {
|
|
1013
|
-
const { setup, signal } = part;
|
|
1014
|
-
if (!setup.gpu)
|
|
1015
|
-
throw new ImageKernelExecutionError("unsupported-on-backend", "The display-view setup carries no GPU inputs (setup.gpu); use cpu-reference.");
|
|
1016
|
-
const abort = new AbortController(), onAbort = () => abort.abort();
|
|
1017
|
-
if (signal?.aborted)
|
|
1018
|
-
throw abortedError();
|
|
1019
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1020
|
-
try {
|
|
1021
|
-
const signalInput = setup.gpu.signalInput;
|
|
1022
|
-
let result;
|
|
1023
|
-
let material;
|
|
1024
|
-
if (displayViewSpeedGpuUnsupportedReason(setup, signalInput) === null) {
|
|
1025
|
-
material = await modules.moduleFor(setup.gpu, abort.signal);
|
|
1026
|
-
result = await renderDisplayViewSpeedOnGpu(runtime, bandBytes, { setup, signalInput, material }, abort, options.yieldControl, source);
|
|
1027
|
-
}
|
|
1028
|
-
else {
|
|
1029
|
-
material = await qualityMaterialModule(modules, setup.gpu, abort.signal);
|
|
1030
|
-
const key = displayViewCorrectionKey(setup);
|
|
1031
|
-
let states = key === null ? undefined : stores.get(key);
|
|
1032
|
-
if (key !== null && !states) {
|
|
1033
|
-
states = new CorrectionStateStore();
|
|
1034
|
-
stores.set(key, states);
|
|
1035
|
-
}
|
|
1036
|
-
result = await renderDisplayViewQualityOnGpu(runtime, bandBytes, { setup, signalInput, material, ...(states ? { states } : {}) }, abort, options.yieldControl, source);
|
|
1037
|
-
}
|
|
1038
|
-
return { samples: result.samples, expected: result.expected, noiseStd: result.noiseStd, sampleCount: result.sampleCount, siteCount: result.siteCount, detailPixelCount: result.detailPixelCount,
|
|
1039
|
-
uniformPixelCount: result.uniformPixelCount, ahdCorrectedPixelCount: result.ahdCorrectedPixelCount, estimation: result.estimation, saturated: result.saturated,
|
|
1040
|
-
saturatedPixelCount: result.saturatedPixelCount, errorBound: result.expectedValueBound,
|
|
1041
|
-
diagnosticUncertaintySampleCount: result.execution.diagnosticUncertaintySampleCount, ...(material.compositeProbe ? { compositeProbe: material.compositeProbe } : {}) };
|
|
1042
|
-
}
|
|
1043
|
-
finally {
|
|
1044
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1045
|
-
}
|
|
1046
|
-
},
|
|
1047
|
-
dispose() { modules.release(); stores.clear(); }
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
/** The factory `prepareDisplayView` creates the renderer with (one per device): the Speed and Quality kernels behind one renderer. */
|
|
1051
|
-
export const displayViewGpuRendererFactory = (handle) => createDisplayViewGpuRenderer(handle);
|
|
1052
|
-
/** Registers the combined renderer as the display view's GPU renderer and returns the factory registered before. */
|
|
1053
|
-
export function registerDisplayViewGpuRenderer() {
|
|
1054
|
-
return setDisplayViewGpuRendererFactory(displayViewGpuRendererFactory);
|
|
1055
|
-
}
|
|
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]);
|
|
1056
798
|
//# sourceMappingURL=display-view-quality-gpu.js.map
|