@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
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* The image-plane module of the GPU display view (#581): the Speed kernel's `scene_value` (the seam of #478 and #491,
|
|
4
|
+
* `DisplayViewGpuMaterialModule`) for an image-plane print, so that a photo renders on the device like a chart.
|
|
5
|
+
*
|
|
6
|
+
* **What the reference computes** at a query point (expected-signal.ts, the direct path's `signalAt`): the print-local point
|
|
7
|
+
* cu = u·m/h, cv = v·m/h (outside |cu| ≤ 1, |cv| ≤ aspect the surround: its reflectance times the neutral signal N_c); the
|
|
8
|
+
* pixel coordinates X = (cu + 1)·W/2, Y = (aspect − cv)·H/(2·aspect); then the bilinear weights of the four nearest pixel
|
|
9
|
+
* centers (clamped to the edge) times each pixel's channel signal S_j,c = Σ_n A_c(λ_n)·ρ_j(λ_n)·W_n, integrated from the pixel's
|
|
10
|
+
* reconstructed spectrum ρ_j (its cell's eight node spectra blended, then the paper; `ResolvedImagePlaneScene.pixelSignals`), or
|
|
11
|
+
* the nearest pixel's. No per-channel values of the lattice nodes are formed.
|
|
12
|
+
*
|
|
13
|
+
* **On the device.** The host computes every pixel's S_j,c in float64 and rounds it once to float32 (3 values per pixel). When
|
|
14
|
+
* they fit one storage binding they are uploaded once per scene and kept on the device while the renderer lives; otherwise each
|
|
15
|
+
* band of a render builds and uploads only its rows. Either way they are built and written 4 MiB of rows at a time, so the host
|
|
16
|
+
* never holds more than that of S (owner decision 2026-11-09; the bytes are those of the whole array). The module recovers the exact lattice point from the
|
|
17
|
+
* kernel's float32 point as the composite module does (`LATTICE_POINT_WGSL`, about 2⁻⁴⁶ relative), forms cu, cv, X and Y
|
|
18
|
+
* in double-float32 and decides the print's edge in the composite kernel's bound arithmetic (a point within its bound of the
|
|
19
|
+
* edge is flagged: the display pixel is recomputed on the host). Per axis it splits s = X − ½ into its integer part n and
|
|
20
|
+
* fraction f exactly (double-float32), rounds the smaller of f and 1 − f once to float32 and forms the other as 1 minus it, so
|
|
21
|
+
* each weight carries at most 2E relative error (E = 2⁻²³) besides the point's own error. The point's error moves weight
|
|
22
|
+
* between neighbors: with δ the bound of |X_gpu − X_ref| in pixels (4·(2⁻⁴⁰·(|X| + 1)) + 4·10⁻¹⁴·W/2·k: the recovery, the
|
|
23
|
+
* double-float32 steps and the reference's own float64 rounding of its query point, generously), the value moves by at most
|
|
24
|
+
* (δx + δy)·2·max S, and the point is flagged when that exceeds E times the value. So an unflagged value carries a relative
|
|
25
|
+
* error only: S's rounding 1, the weights 2 + 2, the two products and two sums 4, the flag's budget 1, that is 10 roundings
|
|
26
|
+
* beyond the table lookup's one (`extraSiteRoundings`), added to the Speed kernel's chains (docs/WEBGPU.md, "Display view (Speed)").
|
|
27
|
+
* `nearest` takes the pixel under the point, flagging a point within its bound of a pixel edge (`bfloor`). All S are
|
|
28
|
+
* non-negative (non-negative responses, illuminant and spectra), so the weighted sums need no cancellation term.
|
|
29
|
+
*
|
|
30
|
+
* The surround's three channel signals, integrated from its spectrum (its reflectance times the white radiance at each node) like
|
|
31
|
+
* a pixel's, are the kernel's binding 3 (`spectral_table`, which this module does not otherwise use).
|
|
32
|
+
*/
|
|
33
|
+
import { ImageKernelExecutionError } from "./image-kernel-execution.js";
|
|
34
|
+
import { BUFFER_USAGE, GpuResourceScope } from "./webgpu-runtime.js";
|
|
35
|
+
import { dwSplit } from "./chart-composite-gpu.js";
|
|
36
|
+
import { displayViewModuleCommonWgsl, displayViewLatticeConstants, LATTICE_BINDINGS_WGSL, LATTICE_POINT_WGSL } from "./display-view-composite-material-module.js";
|
|
37
|
+
import { renderDisplayView } from "../facade/display-view.js";
|
|
38
|
+
import { resolveCaptureIlluminant } from "../facade/scene-radiance.js";
|
|
39
|
+
import { resolveMixedIllumination } from "../facade/mixed-illumination.js";
|
|
40
|
+
import { imagePlaneSupport } from "../facade/image-plane-support.js";
|
|
41
|
+
import { CONFORMANCE_FRAME, conformanceChannelAt, conformanceResponses, conformanceSetup, conformanceVerdict, renderDisplayViewSpeedOnGpu, withConformanceAbort } from "./display-view-speed-gpu.js";
|
|
42
|
+
import { renderDisplayViewQualityOnGpu } from "./display-view-quality-gpu.js";
|
|
43
|
+
import { imagePlaneQualityPasses } from "./display-view-image-quality.js";
|
|
44
|
+
import { ChartCompositeProbeError, probeChartCompositeDevice } from "./chart-composite-gpu.js";
|
|
45
|
+
import { createComputePipeline } from "./webgpu-runtime.js";
|
|
46
|
+
/** The module's id per product path. */
|
|
47
|
+
export function imagePlaneMaterialModuleId(path) { return path === "dekker" ? "image-plane" : "image-plane-fma"; }
|
|
48
|
+
/** Roundings of the module's value beyond the table lookup's one (see the module comment). */
|
|
49
|
+
export const IMAGE_PLANE_EXTRA_SITE_ROUNDINGS = 10;
|
|
50
|
+
/** Layout of the photo uniform (float32 / u32 words, 64 bytes). */
|
|
51
|
+
export const IMAGE_PLANE_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, row0: 3, halfWHi: 4, halfWLo: 5, halfHHi: 6, halfHLo: 7, aspectHi: 8, aspectLo: 9,
|
|
52
|
+
slackX: 10, slackY: 11, words: 16 });
|
|
53
|
+
/** Table values above this are outside the float32 domain. */
|
|
54
|
+
const FLOAT32_SAFE_MAGNITUDE = 2 ** 120;
|
|
55
|
+
/** Host bytes of pixel signals built per write (whole uploads and bands). */
|
|
56
|
+
const UPLOAD_CHUNK_BYTES = 4 << 20;
|
|
57
|
+
/** Scenes whose pixel signals stay uploaded per renderer. */
|
|
58
|
+
const KEPT_SCENES = 2;
|
|
59
|
+
/** The module's WGSL with one product path (see the module comment). */
|
|
60
|
+
export function imagePlaneMaterialWgsl(path = "dekker") {
|
|
61
|
+
return `${displayViewModuleCommonWgsl(path)}
|
|
62
|
+
@group(1) @binding(0) var<storage, read> photo_signal: array<f32>;
|
|
63
|
+
struct PhotoParams {
|
|
64
|
+
width: u32, height: u32, nearest: u32, row0: u32,
|
|
65
|
+
half_w_hi: f32, half_w_lo: f32, half_h_hi: f32, half_h_lo: f32,
|
|
66
|
+
aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32,
|
|
67
|
+
pad1: f32, pad2: f32, pad3: f32, pad4: f32,
|
|
68
|
+
};
|
|
69
|
+
@group(1) @binding(1) var<uniform> photo: PhotoParams;
|
|
70
|
+
${LATTICE_BINDINGS_WGSL}
|
|
71
|
+
${LATTICE_POINT_WGSL}
|
|
72
|
+
// The point's error bound in pixels along one axis (see the module comment).
|
|
73
|
+
fn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }
|
|
74
|
+
// One axis of the bilinear weights: the pixel pair (clamped to the edge) and the weights of s = X − 1/2.
|
|
75
|
+
struct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }
|
|
76
|
+
fn bilinear_axis(s: D, n: u32) -> Axis {
|
|
77
|
+
var f0 = floor(s.h);
|
|
78
|
+
if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }
|
|
79
|
+
let frac = d_sub(s, D(f0, 0.0));
|
|
80
|
+
let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);
|
|
81
|
+
var w0 = 0.0; var w1 = 0.0;
|
|
82
|
+
if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }
|
|
83
|
+
let last = i32(n) - 1;
|
|
84
|
+
return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);
|
|
85
|
+
}
|
|
86
|
+
fn photo_at(i: u32, j: u32, c: u32) -> f32 { return photo_signal[((j - photo.row0) * photo.width + i) * 3u + c]; }
|
|
87
|
+
fn scene_value(p: vec2<f32>, c: u32) -> f32 {
|
|
88
|
+
let k = D(lattice.chart_k_hi, lattice.chart_k_lo);
|
|
89
|
+
let u = d_mul(lattice_point(p.x, D(lattice.scale_u_hi, lattice.scale_u_lo), 0u), k);
|
|
90
|
+
let v = d_mul(lattice_point(p.y, D(lattice.scale_v_hi, lattice.scale_v_lo), 1u), k);
|
|
91
|
+
let aspect = D(photo.aspect_hi, photo.aspect_lo);
|
|
92
|
+
if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) { return spectral_table[c]; }
|
|
93
|
+
let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.half_w_hi, photo.half_w_lo));
|
|
94
|
+
let y = d_mul(d_sub(aspect, v), D(photo.half_h_hi, photo.half_h_lo));
|
|
95
|
+
if (photo.nearest != 0u) {
|
|
96
|
+
let last_x = f32(photo.width - 1u);
|
|
97
|
+
let last_y = f32(photo.height - 1u);
|
|
98
|
+
let i = u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x));
|
|
99
|
+
let j = u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y));
|
|
100
|
+
return photo_at(i, j, c);
|
|
101
|
+
}
|
|
102
|
+
let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);
|
|
103
|
+
let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);
|
|
104
|
+
let s00 = photo_at(ax.i0, ay.i0, c);
|
|
105
|
+
let s10 = photo_at(ax.i1, ay.i0, c);
|
|
106
|
+
let s01 = photo_at(ax.i0, ay.i1, c);
|
|
107
|
+
let s11 = photo_at(ax.i1, ay.i1, c);
|
|
108
|
+
let value = ob(ob(ay.w0 * ob(ob(ax.w0 * s00) + ob(ax.w1 * s10))) + ob(ay.w1 * ob(ob(ax.w0 * s01) + ob(ax.w1 * s11))));
|
|
109
|
+
let largest = max(max(s00, s10), max(s01, s11));
|
|
110
|
+
let moved = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0 * largest;
|
|
111
|
+
if (!(moved <= E * value)) { material_uncertain = true; }
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
/** The image-plane print behind a GPU setup's expected signal (`ImagePlaneSignalScene.image`), or undefined. */
|
|
117
|
+
export function imagePlaneOf(gpu) {
|
|
118
|
+
return gpu.signalInput.scene.image;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The image-plane modules of a renderer; `probe` is the composite module's device probe (single flight), whose double-word product
|
|
122
|
+
* path the module's double-float32 takes.
|
|
123
|
+
*/
|
|
124
|
+
export function createDisplayViewImageModules(runtime, probe) {
|
|
125
|
+
const uploads = [], wgsl = new Map();
|
|
126
|
+
// Pixel rows [j0, j1)'s channel signals, built on demand so that the host holds one chunk or band (#581): each pixel's signals
|
|
127
|
+
// integrated from its reconstructed spectrum (`pixelSignals`, the reference's own float64 values), rounded once.
|
|
128
|
+
const pixel = new Float64Array(3);
|
|
129
|
+
const rowsOf = (image, j0, j1) => {
|
|
130
|
+
const { scene, nodes } = image, values = new Float32Array((j1 - j0) * scene.width * 3);
|
|
131
|
+
for (let j = j0 * scene.width, o = 0; j < j1 * scene.width; j++) {
|
|
132
|
+
scene.pixelSignals(j, nodes, pixel, 0);
|
|
133
|
+
for (let c = 0; c < 3; c++) {
|
|
134
|
+
const value = pixel[c];
|
|
135
|
+
if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE))
|
|
136
|
+
throw new ImageKernelExecutionError("unsupported-float32-domain", "A pixel's channel signal is outside the float32 domain.");
|
|
137
|
+
values[o++] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return values;
|
|
141
|
+
};
|
|
142
|
+
// Rows [a, b)'s signals written to `buffer` (whose first row is `base`), built UPLOAD_CHUNK_BYTES of rows at a time.
|
|
143
|
+
const fill = (buffer, image, a, b, base) => {
|
|
144
|
+
const rowBytes = image.scene.width * 12, chunk = Math.max(1, Math.floor(UPLOAD_CHUNK_BYTES / rowBytes));
|
|
145
|
+
for (let j = a; j < b; j += chunk) {
|
|
146
|
+
const rows = rowsOf(image, j, Math.min(b, j + chunk));
|
|
147
|
+
runtime.device.queue.writeBuffer(buffer, (j - base) * rowBytes, rows.buffer, 0, rows.byteLength);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const rowsBuffer = (scope, label, image, j0, j1) => {
|
|
151
|
+
const buffer = scope.buffer(label, (j1 - j0) * image.scene.width * 12, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_DST);
|
|
152
|
+
fill(buffer, image, j0, j1, j0);
|
|
153
|
+
return buffer;
|
|
154
|
+
};
|
|
155
|
+
// The latest band's rows stay on the device until the next band: the rows the two share are copied there on the device, and
|
|
156
|
+
// only the others are built on the host (the same bytes; the host still holds one chunk at most).
|
|
157
|
+
let lastBand = null;
|
|
158
|
+
const bandBuffer = (image, j0, j1) => {
|
|
159
|
+
const rowBytes = image.scene.width * 12, scope = new GpuResourceScope(runtime), last = lastBand?.image === image ? lastBand : null;
|
|
160
|
+
try {
|
|
161
|
+
const buffer = scope.buffer("photivra-display-view-image-signal-band", (j1 - j0) * rowBytes, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_DST | BUFFER_USAGE.COPY_SRC);
|
|
162
|
+
const o0 = last ? Math.max(j0, last.j0) : j0, o1 = last ? Math.min(j1, last.j1) : j0;
|
|
163
|
+
if (o1 > o0) {
|
|
164
|
+
const encoder = runtime.device.createCommandEncoder({ label: "photivra-display-view-image-band-rows" });
|
|
165
|
+
encoder.copyBufferToBuffer(last.buffer, (o0 - last.j0) * rowBytes, buffer, (o0 - j0) * rowBytes, (o1 - o0) * rowBytes);
|
|
166
|
+
runtime.device.queue.submit([encoder.finish()]);
|
|
167
|
+
fill(buffer, image, j0, o0, j0);
|
|
168
|
+
fill(buffer, image, o1, j1, j0);
|
|
169
|
+
}
|
|
170
|
+
else
|
|
171
|
+
fill(buffer, image, j0, j1, j0);
|
|
172
|
+
lastBand?.scope.release();
|
|
173
|
+
lastBand = { image, j0, j1, scope, buffer };
|
|
174
|
+
return buffer;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
scope.release();
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const upload = (image) => {
|
|
182
|
+
const key = image, index = uploads.findIndex((e) => e.key === key);
|
|
183
|
+
if (index >= 0) {
|
|
184
|
+
const [entry] = uploads.splice(index, 1);
|
|
185
|
+
uploads.push(entry);
|
|
186
|
+
return entry;
|
|
187
|
+
}
|
|
188
|
+
const scope = new GpuResourceScope(runtime);
|
|
189
|
+
let buffer;
|
|
190
|
+
try {
|
|
191
|
+
buffer = rowsBuffer(scope, "photivra-display-view-image-signal", image, 0, image.scene.height);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
scope.release();
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
const entry = { key, scope, buffer, surround: Float32Array.from(image.surround) };
|
|
198
|
+
uploads.push(entry);
|
|
199
|
+
while (uploads.length > KEPT_SCENES)
|
|
200
|
+
uploads.shift().scope.release();
|
|
201
|
+
return entry;
|
|
202
|
+
};
|
|
203
|
+
let checked = null;
|
|
204
|
+
const build = (gpu, path) => {
|
|
205
|
+
const image = imagePlaneOf(gpu);
|
|
206
|
+
if (!image || !gpu.placement)
|
|
207
|
+
throw new ImageKernelExecutionError("unsupported-on-backend", "The setup carries no image-plane print and placement; use cpu-reference.");
|
|
208
|
+
const scene = image.scene, lattice = displayViewLatticeConstants(gpu.signalInput, gpu.placement, 0), rowBytes = scene.width * 12, banded = scene.height * rowBytes > runtime.maximumStorageBindingBytes;
|
|
209
|
+
const params = new ArrayBuffer(4 * IMAGE_PLANE_PARAMS.words), words = new Uint32Array(params), floats = new Float32Array(params), P = IMAGE_PLANE_PARAMS;
|
|
210
|
+
words[P.width] = scene.width;
|
|
211
|
+
words[P.height] = scene.height;
|
|
212
|
+
words[P.nearest] = scene.interpolation === "nearest" ? 1 : 0;
|
|
213
|
+
const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect);
|
|
214
|
+
[floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW);
|
|
215
|
+
[floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH);
|
|
216
|
+
[floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);
|
|
217
|
+
// The reference's float64 query point carries up to 10⁻¹⁴ frame units (the composite kernel's slack) times the pixels per frame unit.
|
|
218
|
+
floats[P.slackX] = 1e-14 * gpu.placement.chartUnitsPerFrameUnit * halfW;
|
|
219
|
+
floats[P.slackY] = 1e-14 * gpu.placement.chartUnitsPerFrameUnit * halfH;
|
|
220
|
+
let code = wgsl.get(path);
|
|
221
|
+
if (code === undefined) {
|
|
222
|
+
code = imagePlaneMaterialWgsl(path);
|
|
223
|
+
wgsl.set(path, code);
|
|
224
|
+
}
|
|
225
|
+
const base = { id: imagePlaneMaterialModuleId(path), wgsl: code, extraSiteRoundings: IMAGE_PLANE_EXTRA_SITE_ROUNDINGS, siteTable: Float32Array.from(image.surround) };
|
|
226
|
+
if (!banded) {
|
|
227
|
+
const uploaded = upload(image);
|
|
228
|
+
return { ...base, bindings: [{ binding: 0, kind: "storage", buffer: uploaded.buffer }, { binding: 1, kind: "uniform", data: new Uint8Array(params) },
|
|
229
|
+
{ binding: 2, kind: "uniform", data: new Uint8Array(lattice.params) }, { binding: 3, kind: "storage", data: lattice.offsets }] };
|
|
230
|
+
}
|
|
231
|
+
// A photo larger than one binding: per band the pixel rows its native site rows reach. The print-local v of site row y and
|
|
232
|
+
// query offset o is (y + c_o)·scale_v·k; the photo row is (aspect − v)·H/(2·aspect), plus two rows of margin either side.
|
|
233
|
+
const offsetsV = Array.from({ length: lattice.offsets.length / 4 }, (_, o) => lattice.offsets[4 * o + 2] + lattice.offsets[4 * o + 3]);
|
|
234
|
+
const rowOf = (y, c) => (scene.aspect - (y + c) * lattice.scaleV * gpu.placement.chartUnitsPerFrameUnit) * halfH;
|
|
235
|
+
const rate = Math.abs(lattice.scaleV) * gpu.placement.chartUnitsPerFrameUnit * halfH, maximumRows = Math.floor(runtime.maximumStorageBindingBytes / rowBytes);
|
|
236
|
+
return { ...base, bindings: [],
|
|
237
|
+
bandRows: (sitesPerDisplayRow) => Math.max(1, Math.floor(((maximumRows - 6) / rate - 6) / sitesPerDisplayRow)),
|
|
238
|
+
band(scope, layout, y0, y1) {
|
|
239
|
+
let low = Infinity, high = -Infinity;
|
|
240
|
+
for (const y of [y0, y1])
|
|
241
|
+
for (const c of offsetsV) {
|
|
242
|
+
const r = rowOf(y, c);
|
|
243
|
+
low = Math.min(low, r);
|
|
244
|
+
high = Math.max(high, r);
|
|
245
|
+
}
|
|
246
|
+
const j0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), j1 = Math.max(j0, Math.min(scene.height - 1, Math.floor(high) + 2));
|
|
247
|
+
if ((j1 - j0 + 1) * rowBytes > runtime.maximumStorageBindingBytes)
|
|
248
|
+
throw new ImageKernelExecutionError("insufficient-limits", "A band's pixel signals exceed the device's storage binding limit.");
|
|
249
|
+
const bandParams = params.slice(0);
|
|
250
|
+
new Uint32Array(bandParams)[P.row0] = j0;
|
|
251
|
+
return runtime.device.createBindGroup({ layout, entries: [
|
|
252
|
+
{ binding: 0, resource: { buffer: bandBuffer(image, j0, j1 + 1) } },
|
|
253
|
+
{ binding: 1, resource: { buffer: scope.upload("photivra-display-view-image-params", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },
|
|
254
|
+
{ binding: 2, resource: { buffer: scope.upload("photivra-display-view-image-lattice", new Uint8Array(lattice.params), BUFFER_USAGE.UNIFORM) } },
|
|
255
|
+
{ binding: 3, resource: { buffer: scope.upload("photivra-display-view-image-offsets", lattice.offsets, BUFFER_USAGE.STORAGE) } }
|
|
256
|
+
] });
|
|
257
|
+
} };
|
|
258
|
+
};
|
|
259
|
+
// The module's self-check, once per runtime (single flight; a failure is retried on the next photo).
|
|
260
|
+
const check = async () => {
|
|
261
|
+
checked ??= runImagePlaneConformance(runtime, undefined).catch((error) => { checked = null; throw error; });
|
|
262
|
+
const verdict = await checked;
|
|
263
|
+
if (verdict !== true) {
|
|
264
|
+
checked = null;
|
|
265
|
+
throw new ImageKernelExecutionError("unsupported-on-backend", "The image-plane module failed its self-check on this device: " + verdict);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
async moduleFor(gpu, signal) { const path = (await probe(signal)).productPath ?? "dekker"; await check(); return build(gpu, path); },
|
|
270
|
+
async qualityPasses(gpu, signal) { const path = (await probe(signal)).productPath ?? "dekker"; await check(); return imagePlaneQualityPasses(runtime, gpu, path); },
|
|
271
|
+
build,
|
|
272
|
+
release() { for (const entry of uploads.splice(0))
|
|
273
|
+
entry.scope.release(); lastBand?.scope.release(); lastBand = null; }
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* #581's run-time self-check of the module, run on a device's first photo: a 12 × 8 photo (a color ramp with hard edges, every
|
|
278
|
+
* lattice cell's corner colors among its pixels) printed at 0.8 of the frame's width on the 18 % surround of a 38 × 26 frame shown
|
|
279
|
+
* at 13 × 9, the GPU against `renderDisplayView` within the a-priori bounds, every flagged pixel recomputed: the Speed kernel with
|
|
280
|
+
* the 2 × 2 box, then the Quality passes with the four-spot filter's 16 offsets and synthetic radius-1 kernels at three nodes, and
|
|
281
|
+
* (#597) the same passes under mixed light (a three-source gradient with a hard edge and a point source).
|
|
282
|
+
* A device that fails the double-float32 probe refuses photos (unsupported-on-backend, as composite charts), so the check passes
|
|
283
|
+
* there without keeping the device's charts off the GPU.
|
|
284
|
+
*/
|
|
285
|
+
export async function runImagePlaneConformance(handle, signal) {
|
|
286
|
+
const runtime = handle, support = imagePlaneSupport();
|
|
287
|
+
if (!support)
|
|
288
|
+
return true;
|
|
289
|
+
const { nativeWidth, nativeHeight, frameWidth, nodes } = CONFORMANCE_FRAME, g = 2 / frameWidth;
|
|
290
|
+
const width = 12, height = 8, data = new Uint8Array(width * height * 3);
|
|
291
|
+
for (let y = 0; y < height; y++)
|
|
292
|
+
for (let x = 0; x < width; x++)
|
|
293
|
+
for (let c = 0; c < 3; c++)
|
|
294
|
+
data[(y * width + x) * 3 + c] = [(x * 23) % 256, (y * 37 + x * 5) % 256, x < 6 ? 255 - y * 30 : 10][c];
|
|
295
|
+
const printWidth = 1.6, aspect = height / width, k = 2 / printWidth, surround = 0.18, placement = { chartUnitsPerFrameUnit: k, aspect };
|
|
296
|
+
const scene = await support.resolveImagePlaneScene({ kind: "image-plane", sceneId: "conformance", image: { width, height, channels: 3, encoding: "srgb-8", data } }, aspect);
|
|
297
|
+
const white = Float64Array.of(0.5, 0.9, 0.7), A = conformanceResponses(1);
|
|
298
|
+
// The print's radiance as the provider forms it: the print-local point, the surround outside, times the white radiance at the node.
|
|
299
|
+
const radianceAt = (u, v, nm) => {
|
|
300
|
+
const cu = u * k, cv = v * k;
|
|
301
|
+
return (Math.abs(cu) <= 1 && Math.abs(cv) <= aspect ? scene.reflectanceAt(cu, cv, nm) : surround) * white[nodes.indexOf(nm)];
|
|
302
|
+
};
|
|
303
|
+
const printScene = scene.signalScene(nodes, A, white, radianceAt, k, 1, surround), readout = { perElectron: 1 / 60000, darkElectrons: 0.07, constantVariance: 1.5e-9 };
|
|
304
|
+
const speedOffsets = [[-0.25, -0.25], [0.25, -0.25], [-0.25, 0.25], [0.25, 0.25]].map(([x, y]) => ({ x: x, y: y, weight: 0.25 }));
|
|
305
|
+
const speedInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: A, offsets: speedOffsets, scene: printScene,
|
|
306
|
+
channelAt: conformanceChannelAt, queryPoint: (x, y, o, out) => { out[0] = (x + 0.5 + speedOffsets[o].x - nativeWidth / 2) * g; out[1] = -(y + 0.5 + speedOffsets[o].y - nativeHeight / 2) * g; } };
|
|
307
|
+
const speed = conformanceSetup(speedInput, readout, 581, 2, signal, { gpu: { signalInput: speedInput, chart: null, placement } });
|
|
308
|
+
const qualityOffsets = [];
|
|
309
|
+
for (const [nx, ny] of [[-0.25, -0.25], [0.25, -0.25], [-0.25, 0.25], [0.25, 0.25]])
|
|
310
|
+
for (const [ax, ay] of [[-0.5, -0.5], [0.5, -0.5], [-0.5, 0.5], [0.5, 0.5]])
|
|
311
|
+
qualityOffsets.push({ x: nx - ax, y: ny - ay, weight: 1 / 16 });
|
|
312
|
+
const kernel = (sigma) => { const w = Float64Array.from({ length: 9 }, (_, t) => Math.exp(-0.5 * (((t % 3) - 1) ** 2 + (Math.floor(t / 3) - 1) ** 2) / (sigma * sigma))); const sum = w.reduce((a, b) => a + b, 0); return w.map((v) => v / sum); };
|
|
313
|
+
const qualityInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: A, offsets: qualityOffsets,
|
|
314
|
+
kernels: { radius: 1, perNode: [kernel(0.6), kernel(0.7), kernel(0.8)] }, scene: printScene, channelAt: conformanceChannelAt };
|
|
315
|
+
const quality = conformanceSetup(qualityInput, readout, 582, 5, signal, { gpu: { signalInput: qualityInput, chart: null, placement } });
|
|
316
|
+
// #597: the same Quality case under mixed light: a gradient of three sources with a hard edge (equal stops) across the print, and a
|
|
317
|
+
// point source over its upper left, the reference's irradiance from the same parts in float64.
|
|
318
|
+
const day = { kind: "planckian", temperatureKelvin: 6500 }, lamp = { kind: "cie-a" };
|
|
319
|
+
const light = resolveMixedIllumination({ sources: [{ illuminant: day, illuminanceLux: 1.2e5 }, { illuminant: lamp, illuminanceLux: 8e4 }, { illuminant: day, illuminanceLux: 3e4 }],
|
|
320
|
+
blend: { kind: "linear-gradient", from: { u: -0.9, v: 0.1 }, to: { u: 0.8, v: -0.2 }, stops: [0, 0.45, 0.45] },
|
|
321
|
+
points: [{ illuminant: lamp, position: { xM: -0.3, yM: 0.2, heightM: 0.35 }, intensity: { illuminanceLux: 6e4, distanceM: 0.35 } }] }, resolveCaptureIlluminant);
|
|
322
|
+
// The print's half width is 1 m here (print-local units are metres), so the point sources read the print-local point.
|
|
323
|
+
const litIrradiance = (cu, cv, nm) => light.irradianceAt(cu, cv, cu, cv, nm);
|
|
324
|
+
const litRadianceAt = (u, v, nm) => {
|
|
325
|
+
const cu = u * k, cv = v * k;
|
|
326
|
+
return (Math.abs(cu) <= 1 && Math.abs(cv) <= aspect ? scene.reflectanceAt(cu, cv, nm) : surround) * litIrradiance(cu, cv, nm) / Math.PI;
|
|
327
|
+
};
|
|
328
|
+
const litInput = { ...qualityInput, scene: scene.signalScene(nodes, A, white, litRadianceAt, k, 1, surround, light) };
|
|
329
|
+
const lit = conformanceSetup(litInput, readout, 597, 5, signal, { gpu: { signalInput: litInput, chart: null, placement } });
|
|
330
|
+
const [speedReference, qualityReference, litReference] = [await renderDisplayView(speed), await renderDisplayView(quality), await renderDisplayView(lit)];
|
|
331
|
+
const images = createDisplayViewImageModules(runtime, (s) => probeChartCompositeDevice(runtime, (label, code) => createComputePipeline(runtime, label, code, s), s));
|
|
332
|
+
try {
|
|
333
|
+
return await withConformanceAbort(runtime, signal, async (abort, pipelines) => {
|
|
334
|
+
let path;
|
|
335
|
+
try {
|
|
336
|
+
path = (await probeChartCompositeDevice(runtime, pipelines, abort.signal)).productPath ?? "dekker";
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (error instanceof ChartCompositeProbeError)
|
|
340
|
+
return true;
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
const verdict = conformanceVerdict(speedReference, await renderDisplayViewSpeedOnGpu(runtime, 1 << 20, { setup: speed, signalInput: speedInput, material: images.build(speed.gpu, path) }, abort, undefined, pipelines));
|
|
344
|
+
if (verdict !== true)
|
|
345
|
+
return "Speed: " + verdict;
|
|
346
|
+
const gpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup: quality, signalInput: qualityInput, material: { id: "image-plane", wgsl: "", bindings: [] },
|
|
347
|
+
image: imagePlaneQualityPasses(runtime, quality.gpu, path) }, abort, undefined, pipelines);
|
|
348
|
+
const qualityVerdict = conformanceVerdict(qualityReference, gpu);
|
|
349
|
+
if (qualityVerdict !== true)
|
|
350
|
+
return "Quality: " + qualityVerdict;
|
|
351
|
+
const litGpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup: lit, signalInput: litInput, material: { id: "image-plane", wgsl: "", bindings: [] },
|
|
352
|
+
image: imagePlaneQualityPasses(runtime, lit.gpu, path) }, abort, undefined, pipelines);
|
|
353
|
+
const litVerdict = conformanceVerdict(litReference, litGpu);
|
|
354
|
+
return litVerdict === true ? true : "Quality under mixed light: " + litVerdict;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
images.release();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=display-view-image-material-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-view-image-material-module.js","sourceRoot":"","sources":["../../src/gpu/display-view-image-material-module.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The image-plane module of the GPU display view (#581): the Speed kernel's `scene_value` (the seam of #478 and #491,\n * `DisplayViewGpuMaterialModule`) for an image-plane print, so that a photo renders on the device like a chart.\n *\n * **What the reference computes** at a query point (expected-signal.ts, the direct path's `signalAt`): the print-local point\n * cu = u·m/h, cv = v·m/h (outside |cu| ≤ 1, |cv| ≤ aspect the surround: its reflectance times the neutral signal N_c); the\n * pixel coordinates X = (cu + 1)·W/2, Y = (aspect − cv)·H/(2·aspect); then the bilinear weights of the four nearest pixel\n * centers (clamped to the edge) times each pixel's channel signal S_j,c = Σ_n A_c(λ_n)·ρ_j(λ_n)·W_n, integrated from the pixel's\n * reconstructed spectrum ρ_j (its cell's eight node spectra blended, then the paper; `ResolvedImagePlaneScene.pixelSignals`), or\n * the nearest pixel's. No per-channel values of the lattice nodes are formed.\n *\n * **On the device.** The host computes every pixel's S_j,c in float64 and rounds it once to float32 (3 values per pixel). When\n * they fit one storage binding they are uploaded once per scene and kept on the device while the renderer lives; otherwise each\n * band of a render builds and uploads only its rows. Either way they are built and written 4 MiB of rows at a time, so the host\n * never holds more than that of S (owner decision 2026-11-09; the bytes are those of the whole array). The module recovers the exact lattice point from the\n * kernel's float32 point as the composite module does (`LATTICE_POINT_WGSL`, about 2⁻⁴⁶ relative), forms cu, cv, X and Y\n * in double-float32 and decides the print's edge in the composite kernel's bound arithmetic (a point within its bound of the\n * edge is flagged: the display pixel is recomputed on the host). Per axis it splits s = X − ½ into its integer part n and\n * fraction f exactly (double-float32), rounds the smaller of f and 1 − f once to float32 and forms the other as 1 minus it, so\n * each weight carries at most 2E relative error (E = 2⁻²³) besides the point's own error. The point's error moves weight\n * between neighbors: with δ the bound of |X_gpu − X_ref| in pixels (4·(2⁻⁴⁰·(|X| + 1)) + 4·10⁻¹⁴·W/2·k: the recovery, the\n * double-float32 steps and the reference's own float64 rounding of its query point, generously), the value moves by at most\n * (δx + δy)·2·max S, and the point is flagged when that exceeds E times the value. So an unflagged value carries a relative\n * error only: S's rounding 1, the weights 2 + 2, the two products and two sums 4, the flag's budget 1, that is 10 roundings\n * beyond the table lookup's one (`extraSiteRoundings`), added to the Speed kernel's chains (docs/WEBGPU.md, \"Display view (Speed)\").\n * `nearest` takes the pixel under the point, flagging a point within its bound of a pixel edge (`bfloor`). All S are\n * non-negative (non-negative responses, illuminant and spectra), so the weighted sums need no cancellation term.\n *\n * The surround's three channel signals, integrated from its spectrum (its reflectance times the white radiance at each node) like\n * a pixel's, are the kernel's binding 3 (`spectral_table`, which this module does not otherwise use).\n */\n\nimport { ImageKernelExecutionError, type ChartCompositeProbeRecord } from \"./image-kernel-execution.js\";\nimport { BUFFER_USAGE, GpuResourceScope, type WebGpuRuntime } from \"./webgpu-runtime.js\";\nimport { dwSplit } from \"./chart-composite-gpu.js\";\nimport type { ChartCompositeProductPath } from \"./wgsl-chart-composite-kernel.js\";\nimport { displayViewModuleCommonWgsl, displayViewLatticeConstants, LATTICE_BINDINGS_WGSL, LATTICE_POINT_WGSL } from \"./display-view-composite-material-module.js\";\nimport type { DisplayViewGpuMaterialModule } from \"./display-view-speed-gpu.js\";\nimport { renderDisplayView, type DisplayViewGpuSetup } from \"../facade/display-view.js\";\nimport type { DisplayViewExpectedSignalInput } from \"../facade/expected-signal.js\";\nimport { resolveCaptureIlluminant, type ImagePlaneSignalScene } from \"../facade/scene-radiance.js\";\nimport { resolveMixedIllumination } from \"../facade/mixed-illumination.js\";\nimport { imagePlaneSupport } from \"../facade/image-plane-support.js\";\nimport { CONFORMANCE_FRAME, conformanceChannelAt, conformanceResponses, conformanceSetup, conformanceVerdict, renderDisplayViewSpeedOnGpu, withConformanceAbort } from \"./display-view-speed-gpu.js\";\nimport { renderDisplayViewQualityOnGpu, type DisplayViewQualityImagePasses } from \"./display-view-quality-gpu.js\";\nimport { imagePlaneQualityPasses } from \"./display-view-image-quality.js\";\nimport { ChartCompositeProbeError, probeChartCompositeDevice } from \"./chart-composite-gpu.js\";\nimport { createComputePipeline } from \"./webgpu-runtime.js\";\n\n/** The module's id per product path. */\nexport function imagePlaneMaterialModuleId(path: ChartCompositeProductPath): string { return path === \"dekker\" ? \"image-plane\" : \"image-plane-fma\"; }\n/** Roundings of the module's value beyond the table lookup's one (see the module comment). */\nexport const IMAGE_PLANE_EXTRA_SITE_ROUNDINGS = 10;\n/** Layout of the photo uniform (float32 / u32 words, 64 bytes). */\nexport const IMAGE_PLANE_PARAMS = /* @__PURE__ */ Object.freeze({ width: 0, height: 1, nearest: 2, row0: 3, halfWHi: 4, halfWLo: 5, halfHHi: 6, halfHLo: 7, aspectHi: 8, aspectLo: 9,\n slackX: 10, slackY: 11, words: 16 });\n/** Table values above this are outside the float32 domain. */\nconst FLOAT32_SAFE_MAGNITUDE = 2 ** 120;\n/** Host bytes of pixel signals built per write (whole uploads and bands). */\nconst UPLOAD_CHUNK_BYTES = 4 << 20;\n/** Scenes whose pixel signals stay uploaded per renderer. */\nconst KEPT_SCENES = 2;\n\n/** The module's WGSL with one product path (see the module comment). */\nexport function imagePlaneMaterialWgsl(path: ChartCompositeProductPath = \"dekker\"): string {\n return `${displayViewModuleCommonWgsl(path)}\n@group(1) @binding(0) var<storage, read> photo_signal: array<f32>;\nstruct PhotoParams {\n width: u32, height: u32, nearest: u32, row0: u32,\n half_w_hi: f32, half_w_lo: f32, half_h_hi: f32, half_h_lo: f32,\n aspect_hi: f32, aspect_lo: f32, slack_x: f32, slack_y: f32,\n pad1: f32, pad2: f32, pad3: f32, pad4: f32,\n};\n@group(1) @binding(1) var<uniform> photo: PhotoParams;\n${LATTICE_BINDINGS_WGSL}\n${LATTICE_POINT_WGSL}\n// The point's error bound in pixels along one axis (see the module comment).\nfn pixel_slack(x: D, slack: f32) -> f32 { return 4.0 * (DW * (abs(x.h) + 1.0)) + 4.0 * slack; }\n// One axis of the bilinear weights: the pixel pair (clamped to the edge) and the weights of s = X − 1/2.\nstruct Axis { i0: u32, i1: u32, w0: f32, w1: f32 }\nfn bilinear_axis(s: D, n: u32) -> Axis {\n var f0 = floor(s.h);\n if (f0 == s.h && s.l < 0.0) { f0 = f0 - 1.0; }\n let frac = d_sub(s, D(f0, 0.0));\n let comp = d_sub(D(ob(f0 + 1.0), 0.0), s);\n var w0 = 0.0; var w1 = 0.0;\n if (frac.h <= 0.5) { w1 = ob(frac.h + frac.l); w0 = ob(1.0 - w1); } else { w0 = ob(comp.h + comp.l); w1 = ob(1.0 - w0); }\n let last = i32(n) - 1;\n return Axis(u32(clamp(i32(f0), 0, last)), u32(clamp(i32(f0) + 1, 0, last)), w0, w1);\n}\nfn photo_at(i: u32, j: u32, c: u32) -> f32 { return photo_signal[((j - photo.row0) * photo.width + i) * 3u + c]; }\nfn scene_value(p: vec2<f32>, c: u32) -> f32 {\n let k = D(lattice.chart_k_hi, lattice.chart_k_lo);\n let u = d_mul(lattice_point(p.x, D(lattice.scale_u_hi, lattice.scale_u_lo), 0u), k);\n let v = d_mul(lattice_point(p.y, D(lattice.scale_v_hi, lattice.scale_v_lo), 1u), k);\n let aspect = D(photo.aspect_hi, photo.aspect_lo);\n if (!(le(babs(bd(u)), bx(1.0)) && le(babs(bd(v)), bd(aspect)))) { return spectral_table[c]; }\n let x = d_mul(d_add(u, D(1.0, 0.0)), D(photo.half_w_hi, photo.half_w_lo));\n let y = d_mul(d_sub(aspect, v), D(photo.half_h_hi, photo.half_h_lo));\n if (photo.nearest != 0u) {\n let last_x = f32(photo.width - 1u);\n let last_y = f32(photo.height - 1u);\n let i = u32(clamp(bfloor(bd(x), 1.0, last_x), 0.0, last_x));\n let j = u32(clamp(bfloor(bd(y), 1.0, last_y), 0.0, last_y));\n return photo_at(i, j, c);\n }\n let ax = bilinear_axis(d_sub(x, D(0.5, 0.0)), photo.width);\n let ay = bilinear_axis(d_sub(y, D(0.5, 0.0)), photo.height);\n let s00 = photo_at(ax.i0, ay.i0, c);\n let s10 = photo_at(ax.i1, ay.i0, c);\n let s01 = photo_at(ax.i0, ay.i1, c);\n let s11 = photo_at(ax.i1, ay.i1, c);\n let value = ob(ob(ay.w0 * ob(ob(ax.w0 * s00) + ob(ax.w1 * s10))) + ob(ay.w1 * ob(ob(ax.w0 * s01) + ob(ax.w1 * s11))));\n let largest = max(max(s00, s10), max(s01, s11));\n let moved = (pixel_slack(x, photo.slack_x) + pixel_slack(y, photo.slack_y)) * 2.0 * largest;\n if (!(moved <= E * value)) { material_uncertain = true; }\n return value;\n}\n`;\n}\n\n/** The image-plane print behind a GPU setup's expected signal (`ImagePlaneSignalScene.image`), or undefined. */\nexport function imagePlaneOf(gpu: DisplayViewGpuSetup): ImagePlaneSignalScene[\"image\"] | undefined {\n return (gpu.signalInput.scene as { image?: ImagePlaneSignalScene[\"image\"] }).image;\n}\n/** The pixel signals of a scene on the device (per renderer), keyed by the signal scene's image record. */\ninterface UploadedImage { key: object; scope: GpuResourceScope; buffer: GPUBuffer; surround: Float32Array<ArrayBuffer> }\n\n/** The image-plane modules of one display-view renderer: `moduleFor` builds the setup's module (its uploads kept), `release` frees them. */\nexport interface DisplayViewImageModules {\n /** The setup's module; the first call on a runtime runs the module's self-check (`runImagePlaneConformance`) and refuses the device if it fails. */\n moduleFor(gpu: DisplayViewGpuSetup, signal: AbortSignal): Promise<DisplayViewGpuMaterialModule>;\n /** The setup's module without the self-check (the self-check itself uses it). */\n build(gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewGpuMaterialModule;\n /** #581: the print's Quality passes 1 and 2 (src/gpu/display-view-image-quality.ts), after the same self-check. */\n qualityPasses(gpu: DisplayViewGpuSetup, signal: AbortSignal): Promise<DisplayViewQualityImagePasses>;\n release(): void;\n}\n\n/**\n * The image-plane modules of a renderer; `probe` is the composite module's device probe (single flight), whose double-word product\n * path the module's double-float32 takes.\n */\nexport function createDisplayViewImageModules(runtime: WebGpuRuntime, probe: (signal: AbortSignal) => Promise<ChartCompositeProbeRecord>): DisplayViewImageModules {\n const uploads: UploadedImage[] = [], wgsl = new Map<ChartCompositeProductPath, string>();\n // Pixel rows [j0, j1)'s channel signals, built on demand so that the host holds one chunk or band (#581): each pixel's signals\n // integrated from its reconstructed spectrum (`pixelSignals`, the reference's own float64 values), rounded once.\n const pixel = new Float64Array(3);\n const rowsOf = (image: ImagePlaneSignalScene[\"image\"], j0: number, j1: number): Float32Array<ArrayBuffer> => {\n const { scene, nodes } = image, values = new Float32Array((j1 - j0) * scene.width * 3);\n for (let j = j0 * scene.width, o = 0; j < j1 * scene.width; j++) {\n scene.pixelSignals(j, nodes, pixel, 0);\n for (let c = 0; c < 3; c++) {\n const value = pixel[c]!;\n if (!(value >= 0 && value <= FLOAT32_SAFE_MAGNITUDE)) throw new ImageKernelExecutionError(\"unsupported-float32-domain\", \"A pixel's channel signal is outside the float32 domain.\");\n values[o++] = value;\n }\n }\n return values;\n };\n // Rows [a, b)'s signals written to `buffer` (whose first row is `base`), built UPLOAD_CHUNK_BYTES of rows at a time.\n const fill = (buffer: GPUBuffer, image: ImagePlaneSignalScene[\"image\"], a: number, b: number, base: number): void => {\n const rowBytes = image.scene.width * 12, chunk = Math.max(1, Math.floor(UPLOAD_CHUNK_BYTES / rowBytes));\n for (let j = a; j < b; j += chunk) { const rows = rowsOf(image, j, Math.min(b, j + chunk)); runtime.device.queue.writeBuffer(buffer, (j - base) * rowBytes, rows.buffer, 0, rows.byteLength); }\n };\n const rowsBuffer = (scope: GpuResourceScope, label: string, image: ImagePlaneSignalScene[\"image\"], j0: number, j1: number): GPUBuffer => {\n const buffer = scope.buffer(label, (j1 - j0) * image.scene.width * 12, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_DST);\n fill(buffer, image, j0, j1, j0);\n return buffer;\n };\n // The latest band's rows stay on the device until the next band: the rows the two share are copied there on the device, and\n // only the others are built on the host (the same bytes; the host still holds one chunk at most).\n let lastBand: { image: object; j0: number; j1: number; scope: GpuResourceScope; buffer: GPUBuffer } | null = null;\n const bandBuffer = (image: ImagePlaneSignalScene[\"image\"], j0: number, j1: number): GPUBuffer => {\n const rowBytes = image.scene.width * 12, scope = new GpuResourceScope(runtime), last = lastBand?.image === image ? lastBand : null;\n try {\n const buffer = scope.buffer(\"photivra-display-view-image-signal-band\", (j1 - j0) * rowBytes, BUFFER_USAGE.STORAGE | BUFFER_USAGE.COPY_DST | BUFFER_USAGE.COPY_SRC);\n const o0 = last ? Math.max(j0, last.j0) : j0, o1 = last ? Math.min(j1, last.j1) : j0;\n if (o1 > o0) {\n const encoder = runtime.device.createCommandEncoder({ label: \"photivra-display-view-image-band-rows\" });\n encoder.copyBufferToBuffer(last!.buffer, (o0 - last!.j0) * rowBytes, buffer, (o0 - j0) * rowBytes, (o1 - o0) * rowBytes);\n runtime.device.queue.submit([encoder.finish()]);\n fill(buffer, image, j0, o0, j0); fill(buffer, image, o1, j1, j0);\n } else fill(buffer, image, j0, j1, j0);\n lastBand?.scope.release();\n lastBand = { image, j0, j1, scope, buffer };\n return buffer;\n } catch (error) { scope.release(); throw error; }\n };\n const upload = (image: ImagePlaneSignalScene[\"image\"]): UploadedImage => {\n const key = image as object, index = uploads.findIndex((e) => e.key === key);\n if (index >= 0) { const [entry] = uploads.splice(index, 1); uploads.push(entry!); return entry!; }\n const scope = new GpuResourceScope(runtime);\n let buffer: GPUBuffer;\n try { buffer = rowsBuffer(scope, \"photivra-display-view-image-signal\", image, 0, image.scene.height); } catch (error) { scope.release(); throw error; }\n const entry: UploadedImage = { key, scope, buffer, surround: Float32Array.from(image.surround) };\n uploads.push(entry);\n while (uploads.length > KEPT_SCENES) uploads.shift()!.scope.release();\n return entry;\n };\n let checked: Promise<true | string> | null = null;\n const build = (gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewGpuMaterialModule => {\n const image = imagePlaneOf(gpu);\n if (!image || !gpu.placement) throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The setup carries no image-plane print and placement; use cpu-reference.\");\n const scene = image.scene, lattice = displayViewLatticeConstants(gpu.signalInput, gpu.placement, 0), rowBytes = scene.width * 12, banded = scene.height * rowBytes > runtime.maximumStorageBindingBytes;\n const params = new ArrayBuffer(4 * IMAGE_PLANE_PARAMS.words), words = new Uint32Array(params), floats = new Float32Array(params), P = IMAGE_PLANE_PARAMS;\n words[P.width] = scene.width; words[P.height] = scene.height; words[P.nearest] = scene.interpolation === \"nearest\" ? 1 : 0;\n const halfW = scene.width / 2, halfH = scene.height / (2 * scene.aspect);\n [floats[P.halfWHi], floats[P.halfWLo]] = dwSplit(halfW); [floats[P.halfHHi], floats[P.halfHLo]] = dwSplit(halfH); [floats[P.aspectHi], floats[P.aspectLo]] = dwSplit(scene.aspect);\n // The reference's float64 query point carries up to 10⁻¹⁴ frame units (the composite kernel's slack) times the pixels per frame unit.\n floats[P.slackX] = 1e-14 * gpu.placement.chartUnitsPerFrameUnit * halfW; floats[P.slackY] = 1e-14 * gpu.placement.chartUnitsPerFrameUnit * halfH;\n let code = wgsl.get(path);\n if (code === undefined) { code = imagePlaneMaterialWgsl(path); wgsl.set(path, code); }\n const base = { id: imagePlaneMaterialModuleId(path), wgsl: code, extraSiteRoundings: IMAGE_PLANE_EXTRA_SITE_ROUNDINGS, siteTable: Float32Array.from(image.surround) };\n if (!banded) {\n const uploaded = upload(image);\n return { ...base, bindings: [{ binding: 0, kind: \"storage\", buffer: uploaded.buffer }, { binding: 1, kind: \"uniform\", data: new Uint8Array(params) },\n { binding: 2, kind: \"uniform\", data: new Uint8Array(lattice.params) }, { binding: 3, kind: \"storage\", data: lattice.offsets }] };\n }\n // A photo larger than one binding: per band the pixel rows its native site rows reach. The print-local v of site row y and\n // query offset o is (y + c_o)·scale_v·k; the photo row is (aspect − v)·H/(2·aspect), plus two rows of margin either side.\n const offsetsV = Array.from({ length: lattice.offsets.length / 4 }, (_, o) => lattice.offsets[4 * o + 2]! + lattice.offsets[4 * o + 3]!);\n const rowOf = (y: number, c: number): number => (scene.aspect - (y + c) * lattice.scaleV * gpu.placement!.chartUnitsPerFrameUnit) * halfH;\n const rate = Math.abs(lattice.scaleV) * gpu.placement.chartUnitsPerFrameUnit * halfH, maximumRows = Math.floor(runtime.maximumStorageBindingBytes / rowBytes);\n return { ...base, bindings: [],\n bandRows: (sitesPerDisplayRow: number): number => Math.max(1, Math.floor(((maximumRows - 6) / rate - 6) / sitesPerDisplayRow)),\n band(scope, layout, y0, y1): GPUBindGroup {\n let low = Infinity, high = -Infinity;\n for (const y of [y0, y1]) for (const c of offsetsV) { const r = rowOf(y, c); low = Math.min(low, r); high = Math.max(high, r); }\n const j0 = Math.max(0, Math.min(scene.height - 1, Math.floor(low) - 2)), j1 = Math.max(j0, Math.min(scene.height - 1, Math.floor(high) + 2));\n if ((j1 - j0 + 1) * rowBytes > runtime.maximumStorageBindingBytes) throw new ImageKernelExecutionError(\"insufficient-limits\", \"A band's pixel signals exceed the device's storage binding limit.\");\n const bandParams = params.slice(0);\n new Uint32Array(bandParams)[P.row0] = j0;\n return runtime.device.createBindGroup({ layout, entries: [\n { binding: 0, resource: { buffer: bandBuffer(image, j0, j1 + 1) } },\n { binding: 1, resource: { buffer: scope.upload(\"photivra-display-view-image-params\", new Uint8Array(bandParams), BUFFER_USAGE.UNIFORM) } },\n { binding: 2, resource: { buffer: scope.upload(\"photivra-display-view-image-lattice\", new Uint8Array(lattice.params), BUFFER_USAGE.UNIFORM) } },\n { binding: 3, resource: { buffer: scope.upload(\"photivra-display-view-image-offsets\", lattice.offsets, BUFFER_USAGE.STORAGE) } }] });\n } };\n };\n // The module's self-check, once per runtime (single flight; a failure is retried on the next photo).\n const check = async (): Promise<void> => {\n checked ??= runImagePlaneConformance(runtime, undefined).catch((error: unknown) => { checked = null; throw error; });\n const verdict = await checked;\n if (verdict !== true) { checked = null; throw new ImageKernelExecutionError(\"unsupported-on-backend\", \"The image-plane module failed its self-check on this device: \" + verdict); }\n };\n return {\n async moduleFor(gpu, signal): Promise<DisplayViewGpuMaterialModule> { const path = (await probe(signal)).productPath ?? \"dekker\"; await check(); return build(gpu, path); },\n async qualityPasses(gpu, signal): Promise<DisplayViewQualityImagePasses> { const path = (await probe(signal)).productPath ?? \"dekker\"; await check(); return imagePlaneQualityPasses(runtime, gpu, path); },\n build,\n release(): void { for (const entry of uploads.splice(0)) entry.scope.release(); lastBand?.scope.release(); lastBand = null; }\n };\n}\n\n/**\n * #581's run-time self-check of the module, run on a device's first photo: a 12 × 8 photo (a color ramp with hard edges, every\n * lattice cell's corner colors among its pixels) printed at 0.8 of the frame's width on the 18 % surround of a 38 × 26 frame shown\n * at 13 × 9, the GPU against `renderDisplayView` within the a-priori bounds, every flagged pixel recomputed: the Speed kernel with\n * the 2 × 2 box, then the Quality passes with the four-spot filter's 16 offsets and synthetic radius-1 kernels at three nodes, and\n * (#597) the same passes under mixed light (a three-source gradient with a hard edge and a point source).\n * A device that fails the double-float32 probe refuses photos (unsupported-on-backend, as composite charts), so the check passes\n * there without keeping the device's charts off the GPU.\n */\nexport async function runImagePlaneConformance(handle: unknown, signal: AbortSignal | undefined): Promise<true | string> {\n const runtime = handle as WebGpuRuntime, support = imagePlaneSupport();\n if (!support) return true;\n const { nativeWidth, nativeHeight, frameWidth, nodes } = CONFORMANCE_FRAME, g = 2 / frameWidth;\n const width = 12, height = 8, data = new Uint8Array(width * height * 3);\n for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) for (let c = 0; c < 3; c++) data[(y * width + x) * 3 + c] = [(x * 23) % 256, (y * 37 + x * 5) % 256, x < 6 ? 255 - y * 30 : 10][c]!;\n const printWidth = 1.6, aspect = height / width, k = 2 / printWidth, surround = 0.18, placement = { chartUnitsPerFrameUnit: k, aspect };\n const scene = await support.resolveImagePlaneScene({ kind: \"image-plane\", sceneId: \"conformance\", image: { width, height, channels: 3, encoding: \"srgb-8\", data } }, aspect);\n const white = Float64Array.of(0.5, 0.9, 0.7), A = conformanceResponses(1);\n // The print's radiance as the provider forms it: the print-local point, the surround outside, times the white radiance at the node.\n const radianceAt = (u: number, v: number, nm: number): number => {\n const cu = u * k, cv = v * k;\n return (Math.abs(cu) <= 1 && Math.abs(cv) <= aspect ? scene.reflectanceAt(cu, cv, nm) : surround) * white[nodes.indexOf(nm)]!;\n };\n const printScene = scene.signalScene(nodes, A, white, radianceAt, k, 1, surround), readout = { perElectron: 1 / 60000, darkElectrons: 0.07, constantVariance: 1.5e-9 };\n const speedOffsets = [[-0.25, -0.25], [0.25, -0.25], [-0.25, 0.25], [0.25, 0.25]].map(([x, y]) => ({ x: x!, y: y!, weight: 0.25 }));\n const speedInput: DisplayViewExpectedSignalInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: A, offsets: speedOffsets, scene: printScene,\n channelAt: conformanceChannelAt, queryPoint: (x, y, o, out) => { out[0] = (x + 0.5 + speedOffsets[o]!.x - nativeWidth / 2) * g; out[1] = -(y + 0.5 + speedOffsets[o]!.y - nativeHeight / 2) * g; } };\n const speed = conformanceSetup(speedInput, readout, 581, 2, signal, { gpu: { signalInput: speedInput, chart: null, placement } });\n const qualityOffsets: { x: number; y: number; weight: number }[] = [];\n for (const [nx, ny] of [[-0.25, -0.25], [0.25, -0.25], [-0.25, 0.25], [0.25, 0.25]] as const) for (const [ax, ay] of [[-0.5, -0.5], [0.5, -0.5], [-0.5, 0.5], [0.5, 0.5]] as const) qualityOffsets.push({ x: nx - ax, y: ny - ay, weight: 1 / 16 });\n const kernel = (sigma: number): Float64Array => { const w = Float64Array.from({ length: 9 }, (_, t) => Math.exp(-0.5 * (((t % 3) - 1) ** 2 + (Math.floor(t / 3) - 1) ** 2) / (sigma * sigma))); const sum = w.reduce((a, b) => a + b, 0); return w.map((v) => v / sum); };\n const qualityInput: DisplayViewExpectedSignalInput = { nativeWidth, nativeHeight, outputWidth: frameWidth, wavelengthsNm: nodes, electronsPerRadiance: A, offsets: qualityOffsets,\n kernels: { radius: 1, perNode: [kernel(0.6), kernel(0.7), kernel(0.8)] }, scene: printScene, channelAt: conformanceChannelAt };\n const quality = conformanceSetup(qualityInput, readout, 582, 5, signal, { gpu: { signalInput: qualityInput, chart: null, placement } });\n // #597: the same Quality case under mixed light: a gradient of three sources with a hard edge (equal stops) across the print, and a\n // point source over its upper left, the reference's irradiance from the same parts in float64.\n const day = { kind: \"planckian\" as const, temperatureKelvin: 6500 }, lamp = { kind: \"cie-a\" as const };\n const light = resolveMixedIllumination({ sources: [{ illuminant: day, illuminanceLux: 1.2e5 }, { illuminant: lamp, illuminanceLux: 8e4 }, { illuminant: day, illuminanceLux: 3e4 }],\n blend: { kind: \"linear-gradient\", from: { u: -0.9, v: 0.1 }, to: { u: 0.8, v: -0.2 }, stops: [0, 0.45, 0.45] },\n points: [{ illuminant: lamp, position: { xM: -0.3, yM: 0.2, heightM: 0.35 }, intensity: { illuminanceLux: 6e4, distanceM: 0.35 } }] }, resolveCaptureIlluminant);\n // The print's half width is 1 m here (print-local units are metres), so the point sources read the print-local point.\n const litIrradiance = (cu: number, cv: number, nm: number): number => light.irradianceAt(cu, cv, cu, cv, nm);\n const litRadianceAt = (u: number, v: number, nm: number): number => {\n const cu = u * k, cv = v * k;\n return (Math.abs(cu) <= 1 && Math.abs(cv) <= aspect ? scene.reflectanceAt(cu, cv, nm) : surround) * litIrradiance(cu, cv, nm) / Math.PI;\n };\n const litInput: DisplayViewExpectedSignalInput = { ...qualityInput, scene: scene.signalScene(nodes, A, white, litRadianceAt, k, 1, surround, light) };\n const lit = conformanceSetup(litInput, readout, 597, 5, signal, { gpu: { signalInput: litInput, chart: null, placement } });\n const [speedReference, qualityReference, litReference] = [await renderDisplayView(speed), await renderDisplayView(quality), await renderDisplayView(lit)];\n const images = createDisplayViewImageModules(runtime, (s) => probeChartCompositeDevice(runtime, (label, code) => createComputePipeline(runtime, label, code, s), s));\n try {\n return await withConformanceAbort(runtime, signal, async (abort, pipelines) => {\n let path: \"dekker\" | \"fma\";\n try { path = (await probeChartCompositeDevice(runtime, pipelines, abort.signal)).productPath ?? \"dekker\"; } catch (error) {\n if (error instanceof ChartCompositeProbeError) return true;\n throw error;\n }\n const verdict = conformanceVerdict(speedReference, await renderDisplayViewSpeedOnGpu(runtime, 1 << 20, { setup: speed, signalInput: speedInput, material: images.build(speed.gpu!, path) }, abort, undefined, pipelines));\n if (verdict !== true) return \"Speed: \" + verdict;\n const gpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup: quality, signalInput: qualityInput, material: { id: \"image-plane\", wgsl: \"\", bindings: [] },\n image: imagePlaneQualityPasses(runtime, quality.gpu!, path) }, abort, undefined, pipelines);\n const qualityVerdict = conformanceVerdict(qualityReference, gpu);\n if (qualityVerdict !== true) return \"Quality: \" + qualityVerdict;\n const litGpu = await renderDisplayViewQualityOnGpu(runtime, 1 << 20, { setup: lit, signalInput: litInput, material: { id: \"image-plane\", wgsl: \"\", bindings: [] },\n image: imagePlaneQualityPasses(runtime, lit.gpu!, path) }, abort, undefined, pipelines);\n const litVerdict = conformanceVerdict(litReference, litGpu);\n return litVerdict === true ? true : \"Quality under mixed light: \" + litVerdict;\n });\n } finally { images.release(); }\n}\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type WebGpuRuntime } from "./webgpu-runtime.js";
|
|
2
|
+
import type { ChartCompositeProductPath } from "./wgsl-chart-composite-kernel.js";
|
|
3
|
+
import { type DisplayViewQualityImagePasses, type SignalGather } from "./display-view-quality-gpu.js";
|
|
4
|
+
import { type DisplayViewExpectedSignalInput } from "../facade/expected-signal.js";
|
|
5
|
+
import type { DisplayViewGpuSetup } from "../facade/display-view.js";
|
|
6
|
+
import { type ImagePlaneSignalScene } from "../facade/scene-radiance.js";
|
|
7
|
+
/** Relative roundings of a lattice point's radiance (see the module comment). */
|
|
8
|
+
export declare const IMAGE_PLANE_LATTICE_U = 32;
|
|
9
|
+
/**
|
|
10
|
+
* #597: relative roundings of the mixed light at a lattice point (a point whose computed bound exceeds IMAGE_PLANE_LIGHT_U − 1 is
|
|
11
|
+
* flagged; see the module comment), added to IMAGE_PLANE_LATTICE_U for a lit print.
|
|
12
|
+
*/
|
|
13
|
+
export declare const IMAGE_PLANE_LIGHT_U = 40;
|
|
14
|
+
/** #597: layout of the light table (float32 / u32 words; see `imagePlaneLightTable`). */
|
|
15
|
+
export declare const IMAGE_PLANE_LIGHT: Readonly<{
|
|
16
|
+
kind: 0;
|
|
17
|
+
sources: 1;
|
|
18
|
+
points: 2;
|
|
19
|
+
regions: 3;
|
|
20
|
+
outside: 4;
|
|
21
|
+
regionsAt: 6;
|
|
22
|
+
pointsAt: 7;
|
|
23
|
+
tablesAt: 8;
|
|
24
|
+
slack: 9;
|
|
25
|
+
surround: 10;
|
|
26
|
+
maximumInverse: 11;
|
|
27
|
+
halfHi: 12;
|
|
28
|
+
halfLo: 13;
|
|
29
|
+
aU: 16;
|
|
30
|
+
aV: 18;
|
|
31
|
+
dU: 20;
|
|
32
|
+
dV: 22;
|
|
33
|
+
inverse: 24;
|
|
34
|
+
stops: 26;
|
|
35
|
+
regionWords: 9;
|
|
36
|
+
pointWords: 6;
|
|
37
|
+
maximumPoints: 8;
|
|
38
|
+
}>;
|
|
39
|
+
/** Layout of the photo uniform (u32 / float32 words, 96 bytes). */
|
|
40
|
+
export declare const IMAGE_PLANE_QUALITY_PARAMS: Readonly<{
|
|
41
|
+
width: 0;
|
|
42
|
+
height: 1;
|
|
43
|
+
nearest: 2;
|
|
44
|
+
nodes: 3;
|
|
45
|
+
row0: 4;
|
|
46
|
+
zero: 5;
|
|
47
|
+
lit: 6;
|
|
48
|
+
suHi: 8;
|
|
49
|
+
suLo: 9;
|
|
50
|
+
svHi: 10;
|
|
51
|
+
svLo: 11;
|
|
52
|
+
kHi: 12;
|
|
53
|
+
kLo: 13;
|
|
54
|
+
halfWHi: 14;
|
|
55
|
+
halfWLo: 15;
|
|
56
|
+
halfHHi: 16;
|
|
57
|
+
halfHLo: 17;
|
|
58
|
+
aspectHi: 18;
|
|
59
|
+
aspectLo: 19;
|
|
60
|
+
slackX: 20;
|
|
61
|
+
slackY: 21;
|
|
62
|
+
black: 22;
|
|
63
|
+
span: 23;
|
|
64
|
+
words: 24;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* #597: the light table of a lit print at the spectral nodes (the layout `IMAGE_PLANE_LIGHT`): the blend (kind, sources, its geometry
|
|
68
|
+
* and stops with each segment's inverse width, 0 at a hard edge), the regions, the point sources (x, y, h in metres), then per node
|
|
69
|
+
* T_i,n = k_i·S_i(λ_n)/π for each blend source and c_j·S_j(λ_n)/π for each point source: spectra only, no channel value. `slack` is the
|
|
70
|
+
* reference's own rounding of a query point in print-local units.
|
|
71
|
+
*/
|
|
72
|
+
export declare function imagePlaneLightTable(light: NonNullable<ImagePlaneSignalScene["image"]["light"]>, wavelengths: readonly number[], surroundReflectance: number, slack: number): {
|
|
73
|
+
table: Float32Array<ArrayBuffer>;
|
|
74
|
+
largestIrradiance: number;
|
|
75
|
+
};
|
|
76
|
+
/** Pass 1's WGSL (see the module comment). */
|
|
77
|
+
export declare function imagePlaneQualityLatticeWgsl(path: ChartCompositeProductPath): string;
|
|
78
|
+
/** Pass 2's WGSL (see the module comment): the blocked sum over the nodes, then the Quality site tail. */
|
|
79
|
+
export declare const IMAGE_PLANE_QUALITY_SITES_WGSL: string;
|
|
80
|
+
/**
|
|
81
|
+
* The print's gather: per group its dense tap rectangle (with site kernels the reference's combined window; without them the
|
|
82
|
+
* members' span), the lattice extent beyond the site window and the point constants, and the table C_n,c(t) at (n·3 + c)·T + t,
|
|
83
|
+
* the reference's float64 values rounded once.
|
|
84
|
+
*/
|
|
85
|
+
export declare function imagePlaneQualityGather(signalInput: DisplayViewExpectedSignalInput, largestLitRadiance?: number): SignalGather;
|
|
86
|
+
/** The print's Quality passes for a setup (see `DisplayViewQualityImagePasses`), with the double-word product path the device's probe chose. */
|
|
87
|
+
export declare function imagePlaneQualityPasses(runtime: WebGpuRuntime, gpu: DisplayViewGpuSetup, path: ChartCompositeProductPath): DisplayViewQualityImagePasses;
|