@photivra/engine 2.13.0 → 2.14.1
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 +75 -1
- package/CITATION.cff +1 -1
- package/NOTICE +5 -4
- package/README.md +10 -8
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +16 -10
- package/dist/camera/lens-projection.js +2 -1
- package/dist/camera/lens-projection.js.map +1 -1
- package/dist/capture/dng-import.js +1 -1
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/host-yield-cadence.js +1 -1
- package/dist/capture/host-yield-cadence.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.js +1 -1
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-raw.js +1 -1
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/packed-sensor-raw.js +3 -3
- package/dist/capture/packed-sensor-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +1 -1
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/capture-color.js +3 -3
- package/dist/color/capture-color.js.map +1 -1
- package/dist/color/chromatic-adaptation.js +2 -2
- package/dist/color/chromatic-adaptation.js.map +1 -1
- package/dist/color/color-matching-functions.js +1 -1
- package/dist/color/color-matching-functions.js.map +1 -1
- package/dist/color/colorimetry-math.js +2 -2
- package/dist/color/colorimetry-math.js.map +1 -1
- package/dist/color/illuminants.js +3 -3
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/rgb-color-spaces.js +1 -1
- package/dist/color/rgb-color-spaces.js.map +1 -1
- package/dist/color/sensor-color-development.js +1 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/core/record-validation.js +3 -3
- package/dist/core/record-validation.js.map +1 -1
- 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-prime-lenses.js +3 -3
- package/dist/equipment/generic-prime-lenses.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +23 -1
- package/dist/equipment/generic-tier-assets.js +58 -30
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.d.ts +6 -3
- package/dist/equipment/generic-tier-presets.js +26 -13
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +2 -1
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -1
- package/dist/exposure/flash.js.map +1 -1
- package/dist/facade/chart-material-layers.js +2 -2
- package/dist/facade/chart-material-layers.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +1 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +45 -8
- package/dist/facade/chart-scenes.js +101 -44
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/color-patches.d.ts +129 -0
- package/dist/facade/color-patches.js +209 -0
- package/dist/facade/color-patches.js.map +1 -0
- package/dist/facade/display-view-gpu.js +1 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/scene-radiance.js +2 -2
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +47 -3
- package/dist/facade/simulate-capture.js +71 -22
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +2 -2
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +2 -2
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.js +1 -1
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/temporal-sum-gpu.js +23 -23
- package/dist/gpu/webgpu-runtime.js +1 -1
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
- package/dist/gpu/wgsl-chart-kernel.js +117 -117
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.js +505 -505
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-kernels.js +203 -203
- package/dist/gpu/wgsl-kernels.js.map +1 -1
- package/dist/gpu/wgsl-sensor-kernel.js +302 -302
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/motion/temporal-exposure-sum.js +1 -1
- package/dist/motion/temporal-exposure-sum.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.js +4 -4
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/front-of-lens-filter.js +1 -1
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/irradiance-convolution.js +1 -1
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/output/native-capture-sdr.js +1 -1
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/png-encoder.js +2 -2
- package/dist/output/png-encoder.js.map +1 -1
- package/dist/output/print-contrast-reference.js +2 -2
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-jpeg.js +1 -1
- package/dist/output/print-jpeg.js.map +1 -1
- package/dist/sensor/fixed-pattern-noise.js +1 -1
- package/dist/sensor/fixed-pattern-noise.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -1
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/docs/API_REFERENCE.md +29 -1
- package/docs/QUICK_START.md +40 -5
- package/docs/USAGE.md +35 -1
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-tier-assets.md +41 -1
- package/docs/api/equipment-generic-tier-presets.md +11 -3
- package/docs/api/exports.json +107 -2
- package/docs/api/facade-chart-scenes.md +36 -5
- package/docs/api/facade-color-patches.md +150 -0
- package/docs/api/facade-simulate-capture.md +52 -2
- package/llms.txt +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wgsl-sensor-kernel.js","sourceRoot":"","sources":["../../src/gpu/wgsl-sensor-kernel.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * WGSL kernel for the `webgpu-f32` sensor stage (#277, #471), one invocation per native site. It evaluates the\n * reference `simulatePackedSensorSite` (packed-sensor-raw.ts) with the same seeded noise model\n * (`photivra-native-raw-noise` 0.4.0):\n * - the MurmurHash3_x86_32 site seeds, fmix32 scrambling and xorshift32 streams are 32-bit integer\n * arithmetic, so every random state is bit-identical to the reference;\n * - every comparison against a uniform value is made exactly on the integer state where the reference\n * value is an exact dyadic rational;\n * - every other decision (Poisson multiplication-method threshold, PTRS floor and acceptance tests,\n * physical and pre-ADC limits, ADC and digital-gain rounding) is evaluated in float32 together with an\n * a-priori bound on how far the float32 value can be from the float64 reference value. A site where any\n * decision falls inside its bound is flagged. An unflagged site therefore makes every decision exactly\n * as the reference does, and its code and flags equal the reference.\n * - Fixed-pattern noise and additional stored-charge components (2.0): the host evaluates the\n * fixed-pattern values in float64 with the model's own functions and packs, per site, the Poisson means\n * already multiplied by the PRNU and dark factors (exactly the reference's products), the pre-ADC limit\n * minus the readout offset and the offset in codes. Additional Poisson components are drawn from the\n * charge stream after photo and dark charge, in declaration order; deterministic components enter as an\n * exact integer part plus a float32 fraction, with their own bounds.\n *\n * Arithmetic assumptions (W3C WGSL, \"Floating Point Accuracy\" and \"Reassociation and Fusion\"; every margin\n * is derived in docs/WEBGPU.md, \"Sensor-stage margins\"):\n * - E = 2^-23. WGSL specifies no rounding direction, so a \"correctly rounded\" operation (+ − ×, floor, f32(u32))\n * is within one ULP of the exact value, that is ≤ E relative; `/` is 2.5 ULP (≤ 2.5E); `sqrt` inherits\n * 1.0 / inverseSqrt(x) (≤ 4.5E); `exp` is (3 + 2|x|) ULP; `log` is 3 ULP outside [0.5, 2] and 2^-21\n * absolute inside. WGSL's `cos` has only a 2^-11 absolute bound, so the kernel uses its own\n * octant-reduced polynomials.\n * - No decision depends on correctly rounded division: 1/gain and the digital gain arrive from the host as\n * exact 12-bit pieces, INL segments as host-computed slopes, and every quotient that remains (Box–Muller\n * logarithm, PTRS) carries the 2.5 ULP bound.\n * - WGSL lets an implementation reassociate. The integer parts of the ADC input are therefore formed in\n * integer arithmetic, and every floating-point sum carries the order-independent bound\n * (n − 1)·E·Σ|terms| (Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., §4.2), so the\n * sums' margins hold under any association. Fusing a multiply and an add only removes a rounding.\n * - Every margin is then doubled (`MARGIN`), which covers the second-order terms dropped in the derivations.\n */\n\n/** Output word: code in bits 0–15, saturation flags in bits 16–19, the flagged (uncertain) bit 20. */\nexport const SENSOR_OUTPUT = Object.freeze({ codeMask: 0xffff, flagShift: 16, uncertainBit: 1 << 20 });\n/**\n * Auxiliary table layout (float32 words). A gain's four 12-bit pieces are stored as `SENSOR_PIECE_WORDS` words\n * each: the integer mantissa m (< 2^12), the exponent s and the exact scale 2^s, so that piece = m·2^s. The INL\n * table holds the knot codes, the knot offsets and the host-computed segment slopes.\n */\nexport const SENSOR_AUX = Object.freeze({ stirlingRemainder: 0, sigma: 256, inversePieces: 272, digitalPieces: 284, knots: 296 });\nexport const SENSOR_PIECE_WORDS = 3;\nexport const SENSOR_WORKGROUP_SIZE = 256;\n/**\n * Per-site packed words: photo word, photo fraction bits, dark word, dark fraction bits; then 2 words per\n * additional Poisson component; then, with deterministic components, their sum (integer part, fraction\n * bits); then, with fixed-pattern noise, (threshold − offset) and offset/gain as (floor, fraction bits).\n */\nexport const SENSOR_SITE_WORDS = 4;\n/** Bits of `Params.extras`. */\nexport const SENSOR_EXTRAS = Object.freeze({ deterministic: 1, fixedPattern: 2 });\n/** Partial charge sums saturate here: above every supported capacity (2^24), so no decision changes. */\nexport const SENSOR_REALIZED_CLAMP = 2 ** 30;\n/** Mean categories in bits 30–31 of a mean word, mirroring `samplePoisson`'s branches. */\nexport const SENSOR_MEAN_CATEGORY = Object.freeze({ zero: 0, multiplication: 1, transformedRejection: 2, normal: 3 });\n/** One WGSL rounding (one ULP, either direction) is at most this relative error: 2^-23. */\nexport const SENSOR_ROUNDING = 2 ** -23;\n/** |z_f32 − z_f64| ≤ SENSOR_NORMAL_ERROR·E·radius for the Box–Muller normal (docs/WEBGPU.md). */\nexport const SENSOR_NORMAL_ERROR = 20;\n\n/** Smallest integer m with (m + 0.5)/2^32 ≥ t, so `uS ≥ t` ⇔ `m ≥ result` exactly. */\nexport function uniformThresholdInteger(t: number): number {\n return Math.ceil(t * 2 ** 32 - 0.5);\n}\n\nconst SIN_COEFFICIENTS = [-1 / 6, 1 / 120, -1 / 5040, 1 / 362880, -1 / 39916800];\nconst COS_COEFFICIENTS = [-1 / 2, 1 / 24, -1 / 720, 1 / 40320, -1 / 3628800, 1 / 479001600];\nconst literal = (value: number): string => {\n const text = String(Math.fround(value));\n return /[.e]/u.test(text) ? text : `${text}.0`;\n};\n\nexport const SENSOR_RAW_WGSL = /* wgsl */ `\nconst E: f32 = 1.1920928955078125e-7;\nconst INV32: f32 = 2.3283064365386963e-10;\nconst TWO_PI_INV32: f32 = ${literal(2 * Math.PI / 2 ** 32)};\nconst MARGIN: f32 = 2.0;\nconst CPU_REL: f32 = 7.105427357601002e-15;\nconst LOG_ABS: f32 = 4.76837158203125e-7;\nconst Z_ERR: f32 = ${literal(SENSOR_NORMAL_ERROR)};\nstruct Params {\n count: u32, row0: u32, region_w: u32, native_w: u32,\n region_x: u32, region_y: u32, seed: u32, components: u32,\n knots: u32, digital: u32, saturation: u32, adc_max: u32,\n capacity_floor: u32, threshold_floor: u32, black: u32, sat_quantized: i32,\n capacity_frac: f32, threshold_frac: f32, inv_gain: f32, inv_residual: f32,\n digital_gain: f32, digital_residual: f32, inl_slope: f32, inl_offset: f32,\n site_words: u32, poisson_count: u32, extras: u32, pad0: u32,\n fpn_e_slack: f32, fpn_c_slack: f32, inl_knot: f32, pad2: f32,\n};\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> means: array<u32>;\n@group(0) @binding(2) var<storage, read> aux: array<f32>;\n@group(0) @binding(3) var<storage, read_write> out: array<u32>;\n\nvar<private> state: u32;\nvar<private> uncertain: bool;\nvar<private> radius: f32;\nvar<private> ip: i32;\nvar<private> fp: f32;\n\nfn fmix(value: u32) -> u32 {\n var h = value;\n h = h ^ (h >> 16u); h = h * 0x85ebca6bu; h = h ^ (h >> 13u); h = h * 0xc2b2ae35u; h = h ^ (h >> 16u);\n return h;\n}\nfn mix_block(hash: u32, block: u32) -> u32 {\n var k = block * 0xcc9e2d51u;\n k = (k << 15u) | (k >> 17u);\n k = k * 0x1b873593u;\n var h = hash ^ k;\n h = (h << 13u) | (h >> 19u);\n return h * 5u + 0xe6546b64u;\n}\nfn site_seed(seed: u32, index: u32, stream: u32) -> u32 {\n return fmix(mix_block(mix_block(mix_block(0u, seed), index), stream) ^ 12u);\n}\nfn start(seed: u32) {\n state = fmix(seed ^ 0x9e3779b9u);\n if (state == 0u) { state = 0x6d2b79f5u; }\n}\nfn next_bits() -> u32 {\n state = state ^ (state << 13u); state = state ^ (state >> 17u); state = state ^ (state << 5u);\n return state;\n}\n// (bits + 0.5)/2^32 with two roundings (the conversion and the sum; the power-of-two scale is exact): ≤ 2E.\nfn unit(bits: u32) -> f32 { return (f32(bits) + 0.5) * INV32; }\nfn check(distance: f32, margin: f32) { if (abs(distance) <= margin) { uncertain = true; } }\n\n// ln((bits + 0.5)/2^32), relative error ≤ 11E: WGSL log below 1/2 (≤ 6E), else 2·atanh(r) with r = −w/(2 − w)\n// on the exact 1 − unit (division 2.5E; ≤ 10.5E in all).\nfn ln_unit(bits: u32) -> f32 {\n if (bits < 0x80000000u) { return log(unit(bits)); }\n let w = (f32(0xffffffffu - bits) + 0.5) * INV32;\n let r = -w / (2.0 - w);\n let r2 = r * r;\n var p = ${literal(1 / 23)};\n ${[21, 19, 17, 15, 13, 11, 9, 7, 5, 3].map(n => `p = p * r2 + ${literal(1 / n)};`).join(\" \")}\n p = p * r2 + 1.0;\n return 2.0 * r * p;\n}\n// cos(2π (bits + 0.5)/2^32): exact octant reduction on the integer, Taylor polynomials on [0, π/4]; absolute error ≤ 8E.\nfn cos_unit(bits: u32) -> f32 {\n let octant = bits >> 29u;\n let low = bits & 0x1fffffffu;\n let even = (octant & 1u) == 0u;\n var g = (f32(0x1fffffffu - low) + 0.5) * TWO_PI_INV32;\n if (even) { g = (f32(low) + 0.5) * TWO_PI_INV32; }\n let g2 = g * g;\n var c = ${literal(COS_COEFFICIENTS[5]!)};\n ${[4, 3, 2, 1, 0].map(j => `c = c * g2 + ${literal(COS_COEFFICIENTS[j]!)};`).join(\" \")}\n c = c * g2 + 1.0;\n var s = ${literal(SIN_COEFFICIENTS[4]!)};\n ${[3, 2, 1, 0].map(j => `s = s * g2 + ${literal(SIN_COEFFICIENTS[j]!)};`).join(\" \")}\n s = g * (s * g2 + 1.0);\n let cb = select(s, c, even);\n let sb = select(c, s, even);\n let q = octant >> 1u;\n if (q == 0u) { return cb; }\n if (q == 1u) { return -sb; }\n if (q == 2u) { return -cb; }\n return sb;\n}\n// Box–Muller, one value per two uniforms: radius = sqrt(−2 ln u1) (≤ 10E relative), times cos (≤ 8E absolute),\n// one product rounding: |z_f32 − z_f64| ≤ Z_ERR·E·radius with Z_ERR = 20 (derivation in docs/WEBGPU.md).\nfn std_normal() -> f32 {\n let b1 = next_bits();\n let b2 = next_bits();\n radius = sqrt(-2.0 * ln_unit(b1));\n return radius * cos_unit(b2);\n}\n// Multiplication method: product of uniforms (3E per factor) against exp(−mean), whose relative error is\n// (3 + 2·mean) ULP plus the float32 mean's absolute error E·(mean + 1): ≤ (4 + 3·mean)·E.\nfn poisson_small(mean: f32) -> u32 {\n let threshold = exp(-mean);\n var product = 1.0;\n var count = 0u;\n loop {\n count = count + 1u;\n product = product * unit(next_bits());\n check(product - threshold, MARGIN * (3.0 * f32(count) + 4.0 + 3.0 * mean) * E * max(product, threshold));\n if (!(product > threshold)) { break; }\n if (count >= 1000u) { uncertain = true; break; }\n }\n return count - 1u;\n}\n// g(t) = ln(1 + t) − t for |t| <= 1/2 without cancellation: −t²/(2+t) + 2(s³/3 + s⁵/5 + …), s = t/(2+t).\nfn log1p_minus(t: f32) -> f32 {\n let d = 2.0 + t;\n let s = t / d;\n let s2 = s * s;\n var p = ${literal(1 / 25)};\n ${[23, 21, 19, 17, 15, 13, 11, 9, 7, 5].map(n => `p = p * s2 + ${literal(1 / n)};`).join(\" \")}\n p = p * s2 + ${literal(1 / 3)};\n return -(t * t) / d + 2.0 * s * s2 * p;\n}\nfn poisson_ptrs(mi: u32, mf: f32) -> u32 {\n // mean: ≤ 2.1E relative; sq: ≤ 5.6E; b: ≤ 8.7E; a: ≤ 14E; inv_alpha: ≤ 3.5E; v_r: ≤ 6E absolute (docs/WEBGPU.md).\n let mean = f32(mi) + mf;\n let sq = sqrt(mean);\n let b = 0.931 + 2.53 * sq;\n let a = -0.059 + 0.02483 * b;\n let inv_alpha = 1.1239 + 1.1328 / (b - 3.4);\n let v_r = 0.9277 - 3.6224 / (b - 2.0);\n let ln_mean = log(mean);\n for (var iteration = 0u; iteration < 64u; iteration = iteration + 1u) {\n let su = next_bits();\n let sv = next_bits();\n var m = su;\n var sign = -1.0;\n if (su >= 0x80000000u) { m = 0xffffffffu - su; sign = 1.0; }\n let us = unit(m);\n let u = sign * unit(0x7fffffffu - m);\n let v = unit(sv);\n // scale ≤ 19.5E, t = scale·u ≤ 22.5E relative; x = (t + mf) + 0.43 in any order adds 2E·(|t| + 1.43) + 2E.\n let scale = 2.0 * a / us + b;\n let t = scale * u;\n let x = (t + mf) + 0.43;\n let fl = floor(x);\n let ex = MARGIN * (25.0 * E * abs(t) + 4.0 * E + 4.5e-16 * (abs(t) + mean + 1.0));\n if (x - fl <= ex || fl + 1.0 - x <= ex) { uncertain = true; }\n if (fl <= -1073741824.0 || fl >= 1073741824.0) { continue; }\n let k = i32(mi) + i32(fl);\n if (m >= ${uniformThresholdInteger(0.07)}u) {\n if (k >= 0) { check(v - v_r, MARGIN * (7.0 * E + 2.0 * E * v)); }\n if (v <= v_r && k >= 0) { return u32(k); }\n }\n if (k < 0 || (m < ${uniformThresholdInteger(0.013)}u && sv > m)) { continue; }\n // lhs: the argument carries ≤ 31.5E (two divisions), the logarithm 3 ULP or 2^-21.\n let lhs = log(v * inv_alpha / (a / (us * us) + b));\n let dl = 32.0 * E + LOG_ABS + 3.0 * E * abs(lhs);\n var rhs: f32;\n var dr: f32;\n if (k == 0) {\n rhs = -mean;\n dr = 3.0 * E * mean + CPU_REL * mean;\n } else {\n let kf = f32(k);\n let dd = f32(i32(mi) - k) + mf;\n let t2 = dd / kf;\n var kg: f32;\n var dk: f32;\n if (abs(t2) <= 0.5) {\n kg = kf * log1p_minus(t2);\n dk = 25.0 * E * abs(kg) + E;\n } else {\n let lr = log(mean / kf);\n kg = kf * lr - dd;\n dk = kf * (6.0 * E + LOG_ABS + 5.0 * E * abs(lr)) + 2.0 * E * abs(dd) + E + E * abs(kg);\n }\n let lnk = log(kf);\n var corr: f32;\n if (k < 256) { corr = aux[u32(k)]; } else { corr = 1.0 / (12.0 * kf) - 1.0 / (360.0 * kf * kf * kf); }\n rhs = ((kg - 0.5 * lnk) - 0.91893853320467274) - corr;\n dr = dk + 0.5 * LOG_ABS + 1.5 * E * lnk + 2.0 * E + 3.0 * E * (abs(kg) + 0.5 * lnk + 1.0) +\n CPU_REL * (mean + kf * (abs(ln_mean) + lnk + 1.0));\n }\n check(lhs - rhs, MARGIN * (dl + dr));\n if (lhs <= rhs) { return u32(k); }\n }\n uncertain = true;\n return mi;\n}\nfn poisson(word: u32, frac: f32) -> u32 {\n let category = word >> 30u;\n let mi = word & 0x3fffffffu;\n if (category == 0u) { return 0u; }\n if (category == 1u) { return poisson_small(f32(mi) + frac); }\n if (category == 2u) { return poisson_ptrs(mi, frac); }\n // Means of 1e9 and above: the draw always exceeds the capacity (<= 2^24), so the code does not depend on it.\n let z = std_normal();\n let y = sqrt(f32(mi) + frac) * z + frac + 0.5;\n return u32(max(0, i32(mi) + i32(floor(y))));\n}\n// Adds product·2^s for an exact integer product < 2^24: the integer part by an integer shift, the fraction as\n// an exact float32 below 1 (the masked low bits times the exact power of two). No float split depends on order.\nfn add_scaled(product: u32, s: i32, scale: f32) {\n if (product == 0u) { return; }\n // product·2^s is below 2^30 by the host's domain check, so the shift count is below 31.\n if (s >= 0) { ip = ip + i32(product << u32(s)); return; }\n if (s <= -24) { fp = fp + f32(product) * scale; return; }\n let shift = u32(-s);\n ip = ip + i32(product >> shift);\n fp = fp + f32(product & ((1u << shift) - 1u)) * scale;\n}\n// n·(p0 + p1 + p2 + p3) for an integer 0 <= n < 2^24 and pieces p_j = m_j·2^(s_j) with m_j < 2^12: the\n// products hi·m_j and lo·m_j are exact 32-bit integers.\nfn scaled(n: u32, base: u32) {\n let hi = n >> 12u;\n let lo = n & 0xfffu;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let m = u32(aux[base + ${SENSOR_PIECE_WORDS}u * j]);\n let s = i32(aux[base + ${SENSOR_PIECE_WORDS}u * j + 1u]);\n let scale = aux[base + ${SENSOR_PIECE_WORDS}u * j + 2u];\n add_scaled(hi * m, s + 12, scale * 4096.0);\n add_scaled(lo * m, s, scale);\n }\n}\nfn normalize() {\n let f = floor(fp);\n ip = ip + i32(f);\n fp = fp - f;\n}\n// Piecewise-linear INL offset with host-computed segment slopes (no division): ao + (code − ac)·slope.\nfn inl_offset(code: f32) -> f32 {\n let n = params.knots;\n let base = ${SENSOR_AUX.knots}u;\n if (code <= aux[base]) { return aux[base + n]; }\n if (code >= aux[base + n - 1u]) { return aux[base + 2u * n - 1u]; }\n var low = 0u;\n var high = n - 1u;\n loop {\n if (high - low <= 1u) { break; }\n let middle = (low + high) >> 1u;\n if (aux[base + middle] <= code) { low = middle; } else { high = middle; }\n }\n return aux[base + n + low] + (code - aux[base + low]) * aux[base + 2u * n + low];\n}\n@compute @workgroup_size(${SENSOR_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) groups: vec3<u32>) {\n let i = gid.x + gid.y * groups.x * ${SENSOR_WORKGROUP_SIZE}u;\n if (i >= params.count) { return; }\n let row = params.row0 + i / params.region_w;\n let index = (params.region_y + row) * params.native_w + params.region_x + i % params.region_w;\n uncertain = false;\n let base = i * params.site_words;\n start(site_seed(params.seed, index, 0u));\n let photo = poisson(means[base], bitcast<f32>(means[base + 1u]));\n let dark = poisson(means[base + 2u], bitcast<f32>(means[base + 3u]));\n // Saturating sums: any total above the clamp is above every supported capacity, as in the reference.\n var realized = min(photo + dark, ${SENSOR_REALIZED_CLAMP}u);\n for (var k = 0u; k < params.poisson_count; k = k + 1u) {\n realized = min(realized + poisson(means[base + 4u + 2u * k], bitcast<f32>(means[base + 5u + 2u * k])), ${SENSOR_REALIZED_CLAMP}u);\n }\n var cursor = base + 4u + 2u * params.poisson_count;\n let deterministic = (params.extras & ${SENSOR_EXTRAS.deterministic}u) != 0u;\n let pattern = (params.extras & ${SENSOR_EXTRAS.fixedPattern}u) != 0u;\n var r_frac = 0.0;\n var det_slack = 0.0;\n if (deterministic) {\n realized = min(realized + means[cursor], ${SENSOR_REALIZED_CLAMP}u);\n r_frac = bitcast<f32>(means[cursor + 1u]);\n det_slack = E;\n cursor = cursor + 2u;\n }\n var t_floor = i32(params.threshold_floor);\n var t_frac = params.threshold_frac;\n var fpn_e_slack = 0.0;\n if (pattern) {\n t_floor = bitcast<i32>(means[cursor]);\n t_frac = bitcast<f32>(means[cursor + 1u]);\n fpn_e_slack = params.fpn_e_slack;\n }\n start(site_seed(params.seed, index, 1u));\n var noise = 0.0;\n var noise_bound = 0.0;\n for (var c = 0u; c < params.components; c = c + 1u) {\n let sigma = aux[256u + c];\n if (sigma != 0.0) {\n let z = std_normal();\n noise = noise + z * sigma;\n // z's error, sigma's rounding, the product, and the order-independent sum over the component count.\n noise_bound = noise_bound + sigma * radius * (Z_ERR + 2.0 + f32(params.components)) * E;\n }\n }\n var flags = 0u;\n var a_int = realized;\n var a_frac = r_frac;\n if (!deterministic) {\n if (realized > params.capacity_floor) { flags = 1u; a_int = params.capacity_floor; a_frac = params.capacity_frac; }\n } else {\n // realized > capacity with a non-integer total: a three-term float32 sum (any order) with its bound.\n let w = f32(i32(realized) - i32(params.capacity_floor));\n let dcap = w + (r_frac - params.capacity_frac);\n check(dcap, MARGIN * (3.0 * E * abs(w) + 6.0 * E + CPU_REL * (f32(realized) + 1.0)));\n if (dcap > 0.0) { flags = 1u; a_int = params.capacity_floor; a_frac = params.capacity_frac; }\n }\n // Pre-ADC limit: afterPhysical + noise + offset > threshold, as afterPhysical − (threshold − offset) + noise > 0,\n // a four-term float32 sum (any order).\n let whole = f32(i32(a_int) - t_floor);\n let diff = (whole + (a_frac - t_frac)) + noise;\n check(diff, MARGIN * (noise_bound + 4.0 * E * abs(whole) + 3.0 * E * abs(noise) + 8.0 * E + CPU_REL * (f32(a_int) + abs(noise)) +\n fpn_e_slack + det_slack));\n var q: i32;\n if (diff > 0.0) {\n flags = flags | 2u;\n q = params.sat_quantized;\n } else {\n // ideal = (afterPhysical + noise + offset)/gain + black, as an exact integer part plus a float32 fraction.\n ip = i32(params.black);\n fp = 0.0;\n var fpn_c_slack = 0.0;\n if (pattern) {\n ip = ip + bitcast<i32>(means[cursor + 2u]);\n fp = bitcast<f32>(means[cursor + 3u]);\n fpn_c_slack = params.fpn_c_slack;\n }\n scaled(a_int, ${SENSOR_AUX.inversePieces}u);\n let noise_codes = noise * params.inv_gain;\n let frac_codes = a_frac * params.inv_gain;\n fp = fp + frac_codes + noise_codes;\n let a_codes = f32(a_int) * params.inv_gain;\n // The fraction sum (eleven terms below 1, frac_codes and noise_codes), the two normalizations and the\n // half-code comparison in any association: 13E·Σ|terms|; then the term errors, the pieces' residual,\n // the reference's float64 rounding and the packed fractions' rounding.\n var margin = MARGIN * (13.0 * E * (13.0 + frac_codes + abs(noise_codes)) + 3.0 * E * frac_codes + 2.0 * E * abs(noise_codes) +\n noise_bound * params.inv_gain + f32(a_int) * params.inv_residual +\n CPU_REL * (a_codes + abs(noise_codes) + f32(params.black)) + fpn_c_slack + det_slack * params.inv_gain);\n normalize();\n if (params.knots > 0u) {\n let ideal = f32(ip) + fp;\n let offset = inl_offset(ideal);\n fp = fp + offset;\n margin = margin * (1.0 + params.inl_slope) + MARGIN * (params.inl_slope * 2.0 * E * (abs(ideal) + 1.0 + params.inl_knot) +\n 11.0 * E * (params.inl_offset + 1.0) + CPU_REL * (abs(ideal) + params.inl_offset) * (1.0 + params.inl_slope));\n normalize();\n }\n if ((params.digital & 4u) == 0u) { check(fp - 0.5, margin); }\n q = ip + select(0, 1, fp >= 0.5);\n }\n if ((params.digital & 1u) != 0u) {\n let adc = clamp(q, 0, i32(params.adc_max));\n let delta = adc - i32(params.black);\n let estimate = f32(delta) * params.digital_gain;\n if (estimate >= 16777216.0) {\n q = 16777216;\n } else if (estimate <= -16777216.0) {\n q = -16777216;\n } else {\n ip = 0;\n fp = 0.0;\n scaled(u32(abs(delta)), ${SENSOR_AUX.digitalPieces}u);\n // Eight exact fractions summed in any order, the floor split and the half comparison: ≤ 10 roundings on\n // values below 9; the pieces' residual; the reference's float64 product.\n if ((params.digital & 2u) == 0u) { check(fp - floor(fp) - 0.5, MARGIN * (90.0 * E + f32(abs(delta)) * params.digital_residual + CPU_REL * (abs(estimate) + f32(params.black)))); }\n normalize();\n var whole_part = ip;\n var fraction = fp;\n if (delta < 0) {\n whole_part = -ip;\n if (fp > 0.0) { whole_part = -ip - 1; fraction = 1.0 - fp; }\n }\n q = whole_part + i32(params.black) + select(0, 1, fraction >= 0.5);\n }\n }\n if (q >= i32(params.saturation)) { flags = flags | 4u; }\n if (q < 0) { flags = flags | 8u; }\n let code = u32(clamp(q, 0, i32(params.saturation)));\n out[i] = code | (flags << 16u) | select(0u, ${1 << 20}u, uncertain);\n}\n`;\n"]}
|
|
1
|
+
{"version":3,"file":"wgsl-sensor-kernel.js","sourceRoot":"","sources":["../../src/gpu/wgsl-sensor-kernel.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * WGSL kernel for the `webgpu-f32` sensor stage (#277, #471), one invocation per native site. It evaluates the\n * reference `simulatePackedSensorSite` (packed-sensor-raw.ts) with the same seeded noise model\n * (`photivra-native-raw-noise` 0.4.0):\n * - the MurmurHash3_x86_32 site seeds, fmix32 scrambling and xorshift32 streams are 32-bit integer\n * arithmetic, so every random state is bit-identical to the reference;\n * - every comparison against a uniform value is made exactly on the integer state where the reference\n * value is an exact dyadic rational;\n * - every other decision (Poisson multiplication-method threshold, PTRS floor and acceptance tests,\n * physical and pre-ADC limits, ADC and digital-gain rounding) is evaluated in float32 together with an\n * a-priori bound on how far the float32 value can be from the float64 reference value. A site where any\n * decision falls inside its bound is flagged. An unflagged site therefore makes every decision exactly\n * as the reference does, and its code and flags equal the reference.\n * - Fixed-pattern noise and additional stored-charge components (2.0): the host evaluates the\n * fixed-pattern values in float64 with the model's own functions and packs, per site, the Poisson means\n * already multiplied by the PRNU and dark factors (exactly the reference's products), the pre-ADC limit\n * minus the readout offset and the offset in codes. Additional Poisson components are drawn from the\n * charge stream after photo and dark charge, in declaration order; deterministic components enter as an\n * exact integer part plus a float32 fraction, with their own bounds.\n *\n * Arithmetic assumptions (W3C WGSL, \"Floating Point Accuracy\" and \"Reassociation and Fusion\"; every margin\n * is derived in docs/WEBGPU.md, \"Sensor-stage margins\"):\n * - E = 2^-23. WGSL specifies no rounding direction, so a \"correctly rounded\" operation (+ − ×, floor, f32(u32))\n * is within one ULP of the exact value, that is ≤ E relative; `/` is 2.5 ULP (≤ 2.5E); `sqrt` inherits\n * 1.0 / inverseSqrt(x) (≤ 4.5E); `exp` is (3 + 2|x|) ULP; `log` is 3 ULP outside [0.5, 2] and 2^-21\n * absolute inside. WGSL's `cos` has only a 2^-11 absolute bound, so the kernel uses its own\n * octant-reduced polynomials.\n * - No decision depends on correctly rounded division: 1/gain and the digital gain arrive from the host as\n * exact 12-bit pieces, INL segments as host-computed slopes, and every quotient that remains (Box–Muller\n * logarithm, PTRS) carries the 2.5 ULP bound.\n * - WGSL lets an implementation reassociate. The integer parts of the ADC input are therefore formed in\n * integer arithmetic, and every floating-point sum carries the order-independent bound\n * (n − 1)·E·Σ|terms| (Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., §4.2), so the\n * sums' margins hold under any association. Fusing a multiply and an add only removes a rounding.\n * - Every margin is then doubled (`MARGIN`), which covers the second-order terms dropped in the derivations.\n */\n\n/** Output word: code in bits 0–15, saturation flags in bits 16–19, the flagged (uncertain) bit 20. */\nexport const SENSOR_OUTPUT = Object.freeze({ codeMask: 0xffff, flagShift: 16, uncertainBit: 1 << 20 });\n/**\n * Auxiliary table layout (float32 words). A gain's four 12-bit pieces are stored as `SENSOR_PIECE_WORDS` words\n * each: the integer mantissa m (< 2^12), the exponent s and the exact scale 2^s, so that piece = m·2^s. The INL\n * table holds the knot codes, the knot offsets and the host-computed segment slopes.\n */\nexport const SENSOR_AUX = /* @__PURE__ */ Object.freeze({ stirlingRemainder: 0, sigma: 256, inversePieces: 272, digitalPieces: 284, knots: 296 });\nexport const SENSOR_PIECE_WORDS = 3;\nexport const SENSOR_WORKGROUP_SIZE = 256;\n/**\n * Per-site packed words: photo word, photo fraction bits, dark word, dark fraction bits; then 2 words per\n * additional Poisson component; then, with deterministic components, their sum (integer part, fraction\n * bits); then, with fixed-pattern noise, (threshold − offset) and offset/gain as (floor, fraction bits).\n */\nexport const SENSOR_SITE_WORDS = 4;\n/** Bits of `Params.extras`. */\nexport const SENSOR_EXTRAS = /* @__PURE__ */ Object.freeze({ deterministic: 1, fixedPattern: 2 });\n/** Partial charge sums saturate here: above every supported capacity (2^24), so no decision changes. */\nexport const SENSOR_REALIZED_CLAMP = 2 ** 30;\n/** Mean categories in bits 30–31 of a mean word, mirroring `samplePoisson`'s branches. */\nexport const SENSOR_MEAN_CATEGORY = /* @__PURE__ */ Object.freeze({ zero: 0, multiplication: 1, transformedRejection: 2, normal: 3 });\n/** One WGSL rounding (one ULP, either direction) is at most this relative error: 2^-23. */\nexport const SENSOR_ROUNDING = 2 ** -23;\n/** |z_f32 − z_f64| ≤ SENSOR_NORMAL_ERROR·E·radius for the Box–Muller normal (docs/WEBGPU.md). */\nexport const SENSOR_NORMAL_ERROR = 20;\n\n/** Smallest integer m with (m + 0.5)/2^32 ≥ t, so `uS ≥ t` ⇔ `m ≥ result` exactly. */\nexport function uniformThresholdInteger(t: number): number {\n return Math.ceil(t * 2 ** 32 - 0.5);\n}\n\nconst SIN_COEFFICIENTS = [-1 / 6, 1 / 120, -1 / 5040, 1 / 362880, -1 / 39916800];\nconst COS_COEFFICIENTS = [-1 / 2, 1 / 24, -1 / 720, 1 / 40320, -1 / 3628800, 1 / 479001600];\nconst literal = (value: number): string => {\n const text = String(Math.fround(value));\n return /[.e]/u.test(text) ? text : `${text}.0`;\n};\n\nexport const SENSOR_RAW_WGSL = /* wgsl */ `\nconst E: f32 = 1.1920928955078125e-7;\nconst INV32: f32 = 2.3283064365386963e-10;\nconst TWO_PI_INV32: f32 = ${literal(2 * Math.PI / 2 ** 32)};\nconst MARGIN: f32 = 2.0;\nconst CPU_REL: f32 = 7.105427357601002e-15;\nconst LOG_ABS: f32 = 4.76837158203125e-7;\nconst Z_ERR: f32 = ${literal(SENSOR_NORMAL_ERROR)};\nstruct Params {\n count: u32, row0: u32, region_w: u32, native_w: u32,\n region_x: u32, region_y: u32, seed: u32, components: u32,\n knots: u32, digital: u32, saturation: u32, adc_max: u32,\n capacity_floor: u32, threshold_floor: u32, black: u32, sat_quantized: i32,\n capacity_frac: f32, threshold_frac: f32, inv_gain: f32, inv_residual: f32,\n digital_gain: f32, digital_residual: f32, inl_slope: f32, inl_offset: f32,\n site_words: u32, poisson_count: u32, extras: u32, pad0: u32,\n fpn_e_slack: f32, fpn_c_slack: f32, inl_knot: f32, pad2: f32,\n};\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> means: array<u32>;\n@group(0) @binding(2) var<storage, read> aux: array<f32>;\n@group(0) @binding(3) var<storage, read_write> out: array<u32>;\n\nvar<private> state: u32;\nvar<private> uncertain: bool;\nvar<private> radius: f32;\nvar<private> ip: i32;\nvar<private> fp: f32;\n\nfn fmix(value: u32) -> u32 {\n var h = value;\n h = h ^ (h >> 16u); h = h * 0x85ebca6bu; h = h ^ (h >> 13u); h = h * 0xc2b2ae35u; h = h ^ (h >> 16u);\n return h;\n}\nfn mix_block(hash: u32, block: u32) -> u32 {\n var k = block * 0xcc9e2d51u;\n k = (k << 15u) | (k >> 17u);\n k = k * 0x1b873593u;\n var h = hash ^ k;\n h = (h << 13u) | (h >> 19u);\n return h * 5u + 0xe6546b64u;\n}\nfn site_seed(seed: u32, index: u32, stream: u32) -> u32 {\n return fmix(mix_block(mix_block(mix_block(0u, seed), index), stream) ^ 12u);\n}\nfn start(seed: u32) {\n state = fmix(seed ^ 0x9e3779b9u);\n if (state == 0u) { state = 0x6d2b79f5u; }\n}\nfn next_bits() -> u32 {\n state = state ^ (state << 13u); state = state ^ (state >> 17u); state = state ^ (state << 5u);\n return state;\n}\n// (bits + 0.5)/2^32 with two roundings (the conversion and the sum; the power-of-two scale is exact): ≤ 2E.\nfn unit(bits: u32) -> f32 { return (f32(bits) + 0.5) * INV32; }\nfn check(distance: f32, margin: f32) { if (abs(distance) <= margin) { uncertain = true; } }\n\n// ln((bits + 0.5)/2^32), relative error ≤ 11E: WGSL log below 1/2 (≤ 6E), else 2·atanh(r) with r = −w/(2 − w)\n// on the exact 1 − unit (division 2.5E; ≤ 10.5E in all).\nfn ln_unit(bits: u32) -> f32 {\n if (bits < 0x80000000u) { return log(unit(bits)); }\n let w = (f32(0xffffffffu - bits) + 0.5) * INV32;\n let r = -w / (2.0 - w);\n let r2 = r * r;\n var p = ${literal(1 / 23)};\n ${[21, 19, 17, 15, 13, 11, 9, 7, 5, 3].map(n => `p = p * r2 + ${literal(1 / n)};`).join(\" \")}\n p = p * r2 + 1.0;\n return 2.0 * r * p;\n}\n// cos(2π (bits + 0.5)/2^32): exact octant reduction on the integer, Taylor polynomials on [0, π/4]; absolute error ≤ 8E.\nfn cos_unit(bits: u32) -> f32 {\n let octant = bits >> 29u;\n let low = bits & 0x1fffffffu;\n let even = (octant & 1u) == 0u;\n var g = (f32(0x1fffffffu - low) + 0.5) * TWO_PI_INV32;\n if (even) { g = (f32(low) + 0.5) * TWO_PI_INV32; }\n let g2 = g * g;\n var c = ${literal(COS_COEFFICIENTS[5]!)};\n ${[4, 3, 2, 1, 0].map(j => `c = c * g2 + ${literal(COS_COEFFICIENTS[j]!)};`).join(\" \")}\n c = c * g2 + 1.0;\n var s = ${literal(SIN_COEFFICIENTS[4]!)};\n ${[3, 2, 1, 0].map(j => `s = s * g2 + ${literal(SIN_COEFFICIENTS[j]!)};`).join(\" \")}\n s = g * (s * g2 + 1.0);\n let cb = select(s, c, even);\n let sb = select(c, s, even);\n let q = octant >> 1u;\n if (q == 0u) { return cb; }\n if (q == 1u) { return -sb; }\n if (q == 2u) { return -cb; }\n return sb;\n}\n// Box–Muller, one value per two uniforms: radius = sqrt(−2 ln u1) (≤ 10E relative), times cos (≤ 8E absolute),\n// one product rounding: |z_f32 − z_f64| ≤ Z_ERR·E·radius with Z_ERR = 20 (derivation in docs/WEBGPU.md).\nfn std_normal() -> f32 {\n let b1 = next_bits();\n let b2 = next_bits();\n radius = sqrt(-2.0 * ln_unit(b1));\n return radius * cos_unit(b2);\n}\n// Multiplication method: product of uniforms (3E per factor) against exp(−mean), whose relative error is\n// (3 + 2·mean) ULP plus the float32 mean's absolute error E·(mean + 1): ≤ (4 + 3·mean)·E.\nfn poisson_small(mean: f32) -> u32 {\n let threshold = exp(-mean);\n var product = 1.0;\n var count = 0u;\n loop {\n count = count + 1u;\n product = product * unit(next_bits());\n check(product - threshold, MARGIN * (3.0 * f32(count) + 4.0 + 3.0 * mean) * E * max(product, threshold));\n if (!(product > threshold)) { break; }\n if (count >= 1000u) { uncertain = true; break; }\n }\n return count - 1u;\n}\n// g(t) = ln(1 + t) − t for |t| <= 1/2 without cancellation: −t²/(2+t) + 2(s³/3 + s⁵/5 + …), s = t/(2+t).\nfn log1p_minus(t: f32) -> f32 {\n let d = 2.0 + t;\n let s = t / d;\n let s2 = s * s;\n var p = ${literal(1 / 25)};\n ${[23, 21, 19, 17, 15, 13, 11, 9, 7, 5].map(n => `p = p * s2 + ${literal(1 / n)};`).join(\" \")}\n p = p * s2 + ${literal(1 / 3)};\n return -(t * t) / d + 2.0 * s * s2 * p;\n}\nfn poisson_ptrs(mi: u32, mf: f32) -> u32 {\n // mean: ≤ 2.1E relative; sq: ≤ 5.6E; b: ≤ 8.7E; a: ≤ 14E; inv_alpha: ≤ 3.5E; v_r: ≤ 6E absolute (docs/WEBGPU.md).\n let mean = f32(mi) + mf;\n let sq = sqrt(mean);\n let b = 0.931 + 2.53 * sq;\n let a = -0.059 + 0.02483 * b;\n let inv_alpha = 1.1239 + 1.1328 / (b - 3.4);\n let v_r = 0.9277 - 3.6224 / (b - 2.0);\n let ln_mean = log(mean);\n for (var iteration = 0u; iteration < 64u; iteration = iteration + 1u) {\n let su = next_bits();\n let sv = next_bits();\n var m = su;\n var sign = -1.0;\n if (su >= 0x80000000u) { m = 0xffffffffu - su; sign = 1.0; }\n let us = unit(m);\n let u = sign * unit(0x7fffffffu - m);\n let v = unit(sv);\n // scale ≤ 19.5E, t = scale·u ≤ 22.5E relative; x = (t + mf) + 0.43 in any order adds 2E·(|t| + 1.43) + 2E.\n let scale = 2.0 * a / us + b;\n let t = scale * u;\n let x = (t + mf) + 0.43;\n let fl = floor(x);\n let ex = MARGIN * (25.0 * E * abs(t) + 4.0 * E + 4.5e-16 * (abs(t) + mean + 1.0));\n if (x - fl <= ex || fl + 1.0 - x <= ex) { uncertain = true; }\n if (fl <= -1073741824.0 || fl >= 1073741824.0) { continue; }\n let k = i32(mi) + i32(fl);\n if (m >= ${uniformThresholdInteger(0.07)}u) {\n if (k >= 0) { check(v - v_r, MARGIN * (7.0 * E + 2.0 * E * v)); }\n if (v <= v_r && k >= 0) { return u32(k); }\n }\n if (k < 0 || (m < ${uniformThresholdInteger(0.013)}u && sv > m)) { continue; }\n // lhs: the argument carries ≤ 31.5E (two divisions), the logarithm 3 ULP or 2^-21.\n let lhs = log(v * inv_alpha / (a / (us * us) + b));\n let dl = 32.0 * E + LOG_ABS + 3.0 * E * abs(lhs);\n var rhs: f32;\n var dr: f32;\n if (k == 0) {\n rhs = -mean;\n dr = 3.0 * E * mean + CPU_REL * mean;\n } else {\n let kf = f32(k);\n let dd = f32(i32(mi) - k) + mf;\n let t2 = dd / kf;\n var kg: f32;\n var dk: f32;\n if (abs(t2) <= 0.5) {\n kg = kf * log1p_minus(t2);\n dk = 25.0 * E * abs(kg) + E;\n } else {\n let lr = log(mean / kf);\n kg = kf * lr - dd;\n dk = kf * (6.0 * E + LOG_ABS + 5.0 * E * abs(lr)) + 2.0 * E * abs(dd) + E + E * abs(kg);\n }\n let lnk = log(kf);\n var corr: f32;\n if (k < 256) { corr = aux[u32(k)]; } else { corr = 1.0 / (12.0 * kf) - 1.0 / (360.0 * kf * kf * kf); }\n rhs = ((kg - 0.5 * lnk) - 0.91893853320467274) - corr;\n dr = dk + 0.5 * LOG_ABS + 1.5 * E * lnk + 2.0 * E + 3.0 * E * (abs(kg) + 0.5 * lnk + 1.0) +\n CPU_REL * (mean + kf * (abs(ln_mean) + lnk + 1.0));\n }\n check(lhs - rhs, MARGIN * (dl + dr));\n if (lhs <= rhs) { return u32(k); }\n }\n uncertain = true;\n return mi;\n}\nfn poisson(word: u32, frac: f32) -> u32 {\n let category = word >> 30u;\n let mi = word & 0x3fffffffu;\n if (category == 0u) { return 0u; }\n if (category == 1u) { return poisson_small(f32(mi) + frac); }\n if (category == 2u) { return poisson_ptrs(mi, frac); }\n // Means of 1e9 and above: the draw always exceeds the capacity (<= 2^24), so the code does not depend on it.\n let z = std_normal();\n let y = sqrt(f32(mi) + frac) * z + frac + 0.5;\n return u32(max(0, i32(mi) + i32(floor(y))));\n}\n// Adds product·2^s for an exact integer product < 2^24: the integer part by an integer shift, the fraction as\n// an exact float32 below 1 (the masked low bits times the exact power of two). No float split depends on order.\nfn add_scaled(product: u32, s: i32, scale: f32) {\n if (product == 0u) { return; }\n // product·2^s is below 2^30 by the host's domain check, so the shift count is below 31.\n if (s >= 0) { ip = ip + i32(product << u32(s)); return; }\n if (s <= -24) { fp = fp + f32(product) * scale; return; }\n let shift = u32(-s);\n ip = ip + i32(product >> shift);\n fp = fp + f32(product & ((1u << shift) - 1u)) * scale;\n}\n// n·(p0 + p1 + p2 + p3) for an integer 0 <= n < 2^24 and pieces p_j = m_j·2^(s_j) with m_j < 2^12: the\n// products hi·m_j and lo·m_j are exact 32-bit integers.\nfn scaled(n: u32, base: u32) {\n let hi = n >> 12u;\n let lo = n & 0xfffu;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let m = u32(aux[base + ${SENSOR_PIECE_WORDS}u * j]);\n let s = i32(aux[base + ${SENSOR_PIECE_WORDS}u * j + 1u]);\n let scale = aux[base + ${SENSOR_PIECE_WORDS}u * j + 2u];\n add_scaled(hi * m, s + 12, scale * 4096.0);\n add_scaled(lo * m, s, scale);\n }\n}\nfn normalize() {\n let f = floor(fp);\n ip = ip + i32(f);\n fp = fp - f;\n}\n// Piecewise-linear INL offset with host-computed segment slopes (no division): ao + (code − ac)·slope.\nfn inl_offset(code: f32) -> f32 {\n let n = params.knots;\n let base = ${SENSOR_AUX.knots}u;\n if (code <= aux[base]) { return aux[base + n]; }\n if (code >= aux[base + n - 1u]) { return aux[base + 2u * n - 1u]; }\n var low = 0u;\n var high = n - 1u;\n loop {\n if (high - low <= 1u) { break; }\n let middle = (low + high) >> 1u;\n if (aux[base + middle] <= code) { low = middle; } else { high = middle; }\n }\n return aux[base + n + low] + (code - aux[base + low]) * aux[base + 2u * n + low];\n}\n@compute @workgroup_size(${SENSOR_WORKGROUP_SIZE})\nfn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) groups: vec3<u32>) {\n let i = gid.x + gid.y * groups.x * ${SENSOR_WORKGROUP_SIZE}u;\n if (i >= params.count) { return; }\n let row = params.row0 + i / params.region_w;\n let index = (params.region_y + row) * params.native_w + params.region_x + i % params.region_w;\n uncertain = false;\n let base = i * params.site_words;\n start(site_seed(params.seed, index, 0u));\n let photo = poisson(means[base], bitcast<f32>(means[base + 1u]));\n let dark = poisson(means[base + 2u], bitcast<f32>(means[base + 3u]));\n // Saturating sums: any total above the clamp is above every supported capacity, as in the reference.\n var realized = min(photo + dark, ${SENSOR_REALIZED_CLAMP}u);\n for (var k = 0u; k < params.poisson_count; k = k + 1u) {\n realized = min(realized + poisson(means[base + 4u + 2u * k], bitcast<f32>(means[base + 5u + 2u * k])), ${SENSOR_REALIZED_CLAMP}u);\n }\n var cursor = base + 4u + 2u * params.poisson_count;\n let deterministic = (params.extras & ${SENSOR_EXTRAS.deterministic}u) != 0u;\n let pattern = (params.extras & ${SENSOR_EXTRAS.fixedPattern}u) != 0u;\n var r_frac = 0.0;\n var det_slack = 0.0;\n if (deterministic) {\n realized = min(realized + means[cursor], ${SENSOR_REALIZED_CLAMP}u);\n r_frac = bitcast<f32>(means[cursor + 1u]);\n det_slack = E;\n cursor = cursor + 2u;\n }\n var t_floor = i32(params.threshold_floor);\n var t_frac = params.threshold_frac;\n var fpn_e_slack = 0.0;\n if (pattern) {\n t_floor = bitcast<i32>(means[cursor]);\n t_frac = bitcast<f32>(means[cursor + 1u]);\n fpn_e_slack = params.fpn_e_slack;\n }\n start(site_seed(params.seed, index, 1u));\n var noise = 0.0;\n var noise_bound = 0.0;\n for (var c = 0u; c < params.components; c = c + 1u) {\n let sigma = aux[256u + c];\n if (sigma != 0.0) {\n let z = std_normal();\n noise = noise + z * sigma;\n // z's error, sigma's rounding, the product, and the order-independent sum over the component count.\n noise_bound = noise_bound + sigma * radius * (Z_ERR + 2.0 + f32(params.components)) * E;\n }\n }\n var flags = 0u;\n var a_int = realized;\n var a_frac = r_frac;\n if (!deterministic) {\n if (realized > params.capacity_floor) { flags = 1u; a_int = params.capacity_floor; a_frac = params.capacity_frac; }\n } else {\n // realized > capacity with a non-integer total: a three-term float32 sum (any order) with its bound.\n let w = f32(i32(realized) - i32(params.capacity_floor));\n let dcap = w + (r_frac - params.capacity_frac);\n check(dcap, MARGIN * (3.0 * E * abs(w) + 6.0 * E + CPU_REL * (f32(realized) + 1.0)));\n if (dcap > 0.0) { flags = 1u; a_int = params.capacity_floor; a_frac = params.capacity_frac; }\n }\n // Pre-ADC limit: afterPhysical + noise + offset > threshold, as afterPhysical − (threshold − offset) + noise > 0,\n // a four-term float32 sum (any order).\n let whole = f32(i32(a_int) - t_floor);\n let diff = (whole + (a_frac - t_frac)) + noise;\n check(diff, MARGIN * (noise_bound + 4.0 * E * abs(whole) + 3.0 * E * abs(noise) + 8.0 * E + CPU_REL * (f32(a_int) + abs(noise)) +\n fpn_e_slack + det_slack));\n var q: i32;\n if (diff > 0.0) {\n flags = flags | 2u;\n q = params.sat_quantized;\n } else {\n // ideal = (afterPhysical + noise + offset)/gain + black, as an exact integer part plus a float32 fraction.\n ip = i32(params.black);\n fp = 0.0;\n var fpn_c_slack = 0.0;\n if (pattern) {\n ip = ip + bitcast<i32>(means[cursor + 2u]);\n fp = bitcast<f32>(means[cursor + 3u]);\n fpn_c_slack = params.fpn_c_slack;\n }\n scaled(a_int, ${SENSOR_AUX.inversePieces}u);\n let noise_codes = noise * params.inv_gain;\n let frac_codes = a_frac * params.inv_gain;\n fp = fp + frac_codes + noise_codes;\n let a_codes = f32(a_int) * params.inv_gain;\n // The fraction sum (eleven terms below 1, frac_codes and noise_codes), the two normalizations and the\n // half-code comparison in any association: 13E·Σ|terms|; then the term errors, the pieces' residual,\n // the reference's float64 rounding and the packed fractions' rounding.\n var margin = MARGIN * (13.0 * E * (13.0 + frac_codes + abs(noise_codes)) + 3.0 * E * frac_codes + 2.0 * E * abs(noise_codes) +\n noise_bound * params.inv_gain + f32(a_int) * params.inv_residual +\n CPU_REL * (a_codes + abs(noise_codes) + f32(params.black)) + fpn_c_slack + det_slack * params.inv_gain);\n normalize();\n if (params.knots > 0u) {\n let ideal = f32(ip) + fp;\n let offset = inl_offset(ideal);\n fp = fp + offset;\n margin = margin * (1.0 + params.inl_slope) + MARGIN * (params.inl_slope * 2.0 * E * (abs(ideal) + 1.0 + params.inl_knot) +\n 11.0 * E * (params.inl_offset + 1.0) + CPU_REL * (abs(ideal) + params.inl_offset) * (1.0 + params.inl_slope));\n normalize();\n }\n if ((params.digital & 4u) == 0u) { check(fp - 0.5, margin); }\n q = ip + select(0, 1, fp >= 0.5);\n }\n if ((params.digital & 1u) != 0u) {\n let adc = clamp(q, 0, i32(params.adc_max));\n let delta = adc - i32(params.black);\n let estimate = f32(delta) * params.digital_gain;\n if (estimate >= 16777216.0) {\n q = 16777216;\n } else if (estimate <= -16777216.0) {\n q = -16777216;\n } else {\n ip = 0;\n fp = 0.0;\n scaled(u32(abs(delta)), ${SENSOR_AUX.digitalPieces}u);\n // Eight exact fractions summed in any order, the floor split and the half comparison: ≤ 10 roundings on\n // values below 9; the pieces' residual; the reference's float64 product.\n if ((params.digital & 2u) == 0u) { check(fp - floor(fp) - 0.5, MARGIN * (90.0 * E + f32(abs(delta)) * params.digital_residual + CPU_REL * (abs(estimate) + f32(params.black)))); }\n normalize();\n var whole_part = ip;\n var fraction = fp;\n if (delta < 0) {\n whole_part = -ip;\n if (fp > 0.0) { whole_part = -ip - 1; fraction = 1.0 - fp; }\n }\n q = whole_part + i32(params.black) + select(0, 1, fraction >= 0.5);\n }\n }\n if (q >= i32(params.saturation)) { flags = flags | 4u; }\n if (q < 0) { flags = flags | 8u; }\n let code = u32(clamp(q, 0, i32(params.saturation)));\n out[i] = code | (flags << 16u) | select(0u, ${1 << 20}u, uncertain);\n}\n`;\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* backends are not supported package APIs.
|
|
6
6
|
* @see docs/DEVELOPERS.md for equations, coordinate/unit conventions, blockers and support limits.
|
|
7
7
|
*/
|
|
8
|
-
export { GENERIC_EQUIPMENT_TIER_PRESET_VERSION, GENERIC_EQUIPMENT_TIERS, resolveGenericEquipmentTierCatalog, createGenericEquipmentTierSelection, parseGenericEquipmentTierSelection, resolveGenericEquipmentTierLensProfiles, type GenericEquipmentTierProfileReference, type GenericEquipmentTierSelection, type ResolvedGenericTierLensProfiles } from "./equipment/generic-tier-presets.js";
|
|
8
|
+
export { GENERIC_EQUIPMENT_TIER_PRESET_VERSION, GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS, GENERIC_EQUIPMENT_TIERS, resolveGenericEquipmentTierCatalog, createGenericEquipmentTierSelection, parseGenericEquipmentTierSelection, resolveGenericEquipmentTierLensProfiles, type GenericEquipmentTierProfileReference, type GenericEquipmentTierSelection, type ResolvedGenericTierLensProfiles } from "./equipment/generic-tier-presets.js";
|
|
9
9
|
export type { GenericEquipmentTier, GenericEquipmentTierPreset, GenericVersionedAsset, GenericTierBodyAssets, GenericTierLensAssets } from "./equipment/generic-tier-assets.js";
|
|
10
10
|
export { SENSOR_RAW_PRODUCER_SCHEMA_VERSION, SENSOR_RAW_PRODUCER_NOISE_MODEL, parseSensorRawProducerInput, simulateSensorRawFrame, type SensorRawProducerExposureWindowInput, type SensorRawProducerSiteInput, type SensorRawProducerInput, type SensorRawProducerResult } from "./capture/sensor-raw-producer.js";
|
|
11
11
|
export { CAPTURE_CORRECTED_SDR_SCHEMA_VERSION, parseCaptureCorrectedSdrInput, calculateCaptureCorrectedSdr, type CaptureCorrectedSdrInput, type CaptureCorrectedSdrResult } from "./output/capture-corrected-sdr.js";
|
|
@@ -200,13 +200,15 @@ export { createFocusStackTask, type FocusStackInput, type FocusStackOutput } fro
|
|
|
200
200
|
export { createPanoramaTask, type PanoramaFrame, type PanoramaCanvas, type PanoramaInput, type PanoramaOutput } from "./processing/panorama.js";
|
|
201
201
|
export { SYNTHETIC_FILM_GRAIN_STREAM_ID, createBilateralFilterTask, createNonLocalMeansTask, createUnsharpMaskTask, createSyntheticFilmGrainTask, type ProcessedRgbImage, type BilateralFilterInput, type NonLocalMeansInput, type UnsharpMaskInput, type SyntheticFilmGrainInput, type SyntheticFilmGrainOutput } from "./processing/denoise.js";
|
|
202
202
|
export { calculateSensorApertureRays, type CalculateSensorApertureRaysInput, type IdealCircularPupil, type SensorApertureRay, type SensorApertureRays } from "./optics/sensor-aperture-rays.js";
|
|
203
|
-
export { SIMULATE_CAPTURE_FACADE_VERSION, SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX, SIMULATE_CAPTURE_COST_OPTIONS, simulateCapture, type SimulateCaptureOptions, type SimulateCaptureResult, type SimulateCaptureAssumption, type SimulateCaptureProgress, type SimulateCaptureExposureMode, type SimulateCaptureAutoIsoAfterMaximum, type SimulateCaptureAutoIsoReport, type SimulateCaptureAutomaticSlowestShutterOptions, type SimulateCaptureAutomaticSlowestShutterReport, type SimulateCaptureOutputFormat, type SimulateCaptureWhiteBalance, type SimulateCaptureColorEncoding, type SimulateCaptureDemosaic, type SimulateCaptureBorderHandling, type SimulateCaptureLensDistortion, type SimulateCaptureMotion, type SimulateCaptureSensorAntiAliasing, type SimulateCaptureCostOption, type SimulateCapturePixelAperture, SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES, SIMULATE_CAPTURE_FAST_OPTIONS, type SimulateCaptureFastOption, type SimulateCaptureFastReport, type SimulateCaptureDemosaicRecord, simulateIdealReference, type IdealReferenceResult, serializeSimulatedCaptureDngFromParts, type SimulateCaptureDngPart, type SimulateDisplayViewOptions, type DisplayViewResult, SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS } from "./facade/simulate-capture.js";
|
|
203
|
+
export { SIMULATE_CAPTURE_FACADE_VERSION, SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX, SIMULATE_CAPTURE_COST_OPTIONS, SIMULATE_CAPTURE_METERING_MODES, simulateCapture, type SimulateCaptureOptions, type SimulateCaptureResult, type SimulateCaptureAssumption, type SimulateCaptureProgress, type SimulateCaptureExposureMode, type SimulateCaptureMetering, type SimulateCaptureAutoIsoAfterMaximum, type SimulateCaptureAutoIsoReport, type SimulateCaptureAutomaticSlowestShutterOptions, type SimulateCaptureAutomaticSlowestShutterReport, type SimulateCaptureOutputFormat, type SimulateCaptureWhiteBalance, type SimulateCaptureColorEncoding, type SimulateCaptureDemosaic, type SimulateCaptureBorderHandling, type SimulateCaptureLensDistortion, type SimulateCaptureMotion, type SimulateCaptureSensorAntiAliasing, type SimulateCaptureCostOption, type SimulateCapturePixelAperture, SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES, SIMULATE_CAPTURE_FAST_OPTIONS, type SimulateCaptureFastOption, type SimulateCaptureFastReport, type SimulateCaptureDemosaicRecord, simulateIdealReference, type IdealReferenceResult, serializeSimulatedCaptureDngFromParts, type SimulateCaptureDngPart, type SimulateDisplayViewOptions, type DisplayViewResult, SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS } from "./facade/simulate-capture.js";
|
|
204
204
|
export { simulateDisplayView, prepareDisplayView, type PreparedDisplayView, type PreparedDisplayViewRenderOptions, serializeDisplayViewCorrectionStates, bindDisplayViewCorrectionStates, type DisplayViewCorrectionStateKeys, type DisplayViewCorrectionStateSet } from "./facade/simulate-display-view.js";
|
|
205
205
|
export type { DisplayViewGpuMode, PrepareDisplayViewOptions, DisplayViewGpuFallbackReason, DisplayViewGpuConformance, DisplayViewExecution, DisplayViewFastReport } from "./facade/display-view-gpu.js";
|
|
206
206
|
export { type OutputTextMetadata, addJpegTextMetadata, addPngTextMetadata } from "./output/text-metadata.js";
|
|
207
|
+
export { GENERIC_THIRD_STOP_ISO_SETTINGS, GENERIC_THIRD_STOP_SHUTTER_SECONDS, GENERIC_WHOLE_STOP_ISO_SETTINGS, type GenericTierPresetVersion } from "./equipment/generic-tier-assets.js";
|
|
207
208
|
export { GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM, GENERIC_PRIME_THIRD_STOP_F_NUMBERS, GENERIC_PRIME_MINIMUM_FOCUS, resolveGenericPrimeLens, calculateGenericPrimeMinimumFocusDistanceM, type GenericPrimeLens, type GenericPrimeEquivalentFocalLengthMm } from "./equipment/generic-prime-lenses.js";
|
|
208
209
|
export { calculateChartFraming, calculateSubjectDistanceForFraming, type CalculateChartFramingInput, type CalculateSubjectDistanceForFramingInput, type ChartFraming, type SubjectFraming, type SubjectFramingFit } from "./facade/subject-framing.js";
|
|
209
|
-
export { CHART_SCENE_PRESETS, CHART_SCENE_DEFAULTS, CHART_SPECTRAL_RANGE_NANOMETERS, resolveChartScene, calculateStepWedgeReflectances, calculatePointGridCenters, type ChartScene, type ChartScenePresetId, type ChartReflectance, type ResolvedChartScene, type UniformChartScene, type SlantedEdgeChartScene, type SiemensStarChartScene, type StepWedgeChartScene, type GreyRampChartScene, type CheckerboardChartScene, type PointGridChartScene, type PatchChartScene, CHART_CONTINUOUS_LEVELS, calculateBarGroupTargets, calculateSlantedSquareEdges, type DeadLeavesChartScene, type BarGroupsChartScene, type HyperbolicWedgeChartScene, type ZonePlateChartScene, type SlantedSquaresChartScene, type LineGridChartScene, type FiducialChartScene, type CompositeChartScene, type CompositeChartElement, type ChartElementScene, type ChartRectangle, type ChartBarGroupTarget, type ChartSlantedSquare } from "./facade/chart-scenes.js";
|
|
210
|
+
export { CHART_SCENE_PRESETS, CHART_SCENE_DEFAULTS, CHART_SPECTRAL_RANGE_NANOMETERS, resolveChartScene, calculateStepWedgeReflectances, calculatePointGridCenters, type ChartScene, type ChartScenePresetId, type ChartReflectance, type ResolvedChartScene, type UniformChartScene, type SlantedEdgeChartScene, type SiemensStarChartScene, type StepWedgeChartScene, type GreyRampChartScene, type CheckerboardChartScene, type PointGridChartScene, type PatchChartScene, CHART_CONTINUOUS_LEVELS, calculateBarGroupTargets, calculateSlantedSquareEdges, type DeadLeavesChartScene, type BarGroupsChartScene, type HyperbolicWedgeChartScene, type ZonePlateChartScene, type SlantedSquaresChartScene, type LineGridChartScene, type FiducialChartScene, type CompositeChartScene, type CompositeChartElement, type ChartElementScene, type ChartRectangle, type ChartBarGroupTarget, type ChartSlantedSquare, type ColorPatchReflectance, type ColorPatchesChartScene } from "./facade/chart-scenes.js";
|
|
211
|
+
export { COLOR_PATCH_SET_ID, COLOR_PATCH_SET_VERSION, COLOR_PATCH_IDS, COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS, getColorPatchSet, evaluateColorPatchReflectance, type ColorPatch, type ColorPatchId, type ColorPatchGroup, type ColorPatchSet, type ColorPatchSpectralTerm, type EvaluateColorPatchReflectanceInput } from "./facade/color-patches.js";
|
|
210
212
|
export { PHOTOPIC_MAXIMUM_LUMINOUS_EFFICACY_LM_PER_W, resolveCaptureIlluminant, createPlanarSceneRadianceProvider, CHART_SIZE_DEFAULT_SURROUND_REFLECTANCE, CALLER_MATERIAL_SCENE_MAX_MATERIALS, type ChartSizeOption, type CaptureIlluminant, type ResolvedCaptureIlluminant, type CallerReflectanceScene, type CallerRadianceScene, type CallerMaterialScene, type ScenePlanePoint, type SceneMaterialPoint, type PlanarSceneRadianceProvider } from "./facade/scene-radiance.js";
|
|
211
213
|
export { CHART_RADIANCE_BATCH_SCHEMA_VERSION, CHART_RADIANCE_BATCH_LIMITS, calculateChartRadianceBatchPlan, calculateChartRadianceBatch, mapEnvironmentDirectionsToChartPoints, type ChartRadianceBatchInput, type ChartRadianceBatchPlan } from "./facade/chart-radiance-batch.js";
|
|
212
214
|
export { resolveChartMaterialLayers, CHART_MASK_INDEX_GRAIN, CHART_MATERIAL_LAYER_LIMITS, type ChartMaterialLayerScene, type ChartMaskLayer, type ChartMaskShape, type ChartMaskPolygon, type ChartMaskAnnulus, type ResolvedChartMaterialLayers, type ResolvedChartMaskLayer, type ResolvedChartMaskShape } from "./facade/chart-material-layers.js";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* backends are not supported package APIs.
|
|
7
7
|
* @see docs/DEVELOPERS.md for equations, coordinate/unit conventions, blockers and support limits.
|
|
8
8
|
*/
|
|
9
|
-
export { GENERIC_EQUIPMENT_TIER_PRESET_VERSION, GENERIC_EQUIPMENT_TIERS, resolveGenericEquipmentTierCatalog, createGenericEquipmentTierSelection, parseGenericEquipmentTierSelection, resolveGenericEquipmentTierLensProfiles } from "./equipment/generic-tier-presets.js";
|
|
9
|
+
export { GENERIC_EQUIPMENT_TIER_PRESET_VERSION, GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS, GENERIC_EQUIPMENT_TIERS, resolveGenericEquipmentTierCatalog, createGenericEquipmentTierSelection, parseGenericEquipmentTierSelection, resolveGenericEquipmentTierLensProfiles } from "./equipment/generic-tier-presets.js";
|
|
10
10
|
export { SENSOR_RAW_PRODUCER_SCHEMA_VERSION, SENSOR_RAW_PRODUCER_NOISE_MODEL, parseSensorRawProducerInput, simulateSensorRawFrame } from "./capture/sensor-raw-producer.js";
|
|
11
11
|
export { CAPTURE_CORRECTED_SDR_SCHEMA_VERSION, parseCaptureCorrectedSdrInput, calculateCaptureCorrectedSdr } from "./output/capture-corrected-sdr.js";
|
|
12
12
|
export { PHOTOGRAPHIC_EXPORT_SCHEMA_VERSION, PHOTOGRAPHIC_EXPORT_PIPELINE_VERSION, PROCESSED_SENSOR_RAW_MODEL_VERSION, parseExportSensorColorProfile, parsePhotographicExportInput, createPhotographicExportPair } from "./capture/photographic-export.js";
|
|
@@ -178,12 +178,14 @@ export { createFocusStackTask } from "./processing/focus-stacking.js";
|
|
|
178
178
|
export { createPanoramaTask } from "./processing/panorama.js";
|
|
179
179
|
export { SYNTHETIC_FILM_GRAIN_STREAM_ID, createBilateralFilterTask, createNonLocalMeansTask, createUnsharpMaskTask, createSyntheticFilmGrainTask } from "./processing/denoise.js";
|
|
180
180
|
export { calculateSensorApertureRays } from "./optics/sensor-aperture-rays.js";
|
|
181
|
-
export { SIMULATE_CAPTURE_FACADE_VERSION, SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX, SIMULATE_CAPTURE_COST_OPTIONS, simulateCapture, SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES, SIMULATE_CAPTURE_FAST_OPTIONS, simulateIdealReference, serializeSimulatedCaptureDngFromParts, SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS } from "./facade/simulate-capture.js";
|
|
181
|
+
export { SIMULATE_CAPTURE_FACADE_VERSION, SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX, SIMULATE_CAPTURE_COST_OPTIONS, SIMULATE_CAPTURE_METERING_MODES, simulateCapture, SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES, SIMULATE_CAPTURE_FAST_OPTIONS, simulateIdealReference, serializeSimulatedCaptureDngFromParts, SIMULATE_DISPLAY_VIEW_DEFAULT_SAMPLES_PER_AXIS } from "./facade/simulate-capture.js";
|
|
182
182
|
export { simulateDisplayView, prepareDisplayView, serializeDisplayViewCorrectionStates, bindDisplayViewCorrectionStates } from "./facade/simulate-display-view.js";
|
|
183
183
|
export { addJpegTextMetadata, addPngTextMetadata } from "./output/text-metadata.js";
|
|
184
|
+
export { GENERIC_THIRD_STOP_ISO_SETTINGS, GENERIC_THIRD_STOP_SHUTTER_SECONDS, GENERIC_WHOLE_STOP_ISO_SETTINGS } from "./equipment/generic-tier-assets.js";
|
|
184
185
|
export { GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM, GENERIC_PRIME_THIRD_STOP_F_NUMBERS, GENERIC_PRIME_MINIMUM_FOCUS, resolveGenericPrimeLens, calculateGenericPrimeMinimumFocusDistanceM } from "./equipment/generic-prime-lenses.js";
|
|
185
186
|
export { calculateChartFraming, calculateSubjectDistanceForFraming } from "./facade/subject-framing.js";
|
|
186
187
|
export { CHART_SCENE_PRESETS, CHART_SCENE_DEFAULTS, CHART_SPECTRAL_RANGE_NANOMETERS, resolveChartScene, calculateStepWedgeReflectances, calculatePointGridCenters, CHART_CONTINUOUS_LEVELS, calculateBarGroupTargets, calculateSlantedSquareEdges } from "./facade/chart-scenes.js";
|
|
188
|
+
export { COLOR_PATCH_SET_ID, COLOR_PATCH_SET_VERSION, COLOR_PATCH_IDS, COLOR_PATCH_WAVELENGTH_RANGE_NANOMETERS, getColorPatchSet, evaluateColorPatchReflectance } from "./facade/color-patches.js";
|
|
187
189
|
export { PHOTOPIC_MAXIMUM_LUMINOUS_EFFICACY_LM_PER_W, resolveCaptureIlluminant, createPlanarSceneRadianceProvider, CHART_SIZE_DEFAULT_SURROUND_REFLECTANCE, CALLER_MATERIAL_SCENE_MAX_MATERIALS } from "./facade/scene-radiance.js";
|
|
188
190
|
export { CHART_RADIANCE_BATCH_SCHEMA_VERSION, CHART_RADIANCE_BATCH_LIMITS, calculateChartRadianceBatchPlan, calculateChartRadianceBatch, mapEnvironmentDirectionsToChartPoints } from "./facade/chart-radiance-batch.js";
|
|
189
191
|
export { resolveChartMaterialLayers, CHART_MASK_INDEX_GRAIN, CHART_MATERIAL_LAYER_LIMITS } from "./facade/chart-material-layers.js";
|