@photivra/engine 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +11 -0
- package/dist/capture/c2pa-embed.d.ts +116 -0
- package/dist/capture/c2pa-embed.js +493 -0
- package/dist/capture/c2pa-embed.js.map +1 -0
- package/dist/capture/dng-import.d.ts +73 -8
- package/dist/capture/dng-import.js +315 -80
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/dng-opcodes.d.ts +96 -0
- package/dist/capture/dng-opcodes.js +391 -0
- package/dist/capture/dng-opcodes.js.map +1 -0
- package/dist/capture/dng-profile-maps.d.ts +99 -0
- package/dist/capture/dng-profile-maps.js +381 -0
- package/dist/capture/dng-profile-maps.js.map +1 -0
- package/dist/capture/dng-stage-image.d.ts +23 -0
- package/dist/capture/dng-stage-image.js +65 -0
- package/dist/capture/dng-stage-image.js.map +1 -0
- package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
- package/dist/capture/jpeg-xl-tiles.js +151 -0
- package/dist/capture/jpeg-xl-tiles.js.map +1 -0
- package/dist/capture/lossy-jpeg.d.ts +17 -0
- package/dist/capture/lossy-jpeg.js +369 -0
- package/dist/capture/lossy-jpeg.js.map +1 -0
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
- package/dist/color/neighbor-highlight-reconstruction.js +234 -0
- package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +5 -0
- package/dist/color/sensor-color-development.js +7 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/color/spectral-spikiness.d.ts +11 -0
- package/dist/color/spectral-spikiness.js +23 -0
- package/dist/color/spectral-spikiness.js.map +1 -0
- package/dist/core/execution-error.d.ts +21 -0
- package/dist/core/execution-error.js +12 -0
- package/dist/core/execution-error.js.map +1 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +26 -1
- package/dist/facade/catalogue-lens-signal.js +25 -7
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +1 -1
- package/dist/facade/diffraction-provider.js +4 -4
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +38 -6
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-normal.d.ts +15 -0
- package/dist/facade/display-view-normal.js +44 -0
- package/dist/facade/display-view-normal.js.map +1 -0
- package/dist/facade/display-view-rendering.d.ts +97 -0
- package/dist/facade/display-view-rendering.js +598 -0
- package/dist/facade/display-view-rendering.js.map +1 -0
- package/dist/facade/display-view.d.ts +37 -65
- package/dist/facade/display-view.js +147 -476
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.js +2 -2
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +37 -8
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +1 -1
- package/dist/facade/generic-lens-capture.js +1 -1
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane.js +41 -10
- package/dist/facade/image-plane.js.map +1 -1
- package/dist/facade/on-demand-kits.d.ts +14 -0
- package/dist/facade/on-demand-kits.js +36 -0
- package/dist/facade/on-demand-kits.js.map +1 -0
- package/dist/facade/opt-in-kit.d.ts +9 -0
- package/dist/facade/opt-in-kit.js +11 -0
- package/dist/facade/opt-in-kit.js.map +1 -0
- package/dist/facade/quality-kit.d.ts +21 -0
- package/dist/facade/quality-kit.js +23 -0
- package/dist/facade/quality-kit.js.map +1 -0
- package/dist/facade/quality-paths.d.ts +22 -0
- package/dist/facade/quality-paths.js +44 -0
- package/dist/facade/quality-paths.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +18 -2
- package/dist/facade/scene-radiance.js +6 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +32 -13
- package/dist/facade/simulate-capture.js +162 -69
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +10 -12
- package/dist/facade/simulate-display-view.js +57 -48
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
- package/dist/gpu/display-view-gpu-renderer.js +134 -0
- package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
- package/dist/gpu/display-view-image-material-module.d.ts +3 -2
- package/dist/gpu/display-view-image-material-module.js +25 -3
- package/dist/gpu/display-view-image-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-quality.d.ts +43 -1
- package/dist/gpu/display-view-image-quality.js +256 -11
- package/dist/gpu/display-view-image-quality.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
- package/dist/gpu/display-view-quality-gpu.js +60 -249
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
- package/dist/gpu/display-view-speed-gpu.js +50 -12
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +2 -13
- package/dist/gpu/image-kernel-execution.js +2 -10
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
- package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
- package/dist/gpu/wgsl-display-view-coupled.js +353 -0
- package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -9
- package/dist/gpu/wgsl-display-view-quality-kernel.js +109 -111
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
- package/dist/gpu/wgsl-display-view-speed-kernel.js +71 -8
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/output/camera-channel-response.d.ts +50 -0
- package/dist/output/camera-channel-response.js +69 -0
- package/dist/output/camera-channel-response.js.map +1 -0
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/sdr-rendering.d.ts +18 -1
- package/dist/output/sdr-rendering.js +39 -2
- package/dist/output/sdr-rendering.js.map +1 -1
- package/dist/sensor/native-sensor-report.d.ts +10 -0
- package/dist/sensor/native-sensor-report.js +16 -0
- package/dist/sensor/native-sensor-report.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +81 -13
- package/dist/sensor/sensor-characteristics.js +10 -6
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/API_REFERENCE.md +117 -4
- package/docs/QUICK_START.md +37 -8
- package/docs/USAGE.md +104 -22
- package/docs/api/capture-c2pa-embed.md +177 -0
- package/docs/api/capture-dng-import.md +86 -10
- package/docs/api/capture-dng-opcodes.md +93 -0
- package/docs/api/capture-dng-profile-maps.md +141 -0
- package/docs/api/capture-dng-stage-image.md +43 -0
- package/docs/api/capture-jpeg-xl-tiles.md +33 -0
- package/docs/api/capture-lossy-jpeg.md +41 -0
- package/docs/api/capture-native-raw-development.md +2 -1
- package/docs/api/core-execution-error.md +43 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +255 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-fast-methods.md +38 -9
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-scene-radiance.md +1 -1
- package/docs/api/facade-simulate-capture.md +24 -7
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +1 -0
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-kernel-execution.js","sourceRoot":"","sources":["../../src/gpu/image-kernel-execution.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Execution-backend labels, error bounds and domain errors for the optional image-kernel accelerator.\n *\n * `cpu-reference` is the float64 engine reference and the default. `webgpu-f32` is a separate float32\n * approximation backend. Every one of its results carries a forward-error bound worked out in advance\n * from:\n * - IEEE 754 binary32 rounding (unit roundoff u = 2^-24; γ_k = k·u/(1 − k·u), the classical factor\n * in error bounds for floating-point sums and inner products: Rump & Jeannerod, SIAM J. Matrix\n * Anal. Appl. 35(2), 2014, sec. 1, free author copy. Also see: Higham, Accuracy and Stability of\n * Numerical Algorithms, 2nd ed., SIAM 2002, §2–§3);\n * - the WGSL built-in accuracy table (W3C WebGPU Shading Language, §\"Floating Point Accuracy\").\n * See docs/WEBGPU.md.\n */\n\nimport { requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\n\n/** Requested or actual execution backend. */\nexport type ImageKernelBackend = \"cpu-reference\" | \"webgpu-f32\";\n\n/** Why a requested WebGPU backend was not used (only with `fallback: \"cpu-reference\"`). */\n/** Why a requested `webgpu-f32` backend ran the CPU reference instead. `webgpu-disabled`: `setWebGpuEnabled(false)` in this realm. */\nexport type ImageKernelFallbackReason = \"webgpu-unavailable\" | \"webgpu-disabled\" | \"adapter-unavailable\" | \"device-request-failed\" |\n \"insufficient-limits\" | \"unsupported-platform-endianness\";\n\n/**\n * Failure classes reported by {@link ImageKernelExecutionError}. `conformance-failed` (#481): a device failed the display view's\n * run-time self-check with `gpu: \"require\"`.\n */\nexport type ImageKernelFailureReason = ImageKernelFallbackReason | \"device-lost\" | \"out-of-memory\" | \"gpu-validation\" |\n \"aborted\" | \"disposed\" | \"busy\" | \"unsupported-on-backend\" | \"unsupported-float32-domain\" | \"indeterminate-float32-classification\" | \"conformance-failed\";\n\n/** Domain error for getting and running a backend. Input validation still throws the engine's existing errors. */\nexport class ImageKernelExecutionError extends Error {\n readonly code = \"IMAGE_KERNEL_EXECUTION\";\n readonly reason: ImageKernelFailureReason;\n\n constructor(reason: ImageKernelFailureReason, message: string) {\n super(message);\n this.name = \"ImageKernelExecutionError\";\n this.reason = reason;\n }\n}\n\n/** Integer output codes: |code_webgpu − code_reference| ≤ maximumAbsoluteCodeDifference for every sample. */\nexport interface ImageKernelCodeErrorBound {\n kind: \"integer-code-difference\";\n maximumAbsoluteCodeDifference: number;\n /** Bound on the float32 linear value entering the sRGB transfer, after tone and gamut handling. */\n linearAbsoluteBound: number;\n /** Declared bound on the WGSL sRGB transfer evaluation (pow), see WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND. */\n transferAbsoluteBound: number;\n}\n\n/** Non-negative linear sums: |v_webgpu − v_reference| ≤ relative·v_reference + absolute for every sample. */\nexport interface ImageKernelRelativeErrorBound {\n kind: \"relative-to-reference\";\n relative: number;\n absolute: number;\n}\n\n/**\n * Seeded stochastic stages (the sensor stage). Random states are integer arithmetic and bit-identical to\n * the reference. A site is flagged when any float32 decision (sampling test, saturation limit, rounding)\n * fell inside its a-priori bound. Every unflagged site has exactly the reference code and flags; only\n * flagged sites may differ, and the statistical-equivalence tests cover them (docs/WEBGPU.md).\n */\nexport interface ImageKernelSeededRealizationBound {\n kind: \"exact-except-flagged-sites\";\n /** Number of flagged sites; equal to the execution's `diagnosticUncertaintySampleCount`. */\n flaggedSiteCount: number;\n noiseModel: { id: string; version: string };\n seedSchedule: string;\n}\n\n/**\n * #481: the WebGPU adapter a device came from, as `GPUAdapter.info` describes it (W3C WebGPU, `GPUAdapterInfo`): each field\n * is the browser's string, or empty when the browser withholds it. Diagnostic only; it never changes a result.\n */\nexport interface ImageKernelAdapterDescription {\n vendor: string;\n architecture: string;\n device: string;\n description: string;\n}\n\n/**\n * A component of the composite chart kernel's double-float32 probe (see {@link ChartCompositeProbeRecord}). The first four are the\n * outputs of the probe before 2.11.0: `two-sum` is TwoSum's low part (`s.l`), `two-product` Dekker's TwoProduct low part (`m.l`),\n * `double-word-add` the double-word sum's low part (`a.l`), `double-word-multiply` the Dekker double-word product (`q.h`, `q.l`).\n */\nexport type ChartCompositeProbeComponent = \"two-sum\" | \"two-product\" | \"double-word-add\" | \"double-word-multiply\" | \"fma-two-product\" | \"fma-double-word-multiply\";\n\n/** One probe output that differs from its expected value: the component, the probe input's index, and the float32 bits (hex). */\nexport interface ChartCompositeProbeMismatch {\n component: ChartCompositeProbeComponent;\n input: number;\n got: string;\n want: string;\n}\n\n/**\n * The composite chart kernel's device probe (docs/WEBGPU.md, \"Scene radiance on the GPU\"): how the device evaluated the kernel's\n * double-float32 building blocks, and which product path the kernel was compiled with. Diagnostic; it never changes a result.\n *\n * - `deviceClass`: `exact` (the written order, round to nearest); `contracted` (only the Dekker product paths differ, consistent\n * with a multiply and an add fused into one rounding); `reassociated` (a sum path differs and every differing sum low part is\n * zero, the value an algebraic simplification of (a + b) − a to b gives); `other` (anything else, such as a sum path that differs\n * with non-zero low parts, as a rounding not to nearest gives, or a non-finite output).\n * - `productPath`: `dekker` when the Dekker path passes, else `fma` when the fma path passes (which needs a fused `fma`), else null\n * (the device is refused for composite charts, the 2.3 elements and material-layers scenes).\n * - `fmaFused`: whether every fma TwoProduct low part was exact (a fused `fma`; WGSL also allows an unfused one).\n */\nexport interface ChartCompositeProbeRecord {\n deviceClass: \"exact\" | \"contracted\" | \"reassociated\" | \"other\";\n productPath: \"dekker\" | \"fma\" | null;\n fmaFused: boolean;\n /** Probe inputs, each checked on every component. */\n inputs: number;\n /** Differing outputs per component. */\n mismatchCounts: Readonly<Record<ChartCompositeProbeComponent, number>>;\n /** The indices of the probe inputs (`PROBE_INPUTS`) whose output differs, per component. */\n mismatchedInputs: Readonly<Record<ChartCompositeProbeComponent, readonly number[]>>;\n /** The first differing outputs (at most eight), in component order. */\n mismatches: readonly ChartCompositeProbeMismatch[];\n}\n\n/** Label attached to every accelerated result. */\nexport interface ImageKernelExecution {\n backend: ImageKernelBackend;\n requestedBackend: ImageKernelBackend;\n fallbackReason: ImageKernelFallbackReason | null;\n arithmetic: \"float64\" | \"float32\";\n assurance: \"engine-reference\" | \"approximation-with-a-priori-bound\";\n /**\n * The exact-or-fast flag in the facade's words (#395): false for `cpu-reference` (the engine reference), true for\n * `webgpu-f32` (a bounded approximation). `backend` still names where the work ran.\n */\n fast: boolean;\n /**\n * True only for `cpu-reference`. Bit-exact identities (SHA-256 artifact bindings, byte-parity fixtures,\n * cross-engine determinism hashes) are CPU-only and must never be computed from a `webgpu-f32` result.\n */\n bitIdenticalToReference: boolean;\n errorBound: ImageKernelCodeErrorBound | ImageKernelRelativeErrorBound | ImageKernelSeededRealizationBound | null;\n /**\n * Diagnostic counts equal the reference when no sample fell inside a float32 classification band\n * (`diagnosticUncertaintySampleCount === 0`). Otherwise each count differs by at most that many\n * samples.\n */\n diagnosticsExact: boolean;\n diagnosticUncertaintySampleCount: number;\n limitations: readonly string[];\n}\n\n/** IEEE 754 binary32 unit roundoff. */\nexport const WEBGPU_F32_UNIT_ROUNDOFF = 2 ** -24;\n/**\n * Declared absolute error bound for the WGSL sRGB encode on [0, 1]: `1.055·pow(x, 1/2.4) − 0.055`. pow\n * takes its accuracy from exp2(y·log2 x) (log2: 3 ULP, or 2^-21 absolute on [0.5, 2]; exp2: 3 + 2|x|\n * ULP). The worst case over [0.0031308, 1] is about 2.5e-6. The bound 2^-17 ≈ 7.6e-6 keeps a 3× margin,\n * and also covers the small jump where the sRGB segments join (< 1e-7).\n */\nexport const WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND = 2 ** -17;\n/** Largest slope of the sRGB encode on [0, 1] (the linear segment, 12.92). */\nexport const SRGB_ENCODE_LIPSCHITZ = 12.92;\n\n/** Higham's γ_k for binary32. */\nexport function float32Gamma(k: number): number {\n const ku = k * WEBGPU_F32_UNIT_ROUNDOFF;\n return ku < 1 ? ku / (1 - ku) : Number.POSITIVE_INFINITY;\n}\n\n/** Integer-code bound from a bound on the linear value: |Δcode| ≤ ⌊maximum·(12.92·δ_L + δ_E)⌋ + 1, capped at maximum. */\nexport function integerCodeBound(maximum: number, linearAbsoluteBound: number): ImageKernelCodeErrorBound {\n const linear = Math.min(1, linearAbsoluteBound);\n const real = maximum * (SRGB_ENCODE_LIPSCHITZ * linear + WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND);\n return { kind: \"integer-code-difference\", maximumAbsoluteCodeDifference: Math.min(maximum, Math.floor(real) + 1),\n linearAbsoluteBound: linear, transferAbsoluteBound: WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND };\n}\n\n/**\n * Composes two `relative-to-reference` bounds through a pipeline `second ∘ first`, where the second\n * stage is linear with non-negative weights summing to at most `secondWeightSum` per output (convolution,\n * temporal sums). With |ã − a| ≤ r₁a + a₁ elementwise and |B̃x − Bx| ≤ r₂Bx + a₂:\n * |B̃ã − Ba| ≤ |B̃ã − Bã| + |B(ã − a)| ≤ r₂(1 + r₁)Ba + r₂W·a₁ + a₂ + r₁Ba + W·a₁,\n * so r = r₁ + r₂ + r₁r₂ and a = a₂ + W·a₁·(1 + r₂). See docs/WEBGPU.md, \"Accuracy contract\".\n */\nexport function composeRelativeErrorBounds(first: ImageKernelRelativeErrorBound, second: ImageKernelRelativeErrorBound,\n secondWeightSum: number): ImageKernelRelativeErrorBound {\n requireInputObject(first, \"composeRelativeErrorBounds first bound\");\n requireInputObject(second, \"composeRelativeErrorBounds second bound\");\n for (const v of [first.relative, first.absolute, second.relative, second.absolute, secondWeightSum]) {\n if (!(typeof v === \"number\" && v >= 0 && v <= Number.MAX_VALUE)) {\n throw new InvalidScientificInputError(\"Error bounds and weight sums must be finite and non-negative.\");\n }\n }\n return { kind: \"relative-to-reference\", relative: first.relative + second.relative + first.relative * second.relative,\n absolute: second.absolute + secondWeightSum * first.absolute * (1 + second.relative) };\n}\n\nexport const CPU_REFERENCE_LIMITATIONS = /* @__PURE__ */ Object.freeze([\"Float64 engine reference execution; results are the reference by definition\"]);\nexport const WEBGPU_F32_LIMITATIONS = /* @__PURE__ */ Object.freeze([\n \"Float32 WebGPU execution is an approximation of the float64 reference within the reported a-priori bound\",\n \"Not bit-identical to the reference: never use for SHA-256 artifact identities, byte-parity evidence or determinism hashes\",\n \"Bound assumes a conforming WebGPU implementation (correctly rounded f32 add/multiply/divide, WGSL accuracy table)\",\n \"Execution speed depends on the adapter; software adapters do not represent GPU performance\"\n]);\n\nexport function cpuExecution(requestedBackend: ImageKernelBackend, fallbackReason: ImageKernelFallbackReason | null): ImageKernelExecution {\n return { backend: \"cpu-reference\", requestedBackend, fallbackReason, arithmetic: \"float64\", assurance: \"engine-reference\", fast: false,\n bitIdenticalToReference: true, errorBound: null, diagnosticsExact: true, diagnosticUncertaintySampleCount: 0, limitations: CPU_REFERENCE_LIMITATIONS };\n}\n\nexport function webGpuExecution(errorBound: ImageKernelExecution[\"errorBound\"], uncertainty: number): ImageKernelExecution {\n return { backend: \"webgpu-f32\", requestedBackend: \"webgpu-f32\", fallbackReason: null, arithmetic: \"float32\",\n assurance: \"approximation-with-a-priori-bound\", fast: true, bitIdenticalToReference: false, errorBound, diagnosticsExact: uncertainty === 0,\n diagnosticUncertaintySampleCount: uncertainty, limitations: WEBGPU_F32_LIMITATIONS };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"image-kernel-execution.js","sourceRoot":"","sources":["../../src/gpu/image-kernel-execution.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Execution-backend labels, error bounds and domain errors for the optional image-kernel accelerator.\n *\n * `cpu-reference` is the float64 engine reference and the default. `webgpu-f32` is a separate float32\n * approximation backend. Every one of its results carries a forward-error bound worked out in advance\n * from:\n * - IEEE 754 binary32 rounding (unit roundoff u = 2^-24; γ_k = k·u/(1 − k·u), the classical factor\n * in error bounds for floating-point sums and inner products: Rump & Jeannerod, SIAM J. Matrix\n * Anal. Appl. 35(2), 2014, sec. 1, free author copy. Also see: Higham, Accuracy and Stability of\n * Numerical Algorithms, 2nd ed., SIAM 2002, §2–§3);\n * - the WGSL built-in accuracy table (W3C WebGPU Shading Language, §\"Floating Point Accuracy\").\n * See docs/WEBGPU.md.\n */\n\nimport { requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\n\n/** Requested or actual execution backend. */\nexport type ImageKernelBackend = \"cpu-reference\" | \"webgpu-f32\";\n\n/** Why a requested WebGPU backend was not used (only with `fallback: \"cpu-reference\"`). */\nexport { ImageKernelExecutionError, type ImageKernelFailureReason, type ImageKernelFallbackReason } from \"../core/execution-error.js\";\nimport type { ImageKernelFallbackReason } from \"../core/execution-error.js\";\n\n/** Integer output codes: |code_webgpu − code_reference| ≤ maximumAbsoluteCodeDifference for every sample. */\nexport interface ImageKernelCodeErrorBound {\n kind: \"integer-code-difference\";\n maximumAbsoluteCodeDifference: number;\n /** Bound on the float32 linear value entering the sRGB transfer, after tone and gamut handling. */\n linearAbsoluteBound: number;\n /** Declared bound on the WGSL sRGB transfer evaluation (pow), see WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND. */\n transferAbsoluteBound: number;\n}\n\n/** Non-negative linear sums: |v_webgpu − v_reference| ≤ relative·v_reference + absolute for every sample. */\nexport interface ImageKernelRelativeErrorBound {\n kind: \"relative-to-reference\";\n relative: number;\n absolute: number;\n}\n\n/**\n * Seeded stochastic stages (the sensor stage). Random states are integer arithmetic and bit-identical to\n * the reference. A site is flagged when any float32 decision (sampling test, saturation limit, rounding)\n * fell inside its a-priori bound. Every unflagged site has exactly the reference code and flags; only\n * flagged sites may differ, and the statistical-equivalence tests cover them (docs/WEBGPU.md).\n */\nexport interface ImageKernelSeededRealizationBound {\n kind: \"exact-except-flagged-sites\";\n /** Number of flagged sites; equal to the execution's `diagnosticUncertaintySampleCount`. */\n flaggedSiteCount: number;\n noiseModel: { id: string; version: string };\n seedSchedule: string;\n}\n\n/**\n * #481: the WebGPU adapter a device came from, as `GPUAdapter.info` describes it (W3C WebGPU, `GPUAdapterInfo`): each field\n * is the browser's string, or empty when the browser withholds it. Diagnostic only; it never changes a result.\n */\nexport interface ImageKernelAdapterDescription {\n vendor: string;\n architecture: string;\n device: string;\n description: string;\n}\n\n/**\n * A component of the composite chart kernel's double-float32 probe (see {@link ChartCompositeProbeRecord}). The first four are the\n * outputs of the probe before 2.11.0: `two-sum` is TwoSum's low part (`s.l`), `two-product` Dekker's TwoProduct low part (`m.l`),\n * `double-word-add` the double-word sum's low part (`a.l`), `double-word-multiply` the Dekker double-word product (`q.h`, `q.l`).\n */\nexport type ChartCompositeProbeComponent = \"two-sum\" | \"two-product\" | \"double-word-add\" | \"double-word-multiply\" | \"fma-two-product\" | \"fma-double-word-multiply\";\n\n/** One probe output that differs from its expected value: the component, the probe input's index, and the float32 bits (hex). */\nexport interface ChartCompositeProbeMismatch {\n component: ChartCompositeProbeComponent;\n input: number;\n got: string;\n want: string;\n}\n\n/**\n * The composite chart kernel's device probe (docs/WEBGPU.md, \"Scene radiance on the GPU\"): how the device evaluated the kernel's\n * double-float32 building blocks, and which product path the kernel was compiled with. Diagnostic; it never changes a result.\n *\n * - `deviceClass`: `exact` (the written order, round to nearest); `contracted` (only the Dekker product paths differ, consistent\n * with a multiply and an add fused into one rounding); `reassociated` (a sum path differs and every differing sum low part is\n * zero, the value an algebraic simplification of (a + b) − a to b gives); `other` (anything else, such as a sum path that differs\n * with non-zero low parts, as a rounding not to nearest gives, or a non-finite output).\n * - `productPath`: `dekker` when the Dekker path passes, else `fma` when the fma path passes (which needs a fused `fma`), else null\n * (the device is refused for composite charts, the 2.3 elements and material-layers scenes).\n * - `fmaFused`: whether every fma TwoProduct low part was exact (a fused `fma`; WGSL also allows an unfused one).\n */\nexport interface ChartCompositeProbeRecord {\n deviceClass: \"exact\" | \"contracted\" | \"reassociated\" | \"other\";\n productPath: \"dekker\" | \"fma\" | null;\n fmaFused: boolean;\n /** Probe inputs, each checked on every component. */\n inputs: number;\n /** Differing outputs per component. */\n mismatchCounts: Readonly<Record<ChartCompositeProbeComponent, number>>;\n /** The indices of the probe inputs (`PROBE_INPUTS`) whose output differs, per component. */\n mismatchedInputs: Readonly<Record<ChartCompositeProbeComponent, readonly number[]>>;\n /** The first differing outputs (at most eight), in component order. */\n mismatches: readonly ChartCompositeProbeMismatch[];\n}\n\n/** Label attached to every accelerated result. */\nexport interface ImageKernelExecution {\n backend: ImageKernelBackend;\n requestedBackend: ImageKernelBackend;\n fallbackReason: ImageKernelFallbackReason | null;\n arithmetic: \"float64\" | \"float32\";\n assurance: \"engine-reference\" | \"approximation-with-a-priori-bound\";\n /**\n * The exact-or-fast flag in the facade's words (#395): false for `cpu-reference` (the engine reference), true for\n * `webgpu-f32` (a bounded approximation). `backend` still names where the work ran.\n */\n fast: boolean;\n /**\n * True only for `cpu-reference`. Bit-exact identities (SHA-256 artifact bindings, byte-parity fixtures,\n * cross-engine determinism hashes) are CPU-only and must never be computed from a `webgpu-f32` result.\n */\n bitIdenticalToReference: boolean;\n errorBound: ImageKernelCodeErrorBound | ImageKernelRelativeErrorBound | ImageKernelSeededRealizationBound | null;\n /**\n * Diagnostic counts equal the reference when no sample fell inside a float32 classification band\n * (`diagnosticUncertaintySampleCount === 0`). Otherwise each count differs by at most that many\n * samples.\n */\n diagnosticsExact: boolean;\n diagnosticUncertaintySampleCount: number;\n limitations: readonly string[];\n}\n\n/** IEEE 754 binary32 unit roundoff. */\nexport const WEBGPU_F32_UNIT_ROUNDOFF = 2 ** -24;\n/**\n * Declared absolute error bound for the WGSL sRGB encode on [0, 1]: `1.055·pow(x, 1/2.4) − 0.055`. pow\n * takes its accuracy from exp2(y·log2 x) (log2: 3 ULP, or 2^-21 absolute on [0.5, 2]; exp2: 3 + 2|x|\n * ULP). The worst case over [0.0031308, 1] is about 2.5e-6. The bound 2^-17 ≈ 7.6e-6 keeps a 3× margin,\n * and also covers the small jump where the sRGB segments join (< 1e-7).\n */\nexport const WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND = 2 ** -17;\n/** Largest slope of the sRGB encode on [0, 1] (the linear segment, 12.92). */\nexport const SRGB_ENCODE_LIPSCHITZ = 12.92;\n\n/** Higham's γ_k for binary32. */\nexport function float32Gamma(k: number): number {\n const ku = k * WEBGPU_F32_UNIT_ROUNDOFF;\n return ku < 1 ? ku / (1 - ku) : Number.POSITIVE_INFINITY;\n}\n\n/** Integer-code bound from a bound on the linear value: |Δcode| ≤ ⌊maximum·(12.92·δ_L + δ_E)⌋ + 1, capped at maximum. */\nexport function integerCodeBound(maximum: number, linearAbsoluteBound: number): ImageKernelCodeErrorBound {\n const linear = Math.min(1, linearAbsoluteBound);\n const real = maximum * (SRGB_ENCODE_LIPSCHITZ * linear + WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND);\n return { kind: \"integer-code-difference\", maximumAbsoluteCodeDifference: Math.min(maximum, Math.floor(real) + 1),\n linearAbsoluteBound: linear, transferAbsoluteBound: WEBGPU_F32_TRANSFER_ABSOLUTE_BOUND };\n}\n\n/**\n * Composes two `relative-to-reference` bounds through a pipeline `second ∘ first`, where the second\n * stage is linear with non-negative weights summing to at most `secondWeightSum` per output (convolution,\n * temporal sums). With |ã − a| ≤ r₁a + a₁ elementwise and |B̃x − Bx| ≤ r₂Bx + a₂:\n * |B̃ã − Ba| ≤ |B̃ã − Bã| + |B(ã − a)| ≤ r₂(1 + r₁)Ba + r₂W·a₁ + a₂ + r₁Ba + W·a₁,\n * so r = r₁ + r₂ + r₁r₂ and a = a₂ + W·a₁·(1 + r₂). See docs/WEBGPU.md, \"Accuracy contract\".\n */\nexport function composeRelativeErrorBounds(first: ImageKernelRelativeErrorBound, second: ImageKernelRelativeErrorBound,\n secondWeightSum: number): ImageKernelRelativeErrorBound {\n requireInputObject(first, \"composeRelativeErrorBounds first bound\");\n requireInputObject(second, \"composeRelativeErrorBounds second bound\");\n for (const v of [first.relative, first.absolute, second.relative, second.absolute, secondWeightSum]) {\n if (!(typeof v === \"number\" && v >= 0 && v <= Number.MAX_VALUE)) {\n throw new InvalidScientificInputError(\"Error bounds and weight sums must be finite and non-negative.\");\n }\n }\n return { kind: \"relative-to-reference\", relative: first.relative + second.relative + first.relative * second.relative,\n absolute: second.absolute + secondWeightSum * first.absolute * (1 + second.relative) };\n}\n\nexport const CPU_REFERENCE_LIMITATIONS = /* @__PURE__ */ Object.freeze([\"Float64 engine reference execution; results are the reference by definition\"]);\nexport const WEBGPU_F32_LIMITATIONS = /* @__PURE__ */ Object.freeze([\n \"Float32 WebGPU execution is an approximation of the float64 reference within the reported a-priori bound\",\n \"Not bit-identical to the reference: never use for SHA-256 artifact identities, byte-parity evidence or determinism hashes\",\n \"Bound assumes a conforming WebGPU implementation (correctly rounded f32 add/multiply/divide, WGSL accuracy table)\",\n \"Execution speed depends on the adapter; software adapters do not represent GPU performance\"\n]);\n\nexport function cpuExecution(requestedBackend: ImageKernelBackend, fallbackReason: ImageKernelFallbackReason | null): ImageKernelExecution {\n return { backend: \"cpu-reference\", requestedBackend, fallbackReason, arithmetic: \"float64\", assurance: \"engine-reference\", fast: false,\n bitIdenticalToReference: true, errorBound: null, diagnosticsExact: true, diagnosticUncertaintySampleCount: 0, limitations: CPU_REFERENCE_LIMITATIONS };\n}\n\nexport function webGpuExecution(errorBound: ImageKernelExecution[\"errorBound\"], uncertainty: number): ImageKernelExecution {\n return { backend: \"webgpu-f32\", requestedBackend: \"webgpu-f32\", fallbackReason: null, arithmetic: \"float32\",\n assurance: \"approximation-with-a-priori-bound\", fast: true, bitIdenticalToReference: false, errorBound, diagnosticsExact: uncertainty === 0,\n diagnosticUncertaintySampleCount: uncertainty, limitations: WEBGPU_F32_LIMITATIONS };\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #593, #601: the default form of the display view's coupled GPU steps (no coupled step), statically part of the kernels; the
|
|
3
|
+
* coupled steps themselves (`wgsl-display-view-coupled.ts`) are loaded on demand, only for a view that asks for them.
|
|
4
|
+
*/
|
|
5
|
+
/** The functions' shared declarations (both forms). */
|
|
6
|
+
export declare const DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL = "\nstruct CpOut { hm: vec3<f32>, hv: vec3<f32>, a0: vec3<f32>, a1: vec3<f32>, a2: vec3<f32>, bm: vec3<f32>, bv: vec3<f32>, ju: f32, flag: bool, top: bool };\nfn cp_on() -> bool { return cp_k(0u) != 0.0; }\n";
|
|
7
|
+
/** The default form: no coupled step (the buffer holds one zero word). */
|
|
8
|
+
export declare const DISPLAY_VIEW_COUPLED_STUB_WGSL = "\nstruct CpOut { hm: vec3<f32>, hv: vec3<f32>, a0: vec3<f32>, a1: vec3<f32>, a2: vec3<f32>, bm: vec3<f32>, bv: vec3<f32>, ju: f32, flag: bool, top: bool };\nfn cp_on() -> bool { return cp_k(0u) != 0.0; }\n\nfn cp_zero() -> CpOut { var t: CpOut; return t; }\nfn cp_luminance() -> bool { return false; }\nfn cp_reconstruct() -> bool { return false; }\nfn cp_desaturate() -> bool { return false; }\nfn cp_reconstruct_reach(cm: vec3<f32>, cv: vec3<f32>) -> u32 { return 0u; }\nfn cp_tone(mu: vec3<f32>, s0: vec3<f32>, s1: vec3<f32>, s2: vec3<f32>, dmu: vec3<f32>, dsv: vec3<f32>) -> CpOut { return cp_zero(); }\nfn cp_chain(cm: vec3<f32>, c00: f32, c01: f32, c02: f32, c11: f32, c12: f32, c22: f32, dcm: vec3<f32>, dcv: vec3<f32>) -> CpOut { return cp_zero(); }\n";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* #593, #601: the default form of the display view's coupled GPU steps (no coupled step), statically part of the kernels; the
|
|
4
|
+
* coupled steps themselves (`wgsl-display-view-coupled.ts`) are loaded on demand, only for a view that asks for them.
|
|
5
|
+
*/
|
|
6
|
+
/** The functions' shared declarations (both forms). */
|
|
7
|
+
export const DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL = /* wgsl */ `
|
|
8
|
+
struct CpOut { hm: vec3<f32>, hv: vec3<f32>, a0: vec3<f32>, a1: vec3<f32>, a2: vec3<f32>, bm: vec3<f32>, bv: vec3<f32>, ju: f32, flag: bool, top: bool };
|
|
9
|
+
fn cp_on() -> bool { return cp_k(0u) != 0.0; }
|
|
10
|
+
`;
|
|
11
|
+
/** The default form: no coupled step (the buffer holds one zero word). */
|
|
12
|
+
export const DISPLAY_VIEW_COUPLED_STUB_WGSL = /* wgsl */ `${DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL}
|
|
13
|
+
fn cp_zero() -> CpOut { var t: CpOut; return t; }
|
|
14
|
+
fn cp_luminance() -> bool { return false; }
|
|
15
|
+
fn cp_reconstruct() -> bool { return false; }
|
|
16
|
+
fn cp_desaturate() -> bool { return false; }
|
|
17
|
+
fn cp_reconstruct_reach(cm: vec3<f32>, cv: vec3<f32>) -> u32 { return 0u; }
|
|
18
|
+
fn cp_tone(mu: vec3<f32>, s0: vec3<f32>, s1: vec3<f32>, s2: vec3<f32>, dmu: vec3<f32>, dsv: vec3<f32>) -> CpOut { return cp_zero(); }
|
|
19
|
+
fn cp_chain(cm: vec3<f32>, c00: f32, c01: f32, c02: f32, c11: f32, c12: f32, c22: f32, dcm: vec3<f32>, dcv: vec3<f32>) -> CpOut { return cp_zero(); }
|
|
20
|
+
`;
|
|
21
|
+
//# sourceMappingURL=wgsl-display-view-coupled-stub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wgsl-display-view-coupled-stub.js","sourceRoot":"","sources":["../../src/gpu/wgsl-display-view-coupled-stub.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,sCAAsC,GAAG,UAAU,CAAC;;;CAGhE,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,8BAA8B,GAAG,UAAU,CAAC,GAAG,sCAAsC;;;;;;;;CAQjG,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #593, #601: the default form of the display view's coupled GPU steps (no coupled step), statically part of the kernels; the\n * coupled steps themselves (`wgsl-display-view-coupled.ts`) are loaded on demand, only for a view that asks for them.\n */\n\n/** The functions' shared declarations (both forms). */\nexport const DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL = /* wgsl */ `\nstruct CpOut { hm: vec3<f32>, hv: vec3<f32>, a0: vec3<f32>, a1: vec3<f32>, a2: vec3<f32>, bm: vec3<f32>, bv: vec3<f32>, ju: f32, flag: bool, top: bool };\nfn cp_on() -> bool { return cp_k(0u) != 0.0; }\n`;\n\n/** The default form: no coupled step (the buffer holds one zero word). */\nexport const DISPLAY_VIEW_COUPLED_STUB_WGSL = /* wgsl */ `${DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL}\nfn cp_zero() -> CpOut { var t: CpOut; return t; }\nfn cp_luminance() -> bool { return false; }\nfn cp_reconstruct() -> bool { return false; }\nfn cp_desaturate() -> bool { return false; }\nfn cp_reconstruct_reach(cm: vec3<f32>, cv: vec3<f32>) -> u32 { return 0u; }\nfn cp_tone(mu: vec3<f32>, s0: vec3<f32>, s1: vec3<f32>, s2: vec3<f32>, dmu: vec3<f32>, dsv: vec3<f32>) -> CpOut { return cp_zero(); }\nfn cp_chain(cm: vec3<f32>, c00: f32, c01: f32, c02: f32, c11: f32, c12: f32, c22: f32, dcm: vec3<f32>, dcv: vec3<f32>) -> CpOut { return cp_zero(); }\n`;\n\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #593, #601 (2.22.0): the display view's coupled rendering steps on the GPU, shared by the Speed kernel and the Quality pixel
|
|
3
|
+
* pass: the luminance tone curves by conditioning on luminance (`cp_tone`), and `highlightMode: "reconstruct"` and the
|
|
4
|
+
* desaturation by the 3-D Gauss–Hermite rule over the development chain (`cp_chain`), the float32 forms of
|
|
5
|
+
* src/facade/display-view-rendering.ts with the request's fast or full rule. On demand: a kernel takes this text only for a view
|
|
6
|
+
* that asks for the coupled steps (its pipeline label ends in `+coupled`); every other view takes `DISPLAY_VIEW_COUPLED_STUB_WGSL`.
|
|
7
|
+
*
|
|
8
|
+
* Constants come from the kernel's aux table after its own entries (`cp_k(i)`, which each kernel defines), laid out as
|
|
9
|
+
* `DISPLAY_VIEW_COUPLED` (the host's `displayViewCoupledConstants`). The kernels define `phi` and `pq` (Abramowitz–Stegun) and
|
|
10
|
+
* `CLIP_REACH`; nothing else is shared.
|
|
11
|
+
*
|
|
12
|
+
* Error bounds (first order, float32, E = 2^-23; docs/WEBGPU.md, "Coupled rendering steps"). Every function returns, besides
|
|
13
|
+
* its moments, absolute bounds of the mean and the variance against the float64 reference and the slopes' relative bound:
|
|
14
|
+
* - tone: per node the clipped-normal terms (`CLIP_MEAN_U`·r·σ, `CLIP_VARIANCE_U`·r²·σ²), the conditional variance's
|
|
15
|
+
* cancellation (0.8·r·√(32·E·Σ_kk)), the node position through the slope and the ratio's relative error (log2 and exp2 per the
|
|
16
|
+
* WGSL accuracy table, the image-kernel accelerator's tone analysis); then one E per node of the sums;
|
|
17
|
+
* - chain: per node the rounding of the developed pixel (white balance and blend 8E of the largest channel, matrix 3E per term,
|
|
18
|
+
* tone ratio η, desaturation 8E) carried to the output through the node's own slopes, plus the node point's rounding through
|
|
19
|
+
* the slopes; then one E per node of the sums. The chain is continuous (clip, blend, maximum and desaturation all are), so a
|
|
20
|
+
* branch taken differently on either side of a kink moves the output by at most the slope times the input's error;
|
|
21
|
+
* - inputs: the caller's bounds of the means through |slopes|, of the variances through 0.8·|slopes|·√δ.
|
|
22
|
+
* A decision the reference takes on a threshold (the blend's reach, a component reaching 1 with the desaturation, the fast
|
|
23
|
+
* rule's range, a node at L = 0 where the ratio jumps) that the float32 values cannot settle sets the pixel's recompute flag:
|
|
24
|
+
* the host renders that display pixel with the reference.
|
|
25
|
+
*/
|
|
26
|
+
/** Layout of the coupled-constants buffer (float32 words). */
|
|
27
|
+
export declare const DISPLAY_VIEW_COUPLED: Readonly<{
|
|
28
|
+
on: 0;
|
|
29
|
+
luminance: 1;
|
|
30
|
+
tone: 2;
|
|
31
|
+
a: 3;
|
|
32
|
+
n: 4;
|
|
33
|
+
inverseWhite: 5;
|
|
34
|
+
ratioAtZero: 6;
|
|
35
|
+
weights: 7;
|
|
36
|
+
reconstruct: 10;
|
|
37
|
+
desaturate: 11;
|
|
38
|
+
fast: 12;
|
|
39
|
+
gains: 13;
|
|
40
|
+
clipLevel: 16;
|
|
41
|
+
matrix: 17;
|
|
42
|
+
chainPoints: 26;
|
|
43
|
+
chainX: 27;
|
|
44
|
+
chainW: 43;
|
|
45
|
+
hermiteX: 59;
|
|
46
|
+
hermiteW: 67;
|
|
47
|
+
legendreX: 75;
|
|
48
|
+
legendreW: 91;
|
|
49
|
+
size: 107;
|
|
50
|
+
}>;
|
|
51
|
+
/** The largest finite float32 stands for r(0+) = ∞ (Naka–Rushton below exponent 1). */
|
|
52
|
+
export declare const DISPLAY_VIEW_COUPLED_INFINITE_RATIO = 3.4028234663852886e+38;
|
|
53
|
+
/** The coupled steps (see the module comment). */
|
|
54
|
+
export declare const DISPLAY_VIEW_COUPLED_WGSL: string;
|
|
55
|
+
/** The host's coupled-constants buffer of a view (`DISPLAY_VIEW_COUPLED` layout), or the one-word stub buffer. */
|
|
56
|
+
export declare function displayViewCoupledConstants(setup: {
|
|
57
|
+
gains: readonly number[];
|
|
58
|
+
clipLevel: number;
|
|
59
|
+
matrix: readonly (readonly number[])[];
|
|
60
|
+
rendering?: {
|
|
61
|
+
toneCurve: string;
|
|
62
|
+
toneCurveParameters?: {
|
|
63
|
+
whiteLuminance: number;
|
|
64
|
+
} | {
|
|
65
|
+
exponent: number;
|
|
66
|
+
semiSaturation: number;
|
|
67
|
+
whiteLuminance: number;
|
|
68
|
+
};
|
|
69
|
+
highlightMode: string;
|
|
70
|
+
gamutHandling: string;
|
|
71
|
+
fast: boolean;
|
|
72
|
+
};
|
|
73
|
+
coupled?: {
|
|
74
|
+
chainRule: {
|
|
75
|
+
x: Float64Array;
|
|
76
|
+
w: Float64Array;
|
|
77
|
+
};
|
|
78
|
+
luminanceRule: {
|
|
79
|
+
x: Float64Array;
|
|
80
|
+
w: Float64Array;
|
|
81
|
+
};
|
|
82
|
+
ratioAtZero: number;
|
|
83
|
+
luminanceTone: boolean;
|
|
84
|
+
};
|
|
85
|
+
}, weights: readonly number[], legendre: {
|
|
86
|
+
x: ArrayLike<number>;
|
|
87
|
+
w: ArrayLike<number>;
|
|
88
|
+
}): Float32Array<ArrayBuffer>;
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* #593, #601 (2.22.0): the display view's coupled rendering steps on the GPU, shared by the Speed kernel and the Quality pixel
|
|
4
|
+
* pass: the luminance tone curves by conditioning on luminance (`cp_tone`), and `highlightMode: "reconstruct"` and the
|
|
5
|
+
* desaturation by the 3-D Gauss–Hermite rule over the development chain (`cp_chain`), the float32 forms of
|
|
6
|
+
* src/facade/display-view-rendering.ts with the request's fast or full rule. On demand: a kernel takes this text only for a view
|
|
7
|
+
* that asks for the coupled steps (its pipeline label ends in `+coupled`); every other view takes `DISPLAY_VIEW_COUPLED_STUB_WGSL`.
|
|
8
|
+
*
|
|
9
|
+
* Constants come from the kernel's aux table after its own entries (`cp_k(i)`, which each kernel defines), laid out as
|
|
10
|
+
* `DISPLAY_VIEW_COUPLED` (the host's `displayViewCoupledConstants`). The kernels define `phi` and `pq` (Abramowitz–Stegun) and
|
|
11
|
+
* `CLIP_REACH`; nothing else is shared.
|
|
12
|
+
*
|
|
13
|
+
* Error bounds (first order, float32, E = 2^-23; docs/WEBGPU.md, "Coupled rendering steps"). Every function returns, besides
|
|
14
|
+
* its moments, absolute bounds of the mean and the variance against the float64 reference and the slopes' relative bound:
|
|
15
|
+
* - tone: per node the clipped-normal terms (`CLIP_MEAN_U`·r·σ, `CLIP_VARIANCE_U`·r²·σ²), the conditional variance's
|
|
16
|
+
* cancellation (0.8·r·√(32·E·Σ_kk)), the node position through the slope and the ratio's relative error (log2 and exp2 per the
|
|
17
|
+
* WGSL accuracy table, the image-kernel accelerator's tone analysis); then one E per node of the sums;
|
|
18
|
+
* - chain: per node the rounding of the developed pixel (white balance and blend 8E of the largest channel, matrix 3E per term,
|
|
19
|
+
* tone ratio η, desaturation 8E) carried to the output through the node's own slopes, plus the node point's rounding through
|
|
20
|
+
* the slopes; then one E per node of the sums. The chain is continuous (clip, blend, maximum and desaturation all are), so a
|
|
21
|
+
* branch taken differently on either side of a kink moves the output by at most the slope times the input's error;
|
|
22
|
+
* - inputs: the caller's bounds of the means through |slopes|, of the variances through 0.8·|slopes|·√δ.
|
|
23
|
+
* A decision the reference takes on a threshold (the blend's reach, a component reaching 1 with the desaturation, the fast
|
|
24
|
+
* rule's range, a node at L = 0 where the ratio jumps) that the float32 values cannot settle sets the pixel's recompute flag:
|
|
25
|
+
* the host renders that display pixel with the reference.
|
|
26
|
+
*/
|
|
27
|
+
/** Layout of the coupled-constants buffer (float32 words). */
|
|
28
|
+
export const DISPLAY_VIEW_COUPLED = /* @__PURE__ */ Object.freeze({
|
|
29
|
+
on: 0, luminance: 1, tone: 2, a: 3, n: 4, inverseWhite: 5, ratioAtZero: 6, weights: 7, reconstruct: 10, desaturate: 11, fast: 12,
|
|
30
|
+
gains: 13, clipLevel: 16, matrix: 17, chainPoints: 26, chainX: 27, chainW: 43, hermiteX: 59, hermiteW: 67, legendreX: 75, legendreW: 91, size: 107
|
|
31
|
+
});
|
|
32
|
+
/** The largest finite float32 stands for r(0+) = ∞ (Naka–Rushton below exponent 1). */
|
|
33
|
+
export const DISPLAY_VIEW_COUPLED_INFINITE_RATIO = 3.4028234663852886e38;
|
|
34
|
+
import { DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL } from "./wgsl-display-view-coupled-stub.js";
|
|
35
|
+
const E_LITERAL = "1.1920928955078125e-7";
|
|
36
|
+
const C = DISPLAY_VIEW_COUPLED;
|
|
37
|
+
/** The coupled steps (see the module comment). */
|
|
38
|
+
export const DISPLAY_VIEW_COUPLED_WGSL = /* wgsl */ `${DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL}
|
|
39
|
+
const CP_E: f32 = ${E_LITERAL};
|
|
40
|
+
const CP_CLIP_MEAN_U: f32 = 1400.0;
|
|
41
|
+
const CP_CLIP_VARIANCE_U: f32 = 36000.0;
|
|
42
|
+
const CP_START: f32 = 0.8;
|
|
43
|
+
fn cp_luminance() -> bool { return cp_k(${C.luminance}u) != 0.0; }
|
|
44
|
+
fn cp_reconstruct() -> bool { return cp_k(${C.reconstruct}u) != 0.0; }
|
|
45
|
+
fn cp_desaturate() -> bool { return cp_k(${C.desaturate}u) != 0.0; }
|
|
46
|
+
fn cp_w() -> vec3<f32> { return vec3<f32>(cp_k(${C.weights}u), cp_k(${C.weights + 1}u), cp_k(${C.weights + 2}u)); }
|
|
47
|
+
fn cp_m(r: u32) -> vec3<f32> { return vec3<f32>(cp_k(${C.matrix}u + 3u * r), cp_k(${C.matrix + 1}u + 3u * r), cp_k(${C.matrix + 2}u + 3u * r)); }
|
|
48
|
+
fn cp_dot(a: vec3<f32>, b: vec3<f32>) -> f32 { return (a.x * b.x + a.y * b.y) + a.z * b.z; }
|
|
49
|
+
fn cp_unit(k: u32) -> vec3<f32> { return vec3<f32>(select(0.0, 1.0, k == 0u), select(0.0, 1.0, k == 1u), select(0.0, 1.0, k == 2u)); }
|
|
50
|
+
|
|
51
|
+
fn cp_ratio(l: f32) -> vec3<f32> {
|
|
52
|
+
let a = cp_k(${C.a}u);
|
|
53
|
+
if (cp_k(${C.tone}u) == 2.0) { let d = 1.0 + l; return vec3<f32>((1.0 + l * a) / d, (a - 1.0) / (d * d), 16.0); }
|
|
54
|
+
let n = cp_k(${C.n}u);
|
|
55
|
+
let iw = cp_k(${C.inverseWhite}u);
|
|
56
|
+
let lg = log2(a / l);
|
|
57
|
+
let z = n * lg;
|
|
58
|
+
var g = 0.0; var ug = 0.0;
|
|
59
|
+
if (z >= 0.0) { let p = exp2(-z); g = p / (1.0 + p); ug = 1.0 / (1.0 + p); }
|
|
60
|
+
else { let u = exp2(z); g = 1.0 / (1.0 + u); ug = u / (1.0 + u); }
|
|
61
|
+
let dz = n * (8.5 + 3.0 * abs(lg)) + abs(z);
|
|
62
|
+
return vec3<f32>(g * iw / l, g * (n * ug - 1.0) * iw / (l * l), 2.0 * (0.7 * dz + 3.0 + 2.0 * abs(z)) + 24.0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn cp_clip(m: f32, v: f32) -> vec4<f32> {
|
|
66
|
+
if (!(v > 0.0)) { let inside = m > 0.0 && m < 1.0; return vec4<f32>(clamp(m, 0.0, 1.0), 0.0, select(0.0, 1.0, inside), select(0.0, 1.0, m >= 1.0)); }
|
|
67
|
+
let s = sqrt(v);
|
|
68
|
+
let a = -m / s;
|
|
69
|
+
let b = (1.0 - m) / s;
|
|
70
|
+
var pa = 0.0; var da = 0.0;
|
|
71
|
+
if (a > CLIP_REACH) { pa = 1.0; } else if (!(a < -CLIP_REACH)) { pa = pq(a).x; da = phi(a); }
|
|
72
|
+
var pb = 1.0; var qb = 0.0; var db = 0.0;
|
|
73
|
+
if (b < -CLIP_REACH) { pb = 0.0; qb = 1.0; } else if (!(b > CLIP_REACH)) { let p = pq(b); pb = p.x; qb = p.y; db = phi(b); }
|
|
74
|
+
let inside = pb - pa;
|
|
75
|
+
let eg = a * pa + (da - db) + b * qb;
|
|
76
|
+
let eg2 = a * a * pa + inside - (b * db - a * da) + b * b * qb;
|
|
77
|
+
return vec4<f32>(m + s * eg, max(0.0, v * (eg2 - eg * eg)), inside, qb);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn cp_reconstruct_reach(cm: vec3<f32>, cv: vec3<f32>) -> u32 {
|
|
81
|
+
var state = 0u;
|
|
82
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
83
|
+
let top = cm[k] + CLIP_REACH * sqrt(max(0.0, cv[k]));
|
|
84
|
+
let margin = 16.0 * CP_E * (abs(cm[k]) + abs(top));
|
|
85
|
+
if (!(top < CP_START - margin)) { if (top >= CP_START + margin) { return 1u; } state = 2u; }
|
|
86
|
+
}
|
|
87
|
+
return state;
|
|
88
|
+
}
|
|
89
|
+
fn cp_tone(mu: vec3<f32>, s0: vec3<f32>, s1: vec3<f32>, s2: vec3<f32>, dmu: vec3<f32>, dsv: vec3<f32>) -> CpOut {
|
|
90
|
+
let w = cp_w();
|
|
91
|
+
let mul = cp_dot(w, mu);
|
|
92
|
+
let sw = vec3<f32>(cp_dot(s0, w), cp_dot(s1, w), cp_dot(s2, w));
|
|
93
|
+
let vl = cp_dot(w, sw);
|
|
94
|
+
let sl = select(0.0, sqrt(vl), vl > 0.0);
|
|
95
|
+
var beta = vec3<f32>(0.0);
|
|
96
|
+
if (sl > 0.0) { beta = sw / vl; }
|
|
97
|
+
let skk = vec3<f32>(s0.x, s1.y, s2.z);
|
|
98
|
+
let cv = max(vec3<f32>(0.0), skk - beta * sw);
|
|
99
|
+
var centre = clamp(mu, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
100
|
+
if (mul > 0.0) { centre = clamp(mu * cp_ratio(mul).x, vec3<f32>(0.0), vec3<f32>(1.0)); }
|
|
101
|
+
var t: CpOut;
|
|
102
|
+
var second = vec3<f32>(0.0);
|
|
103
|
+
var j = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));
|
|
104
|
+
let dl = 8.0 * CP_E * (abs(mul) + 9.0 * sl);
|
|
105
|
+
let hi = mul + CLIP_REACH * sl;
|
|
106
|
+
let ratio_margin = 64.0 * CP_E * (abs(mul) + sl);
|
|
107
|
+
|
|
108
|
+
let fast_test = mul - 2.0 * CLIP_REACH * sl;
|
|
109
|
+
let fast = cp_k(${C.fast}u) != 0.0 && (fast_test > 0.0 || hi < 0.0);
|
|
110
|
+
if (cp_k(${C.fast}u) != 0.0 && sl > 0.0 && abs(fast_test) <= ratio_margin) { t.flag = true; }
|
|
111
|
+
let split = mul - CLIP_REACH * sl < 0.0 && hi > 0.0;
|
|
112
|
+
if (sl > 0.0 && (abs(mul - CLIP_REACH * sl) <= ratio_margin || abs(hi) <= ratio_margin)) { t.flag = true; }
|
|
113
|
+
var pieces = 1u; var nodes = 1u;
|
|
114
|
+
if (sl > 0.0) { if (fast) { nodes = 8u; } else { nodes = 64u; if (split) { pieces = 2u; } } }
|
|
115
|
+
var count = 0.0;
|
|
116
|
+
for (var piece = 0u; piece < pieces; piece = piece + 1u) {
|
|
117
|
+
var za = -CLIP_REACH; var zb = CLIP_REACH;
|
|
118
|
+
if (pieces == 2u) { if (piece == 0u) { zb = -mul / sl; } else { za = -mul / sl; } }
|
|
119
|
+
for (var i = 0u; i < nodes; i = i + 1u) {
|
|
120
|
+
var l = mul; var weight = 1.0;
|
|
121
|
+
if (nodes == 8u) { l = mul + sl * cp_k(${C.hermiteX}u + i); weight = cp_k(${C.hermiteW}u + i); }
|
|
122
|
+
else if (nodes == 64u) {
|
|
123
|
+
let width = (zb - za) / 4.0;
|
|
124
|
+
let hw = width / 2.0;
|
|
125
|
+
let z = za + (f32(i / 16u) + 0.5) * width + hw * cp_k(${C.legendreX}u + (i % 16u));
|
|
126
|
+
l = mul + sl * z;
|
|
127
|
+
weight = hw * cp_k(${C.legendreW}u + (i % 16u)) * phi(z);
|
|
128
|
+
}
|
|
129
|
+
count = count + 1.0;
|
|
130
|
+
var r = 1.0; var rp = 0.0; var eta = 0.0;
|
|
131
|
+
if (l > 0.0) { let q = cp_ratio(l); r = q.x; rp = q.y; eta = q.z; }
|
|
132
|
+
t.ju = max(t.ju, eta);
|
|
133
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
134
|
+
let m = mu[k] + beta[k] * (l - mul);
|
|
135
|
+
let c = cp_clip(r * m, r * r * cv[k]);
|
|
136
|
+
let d = c.x - centre[k];
|
|
137
|
+
t.hm[k] = t.hm[k] + weight * c.x;
|
|
138
|
+
second[k] = second[k] + weight * (c.y + d * d);
|
|
139
|
+
|
|
140
|
+
let zv = r * r * cv[k];
|
|
141
|
+
if (zv > 0.0) {
|
|
142
|
+
let b = (1.0 - r * m) / sqrt(zv);
|
|
143
|
+
if (!(b > CLIP_REACH)) { t.top = true; }
|
|
144
|
+
if (abs(b - CLIP_REACH) <= 64.0 * CP_E * (CLIP_REACH + abs(r * m) / sqrt(zv))) { t.flag = true; }
|
|
145
|
+
} else if (r * m >= 1.0) { t.top = true; }
|
|
146
|
+
let e1 = c.x - c.w;
|
|
147
|
+
j[k] = j[k] + weight * (cp_unit(k) * (r * c.z) + select(vec3<f32>(0.0), w * (rp * e1 / r), rp != 0.0));
|
|
148
|
+
let err = CP_CLIP_MEAN_U * CP_E * r * sqrt(cv[k]) + 0.8 * r * sqrt(32.0 * CP_E * skk[k]) + c.z * (abs(rp * m) + r * abs(beta[k])) * dl + abs(c.x) * eta * CP_E;
|
|
149
|
+
t.bm[k] = t.bm[k] + weight * err;
|
|
150
|
+
t.bv[k] = t.bv[k] + weight * (2.0 * err + CP_CLIP_VARIANCE_U * CP_E * r * r * cv[k]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
let r0 = cp_k(${C.ratioAtZero}u);
|
|
155
|
+
if (sl > 0.0 && r0 != 1.0 && abs(mul) < CLIP_REACH * sl) {
|
|
156
|
+
let density = phi(mul / sl) / sl;
|
|
157
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
158
|
+
let m = mu[k] - beta[k] * mul;
|
|
159
|
+
let below = cp_clip(m, cv[k]).x;
|
|
160
|
+
var above = 0.0;
|
|
161
|
+
if (r0 > 1e30) { if (cv[k] > 0.0) { above = pq(m / sqrt(cv[k])).x; } else { above = select(0.0, 1.0, m > 0.0); } }
|
|
162
|
+
else { above = cp_clip(r0 * m, r0 * r0 * cv[k]).x; }
|
|
163
|
+
j[k] = j[k] + w * (density * (above - below));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
let shifted = t.hm - centre;
|
|
167
|
+
t.hv = max(vec3<f32>(0.0), second - shifted * shifted);
|
|
168
|
+
t.a0 = j[0]; t.a1 = j[1]; t.a2 = j[2];
|
|
169
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
170
|
+
let ak = abs(j[k]);
|
|
171
|
+
t.bm[k] = t.bm[k] + count * CP_E * abs(t.hm[k]) + cp_dot(ak, dmu) + 0.8 * cp_dot(ak, sqrt(dsv));
|
|
172
|
+
t.bv[k] = t.bv[k] + count * CP_E * second[k] + 2.0 * abs(shifted[k]) * t.bm[k] + cp_dot(ak * ak, dsv);
|
|
173
|
+
}
|
|
174
|
+
t.ju = (t.ju + 4.0 * count) * CP_E;
|
|
175
|
+
return t;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
struct CpNode { out: vec3<f32>, t0: vec3<f32>, t1: vec3<f32>, t2: vec3<f32>, err: vec3<f32>, flag: bool };
|
|
180
|
+
fn cp_develop(c: vec3<f32>) -> CpNode {
|
|
181
|
+
let g = vec3<f32>(cp_k(${C.gains}u), cp_k(${C.gains + 1}u), cp_k(${C.gains + 2}u));
|
|
182
|
+
let lc = cp_k(${C.clipLevel}u);
|
|
183
|
+
let u = c * g;
|
|
184
|
+
var b = u;
|
|
185
|
+
var jb = array<vec3<f32>, 3>(vec3<f32>(g.x, 0.0, 0.0), vec3<f32>(0.0, g.y, 0.0), vec3<f32>(0.0, 0.0, g.z));
|
|
186
|
+
var node: CpNode;
|
|
187
|
+
if (!cp_reconstruct()) {
|
|
188
|
+
for (var k = 0u; k < 3u; k = k + 1u) { if (u[k] > lc) { b[k] = lc; jb[k][k] = 0.0; } }
|
|
189
|
+
} else {
|
|
190
|
+
let s = select(select(c.z, c.y, c.y > c.z), select(c.z, c.x, c.x > c.z), c.x > c.y);
|
|
191
|
+
if (s > CP_START) {
|
|
192
|
+
let si = select(select(2u, 1u, c.y > c.z), select(2u, 0u, c.x > c.z), c.x > c.y);
|
|
193
|
+
let kk = min(u, vec3<f32>(lc));
|
|
194
|
+
let n = select(select(kk.z, kk.y, kk.y > kk.z), select(kk.z, kk.x, kk.x > kk.z), kk.x > kk.y);
|
|
195
|
+
let ni = select(select(2u, 1u, kk.y > kk.z), select(2u, 0u, kk.x > kk.z), kk.x > kk.y);
|
|
196
|
+
let ns = select(g[ni], 0.0, u[ni] > lc);
|
|
197
|
+
let t = select((s - CP_START) / (1.0 - CP_START), 1.0, s >= 1.0);
|
|
198
|
+
let ts = select(1.0 / (1.0 - CP_START), 0.0, s >= 1.0);
|
|
199
|
+
b = (1.0 - t) * u + t * n;
|
|
200
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
201
|
+
var row = cp_unit(k) * ((1.0 - t) * g[k]);
|
|
202
|
+
row = row + cp_unit(ni) * (t * ns) + cp_unit(si) * ((n - u[k]) * ts);
|
|
203
|
+
jb[k] = row;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
let eb = 8.0 * CP_E * max(abs(u.x), max(abs(u.y), abs(u.z)));
|
|
208
|
+
var y = vec3<f32>(0.0); var ey = vec3<f32>(0.0);
|
|
209
|
+
var mj = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));
|
|
210
|
+
for (var r = 0u; r < 3u; r = r + 1u) {
|
|
211
|
+
let m = cp_m(r);
|
|
212
|
+
y[r] = cp_dot(m, b);
|
|
213
|
+
ey[r] = cp_dot(abs(m), 3.0 * CP_E * abs(b) + vec3<f32>(eb));
|
|
214
|
+
mj[r] = m.x * jb[0] + m.y * jb[1] + m.z * jb[2];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
var z = y;
|
|
218
|
+
var jt = array<vec3<f32>, 3>(vec3<f32>(1.0, 0.0, 0.0), vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(0.0, 0.0, 1.0));
|
|
219
|
+
var eta = 0.0;
|
|
220
|
+
let w = cp_w();
|
|
221
|
+
if (cp_luminance()) {
|
|
222
|
+
let l = cp_dot(w, y);
|
|
223
|
+
let el = cp_dot(abs(w), ey) + 4.0 * CP_E * cp_dot(abs(w), abs(y));
|
|
224
|
+
if (cp_k(${C.ratioAtZero}u) != 1.0 && abs(l) <= el) { node.flag = true; }
|
|
225
|
+
if (l > 0.0) {
|
|
226
|
+
let q = cp_ratio(l);
|
|
227
|
+
eta = q.z;
|
|
228
|
+
z = y * q.x;
|
|
229
|
+
for (var k = 0u; k < 3u; k = k + 1u) { jt[k] = cp_unit(k) * q.x + w * (y[k] * q.y); }
|
|
230
|
+
}
|
|
231
|
+
} else if (cp_k(${C.tone}u) == 1.0) {
|
|
232
|
+
for (var k = 0u; k < 3u; k = k + 1u) { if (y[k] > 0.0) { z[k] = y[k] / (1.0 + y[k]); jt[k] = cp_unit(k) / ((1.0 + y[k]) * (1.0 + y[k])); } }
|
|
233
|
+
}
|
|
234
|
+
var ez = vec3<f32>(0.0);
|
|
235
|
+
for (var k = 0u; k < 3u; k = k + 1u) { ez[k] = cp_dot(abs(jt[k]), ey) + abs(z[k]) * (eta + 4.0) * CP_E; }
|
|
236
|
+
|
|
237
|
+
var jz = array<vec3<f32>, 3>(vec3<f32>(1.0, 0.0, 0.0), vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(0.0, 0.0, 1.0));
|
|
238
|
+
let ti = select(select(2u, 1u, z.y > z.z), select(2u, 0u, z.x > z.z), z.x > z.y);
|
|
239
|
+
let zt = z[ti];
|
|
240
|
+
if (cp_desaturate() && zt > 1.0) {
|
|
241
|
+
let lp = cp_dot(w, z);
|
|
242
|
+
if (lp >= 1.0) {
|
|
243
|
+
node.out = vec3<f32>(1.0);
|
|
244
|
+
node.err = vec3<f32>(0.0);
|
|
245
|
+
return node;
|
|
246
|
+
}
|
|
247
|
+
let d = zt - lp;
|
|
248
|
+
let a = (1.0 - lp) / d;
|
|
249
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
250
|
+
let da = (-w * d - (1.0 - lp) * (cp_unit(ti) - w)) / (d * d);
|
|
251
|
+
jz[k] = w * (1.0 - a) + cp_unit(k) * a + (z[k] - lp) * da;
|
|
252
|
+
}
|
|
253
|
+
z = lp + a * (z - lp);
|
|
254
|
+
var ez2 = vec3<f32>(0.0);
|
|
255
|
+
for (var k = 0u; k < 3u; k = k + 1u) { ez2[k] = cp_dot(abs(jz[k]), ez) + 8.0 * CP_E * (1.0 + abs(z[k])); }
|
|
256
|
+
ez = ez2;
|
|
257
|
+
}
|
|
258
|
+
node.out = clamp(z, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
259
|
+
node.err = ez;
|
|
260
|
+
var jo = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));
|
|
261
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
262
|
+
if (z[k] > 0.0 && z[k] < 1.0) { jo[k] = jz[k].x * jt[0] + jz[k].y * jt[1] + jz[k].z * jt[2]; }
|
|
263
|
+
}
|
|
264
|
+
node.t0 = jo[0].x * mj[0] + jo[0].y * mj[1] + jo[0].z * mj[2];
|
|
265
|
+
node.t1 = jo[1].x * mj[0] + jo[1].y * mj[1] + jo[1].z * mj[2];
|
|
266
|
+
node.t2 = jo[2].x * mj[0] + jo[2].y * mj[1] + jo[2].z * mj[2];
|
|
267
|
+
return node;
|
|
268
|
+
}
|
|
269
|
+
fn cp_chain(cm: vec3<f32>, c00: f32, c01: f32, c02: f32, c11: f32, c12: f32, c22: f32, dcm: vec3<f32>, dcv: vec3<f32>) -> CpOut {
|
|
270
|
+
let l00 = sqrt(max(0.0, c00));
|
|
271
|
+
let l10 = select(0.0, c01 / l00, l00 > 0.0);
|
|
272
|
+
let l20 = select(0.0, c02 / l00, l00 > 0.0);
|
|
273
|
+
let l11 = sqrt(max(0.0, c11 - l10 * l10));
|
|
274
|
+
let l21 = select(0.0, (c12 - l20 * l10) / l11, l11 > 0.0);
|
|
275
|
+
let l22 = sqrt(max(0.0, c22 - l20 * l20 - l21 * l21));
|
|
276
|
+
let any_noise = l00 > 0.0 || l11 > 0.0 || l22 > 0.0;
|
|
277
|
+
let second_axis = l11 > 0.0 || l21 != 0.0 || l22 > 0.0;
|
|
278
|
+
let n = select(1u, u32(cp_k(${C.chainPoints}u)), any_noise);
|
|
279
|
+
let nj = select(1u, n, second_axis);
|
|
280
|
+
let nk = select(1u, n, l22 > 0.0);
|
|
281
|
+
let centre = cp_develop(cm).out;
|
|
282
|
+
var t: CpOut;
|
|
283
|
+
var e = vec3<f32>(0.0); var q = vec3<f32>(0.0); var count = 0.0;
|
|
284
|
+
for (var i = 0u; i < n; i = i + 1u) {
|
|
285
|
+
let x0 = select(0.0, cp_k(${C.chainX}u + i), any_noise);
|
|
286
|
+
let wi = select(1.0, cp_k(${C.chainW}u + i), any_noise);
|
|
287
|
+
for (var jj = 0u; jj < nj; jj = jj + 1u) {
|
|
288
|
+
let x1 = select(0.0, cp_k(${C.chainX}u + jj), second_axis);
|
|
289
|
+
let wj = select(wi, wi * cp_k(${C.chainW}u + jj), second_axis);
|
|
290
|
+
for (var kk = 0u; kk < nk; kk = kk + 1u) {
|
|
291
|
+
let x2 = select(0.0, cp_k(${C.chainX}u + kk), l22 > 0.0);
|
|
292
|
+
let weight = select(wj, wj * cp_k(${C.chainW}u + kk), l22 > 0.0);
|
|
293
|
+
let c = vec3<f32>(cm.x + l00 * x0, cm.y + l10 * x0 + l11 * x1, cm.z + l20 * x0 + l21 * x1 + l22 * x2);
|
|
294
|
+
let dc = 3.0 * CP_E * (abs(cm) + vec3<f32>(abs(l00 * x0), abs(l10 * x0) + abs(l11 * x1), abs(l20 * x0) + abs(l21 * x1) + abs(l22 * x2)));
|
|
295
|
+
let node = cp_develop(c);
|
|
296
|
+
if (node.flag) { t.flag = true; }
|
|
297
|
+
count = count + 1.0;
|
|
298
|
+
let d = node.out - centre;
|
|
299
|
+
e = e + weight * node.out;
|
|
300
|
+
q = q + weight * (d * d);
|
|
301
|
+
t.a0 = t.a0 + weight * node.t0; t.a1 = t.a1 + weight * node.t1; t.a2 = t.a2 + weight * node.t2;
|
|
302
|
+
let err = node.err + vec3<f32>(cp_dot(abs(node.t0), dc), cp_dot(abs(node.t1), dc), cp_dot(abs(node.t2), dc));
|
|
303
|
+
t.bm = t.bm + weight * err;
|
|
304
|
+
t.bv = t.bv + 2.0 * (weight * err);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
let shifted = e - centre;
|
|
309
|
+
t.hm = e;
|
|
310
|
+
t.hv = max(vec3<f32>(0.0), q - shifted * shifted);
|
|
311
|
+
let rows = array<vec3<f32>, 3>(abs(t.a0), abs(t.a1), abs(t.a2));
|
|
312
|
+
for (var k = 0u; k < 3u; k = k + 1u) {
|
|
313
|
+
t.bm[k] = t.bm[k] + count * CP_E * abs(e[k]) + cp_dot(rows[k], dcm) + 0.8 * cp_dot(rows[k], sqrt(dcv));
|
|
314
|
+
t.bv[k] = t.bv[k] + count * CP_E * q[k] + 2.0 * abs(shifted[k]) * t.bm[k] + cp_dot(rows[k] * rows[k], dcv);
|
|
315
|
+
}
|
|
316
|
+
t.ju = (4.0 * count + 64.0) * CP_E;
|
|
317
|
+
return t;
|
|
318
|
+
}
|
|
319
|
+
`;
|
|
320
|
+
/** The host's coupled-constants buffer of a view (`DISPLAY_VIEW_COUPLED` layout), or the one-word stub buffer. */
|
|
321
|
+
export function displayViewCoupledConstants(setup, weights, legendre) {
|
|
322
|
+
const r = setup.rendering, k = setup.coupled;
|
|
323
|
+
if (!r || !k)
|
|
324
|
+
return new Float32Array(1);
|
|
325
|
+
const out = new Float32Array(C.size), p = r.toneCurveParameters;
|
|
326
|
+
out[C.on] = 1;
|
|
327
|
+
out[C.luminance] = k.luminanceTone ? 1 : 0;
|
|
328
|
+
out[C.tone] = r.toneCurve === "extended-reinhard-luminance" ? 2 : r.toneCurve === "naka-rushton-luminance" ? 3 : r.toneCurve === "positive-reinhard-per-channel" ? 1 : 0;
|
|
329
|
+
if (p && "exponent" in p) {
|
|
330
|
+
out[C.a] = p.semiSaturation;
|
|
331
|
+
out[C.n] = p.exponent;
|
|
332
|
+
out[C.inverseWhite] = 1 + (p.semiSaturation / p.whiteLuminance) ** p.exponent;
|
|
333
|
+
}
|
|
334
|
+
else if (p)
|
|
335
|
+
out[C.a] = 1 / (p.whiteLuminance * p.whiteLuminance);
|
|
336
|
+
out[C.ratioAtZero] = k.ratioAtZero === Infinity ? DISPLAY_VIEW_COUPLED_INFINITE_RATIO : k.ratioAtZero;
|
|
337
|
+
out.set(weights, C.weights);
|
|
338
|
+
out[C.reconstruct] = r.highlightMode === "reconstruct" ? 1 : 0;
|
|
339
|
+
out[C.desaturate] = r.gamutHandling === "desaturate-to-luminance" ? 1 : 0;
|
|
340
|
+
out[C.fast] = r.fast ? 1 : 0;
|
|
341
|
+
out.set(setup.gains, C.gains);
|
|
342
|
+
out[C.clipLevel] = setup.clipLevel;
|
|
343
|
+
out.set(setup.matrix.flat(), C.matrix);
|
|
344
|
+
out[C.chainPoints] = k.chainRule.x.length;
|
|
345
|
+
out.set(k.chainRule.x, C.chainX);
|
|
346
|
+
out.set(k.chainRule.w, C.chainW);
|
|
347
|
+
out.set(k.luminanceRule.x, C.hermiteX);
|
|
348
|
+
out.set(k.luminanceRule.w, C.hermiteW);
|
|
349
|
+
out.set(Array.from(legendre.x), C.legendreX);
|
|
350
|
+
out.set(Array.from(legendre.w), C.legendreW);
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=wgsl-display-view-coupled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wgsl-display-view-coupled.js","sourceRoot":"","sources":["../../src/gpu/wgsl-display-view-coupled.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #593, #601 (2.22.0): the display view's coupled rendering steps on the GPU, shared by the Speed kernel and the Quality pixel\n * pass: the luminance tone curves by conditioning on luminance (`cp_tone`), and `highlightMode: \"reconstruct\"` and the\n * desaturation by the 3-D Gauss–Hermite rule over the development chain (`cp_chain`), the float32 forms of\n * src/facade/display-view-rendering.ts with the request's fast or full rule. On demand: a kernel takes this text only for a view\n * that asks for the coupled steps (its pipeline label ends in `+coupled`); every other view takes `DISPLAY_VIEW_COUPLED_STUB_WGSL`.\n *\n * Constants come from the kernel's aux table after its own entries (`cp_k(i)`, which each kernel defines), laid out as\n * `DISPLAY_VIEW_COUPLED` (the host's `displayViewCoupledConstants`). The kernels define `phi` and `pq` (Abramowitz–Stegun) and\n * `CLIP_REACH`; nothing else is shared.\n *\n * Error bounds (first order, float32, E = 2^-23; docs/WEBGPU.md, \"Coupled rendering steps\"). Every function returns, besides\n * its moments, absolute bounds of the mean and the variance against the float64 reference and the slopes' relative bound:\n * - tone: per node the clipped-normal terms (`CLIP_MEAN_U`·r·σ, `CLIP_VARIANCE_U`·r²·σ²), the conditional variance's\n * cancellation (0.8·r·√(32·E·Σ_kk)), the node position through the slope and the ratio's relative error (log2 and exp2 per the\n * WGSL accuracy table, the image-kernel accelerator's tone analysis); then one E per node of the sums;\n * - chain: per node the rounding of the developed pixel (white balance and blend 8E of the largest channel, matrix 3E per term,\n * tone ratio η, desaturation 8E) carried to the output through the node's own slopes, plus the node point's rounding through\n * the slopes; then one E per node of the sums. The chain is continuous (clip, blend, maximum and desaturation all are), so a\n * branch taken differently on either side of a kink moves the output by at most the slope times the input's error;\n * - inputs: the caller's bounds of the means through |slopes|, of the variances through 0.8·|slopes|·√δ.\n * A decision the reference takes on a threshold (the blend's reach, a component reaching 1 with the desaturation, the fast\n * rule's range, a node at L = 0 where the ratio jumps) that the float32 values cannot settle sets the pixel's recompute flag:\n * the host renders that display pixel with the reference.\n */\n\n/** Layout of the coupled-constants buffer (float32 words). */\nexport const DISPLAY_VIEW_COUPLED = /* @__PURE__ */ Object.freeze({\n on: 0, luminance: 1, tone: 2, a: 3, n: 4, inverseWhite: 5, ratioAtZero: 6, weights: 7, reconstruct: 10, desaturate: 11, fast: 12,\n gains: 13, clipLevel: 16, matrix: 17, chainPoints: 26, chainX: 27, chainW: 43, hermiteX: 59, hermiteW: 67, legendreX: 75, legendreW: 91, size: 107\n});\n/** The largest finite float32 stands for r(0+) = ∞ (Naka–Rushton below exponent 1). */\nexport const DISPLAY_VIEW_COUPLED_INFINITE_RATIO = 3.4028234663852886e38;\n\nimport { DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL } from \"./wgsl-display-view-coupled-stub.js\";\n\nconst E_LITERAL = \"1.1920928955078125e-7\";\nconst C = DISPLAY_VIEW_COUPLED;\n\n/** The coupled steps (see the module comment). */\nexport const DISPLAY_VIEW_COUPLED_WGSL = /* wgsl */ `${DISPLAY_VIEW_COUPLED_DECLARATIONS_WGSL}\nconst CP_E: f32 = ${E_LITERAL};\nconst CP_CLIP_MEAN_U: f32 = 1400.0;\nconst CP_CLIP_VARIANCE_U: f32 = 36000.0;\nconst CP_START: f32 = 0.8;\nfn cp_luminance() -> bool { return cp_k(${C.luminance}u) != 0.0; }\nfn cp_reconstruct() -> bool { return cp_k(${C.reconstruct}u) != 0.0; }\nfn cp_desaturate() -> bool { return cp_k(${C.desaturate}u) != 0.0; }\nfn cp_w() -> vec3<f32> { return vec3<f32>(cp_k(${C.weights}u), cp_k(${C.weights + 1}u), cp_k(${C.weights + 2}u)); }\nfn cp_m(r: u32) -> vec3<f32> { return vec3<f32>(cp_k(${C.matrix}u + 3u * r), cp_k(${C.matrix + 1}u + 3u * r), cp_k(${C.matrix + 2}u + 3u * r)); }\nfn cp_dot(a: vec3<f32>, b: vec3<f32>) -> f32 { return (a.x * b.x + a.y * b.y) + a.z * b.z; }\nfn cp_unit(k: u32) -> vec3<f32> { return vec3<f32>(select(0.0, 1.0, k == 0u), select(0.0, 1.0, k == 1u), select(0.0, 1.0, k == 2u)); }\n// The tone ratio r(L) = f(L)/L, its slope r′ and its relative bound η in units of E (L > 0).\nfn cp_ratio(l: f32) -> vec3<f32> {\n let a = cp_k(${C.a}u);\n if (cp_k(${C.tone}u) == 2.0) { let d = 1.0 + l; return vec3<f32>((1.0 + l * a) / d, (a - 1.0) / (d * d), 16.0); }\n let n = cp_k(${C.n}u);\n let iw = cp_k(${C.inverseWhite}u);\n let lg = log2(a / l);\n let z = n * lg;\n var g = 0.0; var ug = 0.0;\n if (z >= 0.0) { let p = exp2(-z); g = p / (1.0 + p); ug = 1.0 / (1.0 + p); }\n else { let u = exp2(z); g = 1.0 / (1.0 + u); ug = u / (1.0 + u); }\n let dz = n * (8.5 + 3.0 * abs(lg)) + abs(z);\n return vec3<f32>(g * iw / l, g * (n * ug - 1.0) * iw / (l * l), 2.0 * (0.7 * dz + 3.0 + 2.0 * abs(z)) + 24.0);\n}\n// clip(Z, 0, 1) for Z ~ N(m, v), standardized (cancellation-free): (mean, variance, P(0 < Z < 1), P(Z ≥ 1)).\nfn cp_clip(m: f32, v: f32) -> vec4<f32> {\n if (!(v > 0.0)) { let inside = m > 0.0 && m < 1.0; return vec4<f32>(clamp(m, 0.0, 1.0), 0.0, select(0.0, 1.0, inside), select(0.0, 1.0, m >= 1.0)); }\n let s = sqrt(v);\n let a = -m / s;\n let b = (1.0 - m) / s;\n var pa = 0.0; var da = 0.0;\n if (a > CLIP_REACH) { pa = 1.0; } else if (!(a < -CLIP_REACH)) { pa = pq(a).x; da = phi(a); }\n var pb = 1.0; var qb = 0.0; var db = 0.0;\n if (b < -CLIP_REACH) { pb = 0.0; qb = 1.0; } else if (!(b > CLIP_REACH)) { let p = pq(b); pb = p.x; qb = p.y; db = phi(b); }\n let inside = pb - pa;\n let eg = a * pa + (da - db) + b * qb;\n let eg2 = a * a * pa + inside - (b * db - a * da) + b * b * qb;\n return vec4<f32>(m + s * eg, max(0.0, v * (eg2 - eg * eg)), inside, qb);\n}\n// Whether the reconstruct blend is within reach of channels with means cm and variances cv: 0 no, 1 yes, 2 undecided in float32.\nfn cp_reconstruct_reach(cm: vec3<f32>, cv: vec3<f32>) -> u32 {\n var state = 0u;\n for (var k = 0u; k < 3u; k = k + 1u) {\n let top = cm[k] + CLIP_REACH * sqrt(max(0.0, cv[k]));\n let margin = 16.0 * CP_E * (abs(cm[k]) + abs(top));\n if (!(top < CP_START - margin)) { if (top >= CP_START + margin) { return 1u; } state = 2u; }\n }\n return state;\n}\nfn cp_tone(mu: vec3<f32>, s0: vec3<f32>, s1: vec3<f32>, s2: vec3<f32>, dmu: vec3<f32>, dsv: vec3<f32>) -> CpOut {\n let w = cp_w();\n let mul = cp_dot(w, mu);\n let sw = vec3<f32>(cp_dot(s0, w), cp_dot(s1, w), cp_dot(s2, w));\n let vl = cp_dot(w, sw);\n let sl = select(0.0, sqrt(vl), vl > 0.0);\n var beta = vec3<f32>(0.0);\n if (sl > 0.0) { beta = sw / vl; }\n let skk = vec3<f32>(s0.x, s1.y, s2.z);\n let cv = max(vec3<f32>(0.0), skk - beta * sw);\n var centre = clamp(mu, vec3<f32>(0.0), vec3<f32>(1.0));\n if (mul > 0.0) { centre = clamp(mu * cp_ratio(mul).x, vec3<f32>(0.0), vec3<f32>(1.0)); }\n var t: CpOut;\n var second = vec3<f32>(0.0);\n var j = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));\n let dl = 8.0 * CP_E * (abs(mul) + 9.0 * sl);\n let hi = mul + CLIP_REACH * sl;\n let ratio_margin = 64.0 * CP_E * (abs(mul) + sl);\n // The fast rule's range test and the split at L = 0, as the reference takes them (undecided: recompute).\n let fast_test = mul - 2.0 * CLIP_REACH * sl;\n let fast = cp_k(${C.fast}u) != 0.0 && (fast_test > 0.0 || hi < 0.0);\n if (cp_k(${C.fast}u) != 0.0 && sl > 0.0 && abs(fast_test) <= ratio_margin) { t.flag = true; }\n let split = mul - CLIP_REACH * sl < 0.0 && hi > 0.0;\n if (sl > 0.0 && (abs(mul - CLIP_REACH * sl) <= ratio_margin || abs(hi) <= ratio_margin)) { t.flag = true; }\n var pieces = 1u; var nodes = 1u;\n if (sl > 0.0) { if (fast) { nodes = 8u; } else { nodes = 64u; if (split) { pieces = 2u; } } }\n var count = 0.0;\n for (var piece = 0u; piece < pieces; piece = piece + 1u) {\n var za = -CLIP_REACH; var zb = CLIP_REACH;\n if (pieces == 2u) { if (piece == 0u) { zb = -mul / sl; } else { za = -mul / sl; } }\n for (var i = 0u; i < nodes; i = i + 1u) {\n var l = mul; var weight = 1.0;\n if (nodes == 8u) { l = mul + sl * cp_k(${C.hermiteX}u + i); weight = cp_k(${C.hermiteW}u + i); }\n else if (nodes == 64u) {\n let width = (zb - za) / 4.0;\n let hw = width / 2.0;\n let z = za + (f32(i / 16u) + 0.5) * width + hw * cp_k(${C.legendreX}u + (i % 16u));\n l = mul + sl * z;\n weight = hw * cp_k(${C.legendreW}u + (i % 16u)) * phi(z);\n }\n count = count + 1.0;\n var r = 1.0; var rp = 0.0; var eta = 0.0;\n if (l > 0.0) { let q = cp_ratio(l); r = q.x; rp = q.y; eta = q.z; }\n t.ju = max(t.ju, eta);\n for (var k = 0u; k < 3u; k = k + 1u) {\n let m = mu[k] + beta[k] * (l - mul);\n let c = cp_clip(r * m, r * r * cv[k]);\n let d = c.x - centre[k];\n t.hm[k] = t.hm[k] + weight * c.x;\n second[k] = second[k] + weight * (c.y + d * d);\n // The reference's test of a component reaching 1 (b ≤ 8.5 standard deviations); undecided in float32: recompute.\n let zv = r * r * cv[k];\n if (zv > 0.0) {\n let b = (1.0 - r * m) / sqrt(zv);\n if (!(b > CLIP_REACH)) { t.top = true; }\n if (abs(b - CLIP_REACH) <= 64.0 * CP_E * (CLIP_REACH + abs(r * m) / sqrt(zv))) { t.flag = true; }\n } else if (r * m >= 1.0) { t.top = true; }\n let e1 = c.x - c.w;\n j[k] = j[k] + weight * (cp_unit(k) * (r * c.z) + select(vec3<f32>(0.0), w * (rp * e1 / r), rp != 0.0));\n let err = CP_CLIP_MEAN_U * CP_E * r * sqrt(cv[k]) + 0.8 * r * sqrt(32.0 * CP_E * skk[k]) + c.z * (abs(rp * m) + r * abs(beta[k])) * dl + abs(c.x) * eta * CP_E;\n t.bm[k] = t.bm[k] + weight * err;\n t.bv[k] = t.bv[k] + weight * (2.0 * err + CP_CLIP_VARIANCE_U * CP_E * r * r * cv[k]);\n }\n }\n }\n let r0 = cp_k(${C.ratioAtZero}u);\n if (sl > 0.0 && r0 != 1.0 && abs(mul) < CLIP_REACH * sl) {\n let density = phi(mul / sl) / sl;\n for (var k = 0u; k < 3u; k = k + 1u) {\n let m = mu[k] - beta[k] * mul;\n let below = cp_clip(m, cv[k]).x;\n var above = 0.0;\n if (r0 > 1e30) { if (cv[k] > 0.0) { above = pq(m / sqrt(cv[k])).x; } else { above = select(0.0, 1.0, m > 0.0); } }\n else { above = cp_clip(r0 * m, r0 * r0 * cv[k]).x; }\n j[k] = j[k] + w * (density * (above - below));\n }\n }\n let shifted = t.hm - centre;\n t.hv = max(vec3<f32>(0.0), second - shifted * shifted);\n t.a0 = j[0]; t.a1 = j[1]; t.a2 = j[2];\n for (var k = 0u; k < 3u; k = k + 1u) {\n let ak = abs(j[k]);\n t.bm[k] = t.bm[k] + count * CP_E * abs(t.hm[k]) + cp_dot(ak, dmu) + 0.8 * cp_dot(ak, sqrt(dsv));\n t.bv[k] = t.bv[k] + count * CP_E * second[k] + 2.0 * abs(shifted[k]) * t.bm[k] + cp_dot(ak * ak, dsv);\n }\n t.ju = (t.ju + 4.0 * count) * CP_E;\n return t;\n}\n// One node of the development chain at camera channels c: the rendered pixel, the slopes ∂out/∂c (rows), the rounding bound\n// per component (before the node point's own rounding) and whether a luminance at 0 makes the ratio's jump undecided.\nstruct CpNode { out: vec3<f32>, t0: vec3<f32>, t1: vec3<f32>, t2: vec3<f32>, err: vec3<f32>, flag: bool };\nfn cp_develop(c: vec3<f32>) -> CpNode {\n let g = vec3<f32>(cp_k(${C.gains}u), cp_k(${C.gains + 1}u), cp_k(${C.gains + 2}u));\n let lc = cp_k(${C.clipLevel}u);\n let u = c * g;\n var b = u;\n var jb = array<vec3<f32>, 3>(vec3<f32>(g.x, 0.0, 0.0), vec3<f32>(0.0, g.y, 0.0), vec3<f32>(0.0, 0.0, g.z));\n var node: CpNode;\n if (!cp_reconstruct()) {\n for (var k = 0u; k < 3u; k = k + 1u) { if (u[k] > lc) { b[k] = lc; jb[k][k] = 0.0; } }\n } else {\n let s = select(select(c.z, c.y, c.y > c.z), select(c.z, c.x, c.x > c.z), c.x > c.y);\n if (s > CP_START) {\n let si = select(select(2u, 1u, c.y > c.z), select(2u, 0u, c.x > c.z), c.x > c.y);\n let kk = min(u, vec3<f32>(lc));\n let n = select(select(kk.z, kk.y, kk.y > kk.z), select(kk.z, kk.x, kk.x > kk.z), kk.x > kk.y);\n let ni = select(select(2u, 1u, kk.y > kk.z), select(2u, 0u, kk.x > kk.z), kk.x > kk.y);\n let ns = select(g[ni], 0.0, u[ni] > lc);\n let t = select((s - CP_START) / (1.0 - CP_START), 1.0, s >= 1.0);\n let ts = select(1.0 / (1.0 - CP_START), 0.0, s >= 1.0);\n b = (1.0 - t) * u + t * n;\n for (var k = 0u; k < 3u; k = k + 1u) {\n var row = cp_unit(k) * ((1.0 - t) * g[k]);\n row = row + cp_unit(ni) * (t * ns) + cp_unit(si) * ((n - u[k]) * ts);\n jb[k] = row;\n }\n }\n }\n let eb = 8.0 * CP_E * max(abs(u.x), max(abs(u.y), abs(u.z)));\n var y = vec3<f32>(0.0); var ey = vec3<f32>(0.0);\n var mj = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));\n for (var r = 0u; r < 3u; r = r + 1u) {\n let m = cp_m(r);\n y[r] = cp_dot(m, b);\n ey[r] = cp_dot(abs(m), 3.0 * CP_E * abs(b) + vec3<f32>(eb));\n mj[r] = m.x * jb[0] + m.y * jb[1] + m.z * jb[2];\n }\n // Tone (luminance curve, positive Reinhard or identity).\n var z = y;\n var jt = array<vec3<f32>, 3>(vec3<f32>(1.0, 0.0, 0.0), vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(0.0, 0.0, 1.0));\n var eta = 0.0;\n let w = cp_w();\n if (cp_luminance()) {\n let l = cp_dot(w, y);\n let el = cp_dot(abs(w), ey) + 4.0 * CP_E * cp_dot(abs(w), abs(y));\n if (cp_k(${C.ratioAtZero}u) != 1.0 && abs(l) <= el) { node.flag = true; }\n if (l > 0.0) {\n let q = cp_ratio(l);\n eta = q.z;\n z = y * q.x;\n for (var k = 0u; k < 3u; k = k + 1u) { jt[k] = cp_unit(k) * q.x + w * (y[k] * q.y); }\n }\n } else if (cp_k(${C.tone}u) == 1.0) {\n for (var k = 0u; k < 3u; k = k + 1u) { if (y[k] > 0.0) { z[k] = y[k] / (1.0 + y[k]); jt[k] = cp_unit(k) / ((1.0 + y[k]) * (1.0 + y[k])); } }\n }\n var ez = vec3<f32>(0.0);\n for (var k = 0u; k < 3u; k = k + 1u) { ez[k] = cp_dot(abs(jt[k]), ey) + abs(z[k]) * (eta + 4.0) * CP_E; }\n // Desaturation toward the luminance (#601).\n var jz = array<vec3<f32>, 3>(vec3<f32>(1.0, 0.0, 0.0), vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(0.0, 0.0, 1.0));\n let ti = select(select(2u, 1u, z.y > z.z), select(2u, 0u, z.x > z.z), z.x > z.y);\n let zt = z[ti];\n if (cp_desaturate() && zt > 1.0) {\n let lp = cp_dot(w, z);\n if (lp >= 1.0) {\n node.out = vec3<f32>(1.0);\n node.err = vec3<f32>(0.0);\n return node;\n }\n let d = zt - lp;\n let a = (1.0 - lp) / d;\n for (var k = 0u; k < 3u; k = k + 1u) {\n let da = (-w * d - (1.0 - lp) * (cp_unit(ti) - w)) / (d * d);\n jz[k] = w * (1.0 - a) + cp_unit(k) * a + (z[k] - lp) * da;\n }\n z = lp + a * (z - lp);\n var ez2 = vec3<f32>(0.0);\n for (var k = 0u; k < 3u; k = k + 1u) { ez2[k] = cp_dot(abs(jz[k]), ez) + 8.0 * CP_E * (1.0 + abs(z[k])); }\n ez = ez2;\n }\n node.out = clamp(z, vec3<f32>(0.0), vec3<f32>(1.0));\n node.err = ez;\n var jo = array<vec3<f32>, 3>(vec3<f32>(0.0), vec3<f32>(0.0), vec3<f32>(0.0));\n for (var k = 0u; k < 3u; k = k + 1u) {\n if (z[k] > 0.0 && z[k] < 1.0) { jo[k] = jz[k].x * jt[0] + jz[k].y * jt[1] + jz[k].z * jt[2]; }\n }\n node.t0 = jo[0].x * mj[0] + jo[0].y * mj[1] + jo[0].z * mj[2];\n node.t1 = jo[1].x * mj[0] + jo[1].y * mj[1] + jo[1].z * mj[2];\n node.t2 = jo[2].x * mj[0] + jo[2].y * mj[1] + jo[2].z * mj[2];\n return node;\n}\nfn cp_chain(cm: vec3<f32>, c00: f32, c01: f32, c02: f32, c11: f32, c12: f32, c22: f32, dcm: vec3<f32>, dcv: vec3<f32>) -> CpOut {\n let l00 = sqrt(max(0.0, c00));\n let l10 = select(0.0, c01 / l00, l00 > 0.0);\n let l20 = select(0.0, c02 / l00, l00 > 0.0);\n let l11 = sqrt(max(0.0, c11 - l10 * l10));\n let l21 = select(0.0, (c12 - l20 * l10) / l11, l11 > 0.0);\n let l22 = sqrt(max(0.0, c22 - l20 * l20 - l21 * l21));\n let any_noise = l00 > 0.0 || l11 > 0.0 || l22 > 0.0;\n let second_axis = l11 > 0.0 || l21 != 0.0 || l22 > 0.0;\n let n = select(1u, u32(cp_k(${C.chainPoints}u)), any_noise);\n let nj = select(1u, n, second_axis);\n let nk = select(1u, n, l22 > 0.0);\n let centre = cp_develop(cm).out;\n var t: CpOut;\n var e = vec3<f32>(0.0); var q = vec3<f32>(0.0); var count = 0.0;\n for (var i = 0u; i < n; i = i + 1u) {\n let x0 = select(0.0, cp_k(${C.chainX}u + i), any_noise);\n let wi = select(1.0, cp_k(${C.chainW}u + i), any_noise);\n for (var jj = 0u; jj < nj; jj = jj + 1u) {\n let x1 = select(0.0, cp_k(${C.chainX}u + jj), second_axis);\n let wj = select(wi, wi * cp_k(${C.chainW}u + jj), second_axis);\n for (var kk = 0u; kk < nk; kk = kk + 1u) {\n let x2 = select(0.0, cp_k(${C.chainX}u + kk), l22 > 0.0);\n let weight = select(wj, wj * cp_k(${C.chainW}u + kk), l22 > 0.0);\n let c = vec3<f32>(cm.x + l00 * x0, cm.y + l10 * x0 + l11 * x1, cm.z + l20 * x0 + l21 * x1 + l22 * x2);\n let dc = 3.0 * CP_E * (abs(cm) + vec3<f32>(abs(l00 * x0), abs(l10 * x0) + abs(l11 * x1), abs(l20 * x0) + abs(l21 * x1) + abs(l22 * x2)));\n let node = cp_develop(c);\n if (node.flag) { t.flag = true; }\n count = count + 1.0;\n let d = node.out - centre;\n e = e + weight * node.out;\n q = q + weight * (d * d);\n t.a0 = t.a0 + weight * node.t0; t.a1 = t.a1 + weight * node.t1; t.a2 = t.a2 + weight * node.t2;\n let err = node.err + vec3<f32>(cp_dot(abs(node.t0), dc), cp_dot(abs(node.t1), dc), cp_dot(abs(node.t2), dc));\n t.bm = t.bm + weight * err;\n t.bv = t.bv + 2.0 * (weight * err);\n }\n }\n }\n let shifted = e - centre;\n t.hm = e;\n t.hv = max(vec3<f32>(0.0), q - shifted * shifted);\n let rows = array<vec3<f32>, 3>(abs(t.a0), abs(t.a1), abs(t.a2));\n for (var k = 0u; k < 3u; k = k + 1u) {\n t.bm[k] = t.bm[k] + count * CP_E * abs(e[k]) + cp_dot(rows[k], dcm) + 0.8 * cp_dot(rows[k], sqrt(dcv));\n t.bv[k] = t.bv[k] + count * CP_E * q[k] + 2.0 * abs(shifted[k]) * t.bm[k] + cp_dot(rows[k] * rows[k], dcv);\n }\n t.ju = (4.0 * count + 64.0) * CP_E;\n return t;\n}\n`;\n\n/** The host's coupled-constants buffer of a view (`DISPLAY_VIEW_COUPLED` layout), or the one-word stub buffer. */\nexport function displayViewCoupledConstants(setup: {\n gains: readonly number[]; clipLevel: number; matrix: readonly (readonly number[])[];\n rendering?: { toneCurve: string; toneCurveParameters?: { whiteLuminance: number } | { exponent: number; semiSaturation: number; whiteLuminance: number }; highlightMode: string; gamutHandling: string; fast: boolean };\n coupled?: { chainRule: { x: Float64Array; w: Float64Array }; luminanceRule: { x: Float64Array; w: Float64Array }; ratioAtZero: number; luminanceTone: boolean };\n}, weights: readonly number[], legendre: { x: ArrayLike<number>; w: ArrayLike<number> }): Float32Array<ArrayBuffer> {\n const r = setup.rendering, k = setup.coupled;\n if (!r || !k) return new Float32Array(1);\n const out = new Float32Array(C.size), p = r.toneCurveParameters;\n out[C.on] = 1; out[C.luminance] = k.luminanceTone ? 1 : 0;\n out[C.tone] = r.toneCurve === \"extended-reinhard-luminance\" ? 2 : r.toneCurve === \"naka-rushton-luminance\" ? 3 : r.toneCurve === \"positive-reinhard-per-channel\" ? 1 : 0;\n if (p && \"exponent\" in p) {\n out[C.a] = p.semiSaturation; out[C.n] = p.exponent; out[C.inverseWhite] = 1 + (p.semiSaturation / p.whiteLuminance) ** p.exponent;\n } else if (p) out[C.a] = 1 / (p.whiteLuminance * p.whiteLuminance);\n out[C.ratioAtZero] = k.ratioAtZero === Infinity ? DISPLAY_VIEW_COUPLED_INFINITE_RATIO : k.ratioAtZero;\n out.set(weights, C.weights);\n out[C.reconstruct] = r.highlightMode === \"reconstruct\" ? 1 : 0; out[C.desaturate] = r.gamutHandling === \"desaturate-to-luminance\" ? 1 : 0; out[C.fast] = r.fast ? 1 : 0;\n out.set(setup.gains, C.gains); out[C.clipLevel] = setup.clipLevel; out.set(setup.matrix.flat(), C.matrix);\n out[C.chainPoints] = k.chainRule.x.length; out.set(k.chainRule.x, C.chainX); out.set(k.chainRule.w, C.chainW);\n out.set(k.luminanceRule.x, C.hermiteX); out.set(k.luminanceRule.w, C.hermiteW);\n out.set(Array.from(legendre.x), C.legendreX); out.set(Array.from(legendre.w), C.legendreW);\n return out;\n}\n"]}
|