@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.
Files changed (128) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/CITATION.cff +1 -1
  3. package/NOTICE +5 -4
  4. package/README.md +10 -8
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +16 -10
  7. package/dist/camera/lens-projection.js +2 -1
  8. package/dist/camera/lens-projection.js.map +1 -1
  9. package/dist/capture/dng-import.js +1 -1
  10. package/dist/capture/dng-import.js.map +1 -1
  11. package/dist/capture/host-yield-cadence.js +1 -1
  12. package/dist/capture/host-yield-cadence.js.map +1 -1
  13. package/dist/capture/native-environment-optical-stage.js +1 -1
  14. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  15. package/dist/capture/native-raw.js +1 -1
  16. package/dist/capture/native-raw.js.map +1 -1
  17. package/dist/capture/packed-sensor-raw.js +3 -3
  18. package/dist/capture/packed-sensor-raw.js.map +1 -1
  19. package/dist/capture/sensor-raw-producer.js +1 -1
  20. package/dist/capture/sensor-raw-producer.js.map +1 -1
  21. package/dist/color/capture-color.js +3 -3
  22. package/dist/color/capture-color.js.map +1 -1
  23. package/dist/color/chromatic-adaptation.js +2 -2
  24. package/dist/color/chromatic-adaptation.js.map +1 -1
  25. package/dist/color/color-matching-functions.js +1 -1
  26. package/dist/color/color-matching-functions.js.map +1 -1
  27. package/dist/color/colorimetry-math.js +2 -2
  28. package/dist/color/colorimetry-math.js.map +1 -1
  29. package/dist/color/illuminants.js +3 -3
  30. package/dist/color/illuminants.js.map +1 -1
  31. package/dist/color/rgb-color-spaces.js +1 -1
  32. package/dist/color/rgb-color-spaces.js.map +1 -1
  33. package/dist/color/sensor-color-development.js +1 -1
  34. package/dist/color/sensor-color-development.js.map +1 -1
  35. package/dist/core/record-validation.js +3 -3
  36. package/dist/core/record-validation.js.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/core/version.js.map +1 -1
  40. package/dist/equipment/generic-prime-lenses.js +3 -3
  41. package/dist/equipment/generic-prime-lenses.js.map +1 -1
  42. package/dist/equipment/generic-tier-assets.d.ts +23 -1
  43. package/dist/equipment/generic-tier-assets.js +58 -30
  44. package/dist/equipment/generic-tier-assets.js.map +1 -1
  45. package/dist/equipment/generic-tier-presets.d.ts +6 -3
  46. package/dist/equipment/generic-tier-presets.js +26 -13
  47. package/dist/equipment/generic-tier-presets.js.map +1 -1
  48. package/dist/exposure/auto-iso-slowest-shutter.js +2 -1
  49. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  50. package/dist/exposure/flash-ttl.js +1 -1
  51. package/dist/exposure/flash-ttl.js.map +1 -1
  52. package/dist/exposure/flash.js +1 -1
  53. package/dist/exposure/flash.js.map +1 -1
  54. package/dist/facade/chart-material-layers.js +2 -2
  55. package/dist/facade/chart-material-layers.js.map +1 -1
  56. package/dist/facade/chart-radiance-batch.js +1 -1
  57. package/dist/facade/chart-radiance-batch.js.map +1 -1
  58. package/dist/facade/chart-scenes.d.ts +45 -8
  59. package/dist/facade/chart-scenes.js +101 -44
  60. package/dist/facade/chart-scenes.js.map +1 -1
  61. package/dist/facade/color-patches.d.ts +129 -0
  62. package/dist/facade/color-patches.js +209 -0
  63. package/dist/facade/color-patches.js.map +1 -0
  64. package/dist/facade/display-view-gpu.js +1 -1
  65. package/dist/facade/display-view-gpu.js.map +1 -1
  66. package/dist/facade/scene-radiance.js +2 -2
  67. package/dist/facade/scene-radiance.js.map +1 -1
  68. package/dist/facade/simulate-capture.d.ts +47 -3
  69. package/dist/facade/simulate-capture.js +71 -22
  70. package/dist/facade/simulate-capture.js.map +1 -1
  71. package/dist/facade/simulate-display-view.js +2 -2
  72. package/dist/facade/simulate-display-view.js.map +1 -1
  73. package/dist/gpu/chart-composite-gpu.js +2 -2
  74. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  75. package/dist/gpu/display-view-composite-material-module.js +1 -1
  76. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  77. package/dist/gpu/image-kernel-execution.js +2 -2
  78. package/dist/gpu/image-kernel-execution.js.map +1 -1
  79. package/dist/gpu/temporal-sum-gpu.js +23 -23
  80. package/dist/gpu/webgpu-runtime.js +1 -1
  81. package/dist/gpu/webgpu-runtime.js.map +1 -1
  82. package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
  83. package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
  84. package/dist/gpu/wgsl-chart-kernel.js +117 -117
  85. package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
  86. package/dist/gpu/wgsl-display-view-quality-kernel.js +505 -505
  87. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  88. package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
  89. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  90. package/dist/gpu/wgsl-kernels.js +203 -203
  91. package/dist/gpu/wgsl-kernels.js.map +1 -1
  92. package/dist/gpu/wgsl-sensor-kernel.js +302 -302
  93. package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
  94. package/dist/index.d.ts +5 -3
  95. package/dist/index.js +4 -2
  96. package/dist/index.js.map +1 -1
  97. package/dist/motion/temporal-exposure-sum.js +1 -1
  98. package/dist/motion/temporal-exposure-sum.js.map +1 -1
  99. package/dist/optics/diffraction-psf-profile.js +4 -4
  100. package/dist/optics/diffraction-psf-profile.js.map +1 -1
  101. package/dist/optics/front-of-lens-filter.js +1 -1
  102. package/dist/optics/front-of-lens-filter.js.map +1 -1
  103. package/dist/optics/irradiance-convolution.js +1 -1
  104. package/dist/optics/irradiance-convolution.js.map +1 -1
  105. package/dist/output/native-capture-sdr.js +1 -1
  106. package/dist/output/native-capture-sdr.js.map +1 -1
  107. package/dist/output/png-encoder.js +2 -2
  108. package/dist/output/png-encoder.js.map +1 -1
  109. package/dist/output/print-contrast-reference.js +2 -2
  110. package/dist/output/print-contrast-reference.js.map +1 -1
  111. package/dist/output/print-jpeg.js +1 -1
  112. package/dist/output/print-jpeg.js.map +1 -1
  113. package/dist/sensor/fixed-pattern-noise.js +1 -1
  114. package/dist/sensor/fixed-pattern-noise.js.map +1 -1
  115. package/dist/sensor/readout-exposure-linkage.js +1 -1
  116. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  117. package/docs/API_REFERENCE.md +29 -1
  118. package/docs/QUICK_START.md +40 -5
  119. package/docs/USAGE.md +35 -1
  120. package/docs/api/core-version.md +1 -1
  121. package/docs/api/equipment-generic-tier-assets.md +41 -1
  122. package/docs/api/equipment-generic-tier-presets.md +11 -3
  123. package/docs/api/exports.json +107 -2
  124. package/docs/api/facade-chart-scenes.md +36 -5
  125. package/docs/api/facade-color-patches.md +150 -0
  126. package/docs/api/facade-simulate-capture.md +52 -2
  127. package/llms.txt +1 -0
  128. package/package.json +2 -2
@@ -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 = Object.freeze([\"Float64 engine reference execution; results are the reference by definition\"]);\nexport const WEBGPU_F32_LIMITATIONS = 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\"`). */\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"]}
@@ -45,29 +45,29 @@ struct Node { ox: i32, oy: i32, pad0: i32, pad1: i32, b: vec4<f32> };
45
45
  fn sample(sx: i32, sy: i32) -> f32 { return source[u32(sy - i32(params.in_row0)) * params.width + u32(sx)]; }
46
46
  @compute @workgroup_size(${TEMPORAL_WORKGROUP_EDGE}, ${TEMPORAL_WORKGROUP_EDGE})
47
47
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
48
- let x = i32(gid.x);
49
- let local_y = gid.y;
50
- if (gid.x >= params.width || local_y >= params.out_rows) { return; }
51
- let y = i32(params.out_row0 + local_y);
52
- let w = i32(params.width);
53
- let h = i32(params.height);
54
- var sum = 0.0;
55
- for (var k = 0u; k < params.nodes; k = k + 1u) {
56
- let node = nodes[k];
57
- let sx = x + node.ox;
58
- let sy = y + node.oy;
59
- let left = sx >= 0 && sx < w;
60
- let right = sx + 1 >= 0 && sx + 1 < w;
61
- let top = sy >= 0 && sy < h;
62
- let bottom = sy + 1 >= 0 && sy + 1 < h;
63
- var inner = 0.0;
64
- if (top && left) { inner = inner + node.b.x * sample(sx, sy); }
65
- if (top && right) { inner = inner + node.b.y * sample(sx + 1, sy); }
66
- if (bottom && left) { inner = inner + node.b.z * sample(sx, sy + 1); }
67
- if (bottom && right) { inner = inner + node.b.w * sample(sx + 1, sy + 1); }
68
- sum = sum + weights[k * params.out_rows + local_y] * inner;
69
- }
70
- result[local_y * params.width + gid.x] = sum;
48
+ let x = i32(gid.x);
49
+ let local_y = gid.y;
50
+ if (gid.x >= params.width || local_y >= params.out_rows) { return; }
51
+ let y = i32(params.out_row0 + local_y);
52
+ let w = i32(params.width);
53
+ let h = i32(params.height);
54
+ var sum = 0.0;
55
+ for (var k = 0u; k < params.nodes; k = k + 1u) {
56
+ let node = nodes[k];
57
+ let sx = x + node.ox;
58
+ let sy = y + node.oy;
59
+ let left = sx >= 0 && sx < w;
60
+ let right = sx + 1 >= 0 && sx + 1 < w;
61
+ let top = sy >= 0 && sy < h;
62
+ let bottom = sy + 1 >= 0 && sy + 1 < h;
63
+ var inner = 0.0;
64
+ if (top && left) { inner = inner + node.b.x * sample(sx, sy); }
65
+ if (top && right) { inner = inner + node.b.y * sample(sx + 1, sy); }
66
+ if (bottom && left) { inner = inner + node.b.z * sample(sx, sy + 1); }
67
+ if (bottom && right) { inner = inner + node.b.w * sample(sx + 1, sy + 1); }
68
+ sum = sum + weights[k * params.out_rows + local_y] * inner;
69
+ }
70
+ result[local_y * params.width + gid.x] = sum;
71
71
  }
72
72
  `;
73
73
  function checkAbort(abort) {
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { ImageKernelExecutionError } from "./image-kernel-execution.js";
9
9
  /** WebGPU spec constants (GPUBufferUsage, GPUMapMode), defined here so no global is needed at import time. */
10
- export const BUFFER_USAGE = Object.freeze({ MAP_READ: 0x0001, COPY_SRC: 0x0004, COPY_DST: 0x0008, UNIFORM: 0x0040, STORAGE: 0x0080 });
10
+ export const BUFFER_USAGE = /* @__PURE__ */ Object.freeze({ MAP_READ: 0x0001, COPY_SRC: 0x0004, COPY_DST: 0x0008, UNIFORM: 0x0040, STORAGE: 0x0080 });
11
11
  const MAP_MODE_READ = 0x0001;
12
12
  /** Minimum per-binding storage this backend requires (the WebGPU default is 128 MiB). */
13
13
  export const WEBGPU_MINIMUM_STORAGE_BINDING_BYTES = 16 * 1024 * 1024;
@@ -1 +1 @@
1
- {"version":3,"file":"webgpu-runtime.js","sourceRoot":"","sources":["../../src/gpu/webgpu-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Internal WebGPU device handling for the image-kernel accelerator: getting the adapter and device,\n * limits, tracking device loss, racing work against abort and loss, error scopes, banded buffers and\n * cleanup. Not exported from the package root. WebGPU types never appear in the published type\n * declarations, so consumers do not need WebGPU typings.\n */\nimport { ImageKernelExecutionError, type ImageKernelAdapterDescription, type ImageKernelFallbackReason } from \"./image-kernel-execution.js\";\n\n/** WebGPU spec constants (GPUBufferUsage, GPUMapMode), defined here so no global is needed at import time. */\nexport const BUFFER_USAGE = Object.freeze({ MAP_READ: 0x0001, COPY_SRC: 0x0004, COPY_DST: 0x0008, UNIFORM: 0x0040, STORAGE: 0x0080 });\nconst MAP_MODE_READ = 0x0001;\n\n/** Minimum per-binding storage this backend requires (the WebGPU default is 128 MiB). */\nexport const WEBGPU_MINIMUM_STORAGE_BINDING_BYTES = 16 * 1024 * 1024;\n/** Upper limit requested from the adapter, so one band never asks for more than this per binding. */\nexport const WEBGPU_REQUESTED_STORAGE_BINDING_BYTES = 1024 * 1024 * 1024;\n\nexport interface WebGpuRuntime {\n readonly device: GPUDevice;\n /** #481: the adapter's `info`, or null when the implementation has none. */\n readonly adapter: ImageKernelAdapterDescription | null;\n readonly maximumStorageBindingBytes: number;\n readonly maximumWorkgroupsPerDimension: number;\n /** Set once `device.lost` settles; every later wait rejects with `device-lost`. */\n isLost(): boolean;\n /** Races a GPU promise with device loss and the operation's abort signal. */\n guard<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;\n destroy(): void;\n}\n\nexport type WebGpuAcquisition = { runtime: WebGpuRuntime } | { failure: ImageKernelFallbackReason; message: string };\n\nfunction hostIsLittleEndian(): boolean {\n return new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;\n}\n\nexport function abortedError(): ImageKernelExecutionError {\n return new ImageKernelExecutionError(\"aborted\", \"Image-kernel execution was aborted; no output is available.\");\n}\n\n/** Rejects when the signal aborts; the returned cleanup removes the listener. */\nfunction abortRace(signal: AbortSignal | undefined): { promise: Promise<never>; cleanup: () => void } {\n if (!signal) return { promise: new Promise<never>(() => undefined), cleanup: () => undefined };\n let cleanup = (): void => undefined;\n const promise = new Promise<never>((_, reject) => {\n if (signal.aborted) { reject(abortedError()); return; }\n const listener = (): void => reject(abortedError());\n signal.addEventListener(\"abort\", listener, { once: true });\n cleanup = (): void => signal.removeEventListener(\"abort\", listener);\n });\n promise.catch(() => undefined);\n return { promise, cleanup };\n}\n\n/**\n * Requests a device with high limits from `gpu` (normally `navigator.gpu`). It does not throw when\n * WebGPU is unavailable; the caller decides whether to fall back to the CPU or fail. An abort during\n * acquisition does throw.\n */\nexport async function acquireWebGpuRuntime(gpu: unknown, powerPreference: \"low-power\" | \"high-performance\" | undefined,\n signal: AbortSignal | undefined): Promise<WebGpuAcquisition> {\n if (!hostIsLittleEndian()) return { failure: \"unsupported-platform-endianness\", message: \"WebGPU buffers require a little-endian host.\" };\n const api = gpu as Partial<GPU> | null | undefined;\n if (!api || typeof api !== \"object\" || typeof api.requestAdapter !== \"function\") {\n return { failure: \"webgpu-unavailable\", message: \"WebGPU is not available in this runtime.\" };\n }\n const abort = abortRace(signal);\n try {\n let adapter: GPUAdapter | null;\n try {\n adapter = await Promise.race([api.requestAdapter(powerPreference ? { powerPreference } : undefined), abort.promise]);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) throw error;\n adapter = null;\n }\n if (!adapter) return { failure: \"adapter-unavailable\", message: \"No WebGPU adapter is available.\" };\n const adapterStorage = adapter.limits.maxStorageBufferBindingSize, adapterBuffer = adapter.limits.maxBufferSize;\n if (!(adapterStorage >= WEBGPU_MINIMUM_STORAGE_BINDING_BYTES && adapterBuffer >= WEBGPU_MINIMUM_STORAGE_BINDING_BYTES)) {\n return { failure: \"insufficient-limits\", message: \"The WebGPU adapter's storage-buffer limits are below the backend minimum.\" };\n }\n const storage = Math.min(adapterStorage, adapterBuffer, WEBGPU_REQUESTED_STORAGE_BINDING_BYTES);\n let device: GPUDevice, pending: Promise<GPUDevice> | undefined;\n try {\n pending = adapter.requestDevice({ label: \"photivra-image-kernels\",\n requiredLimits: { maxStorageBufferBindingSize: storage, maxBufferSize: storage } });\n device = await Promise.race([pending, abort.promise]);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) {\n // The abort won the race. A device that still arrives later is destroyed, so it never leaks.\n if (pending) void Promise.resolve(pending).then(late => late.destroy(), () => undefined);\n throw error;\n }\n return { failure: \"device-request-failed\", message: \"The WebGPU device request failed.\" };\n }\n if (signal?.aborted) { device.destroy(); throw abortedError(); }\n return { runtime: createRuntime(device, storage, describeAdapter(adapter)) };\n } finally {\n abort.cleanup();\n }\n}\n\n/** `GPUAdapter.info` as plain strings (missing or non-string fields read as empty), or null when the adapter has no `info`. */\nfunction describeAdapter(adapter: GPUAdapter): ImageKernelAdapterDescription | null {\n const info = (adapter as { info?: Partial<Record<keyof ImageKernelAdapterDescription, unknown>> | null }).info;\n if (!info || typeof info !== \"object\") return null;\n const text = (value: unknown): string => (typeof value === \"string\" ? value : \"\");\n return { vendor: text(info.vendor), architecture: text(info.architecture), device: text(info.device), description: text(info.description) };\n}\n\nfunction createRuntime(device: GPUDevice, storage: number, adapter: ImageKernelAdapterDescription | null): WebGpuRuntime {\n let lost = false, destroyed = false;\n let rejectLost: (error: ImageKernelExecutionError) => void = () => undefined;\n const lostPromise = new Promise<never>((_, reject) => { rejectLost = reject; });\n lostPromise.catch(() => undefined);\n void device.lost.then(() => {\n lost = true;\n rejectLost(new ImageKernelExecutionError(\"device-lost\", \"The WebGPU device was lost; no output is available.\"));\n });\n // Errors are collected through error scopes; this only keeps uncaptured errors out of the console.\n device.onuncapturederror = (event: GPUUncapturedErrorEvent): void => { event.preventDefault(); };\n const maximumWorkgroupsPerDimension = device.limits.maxComputeWorkgroupsPerDimension;\n return {\n device, adapter,\n maximumStorageBindingBytes: Math.min(storage, device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize),\n maximumWorkgroupsPerDimension,\n isLost: (): boolean => lost,\n async guard<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n promise.catch(() => undefined);\n if (lost) throw new ImageKernelExecutionError(\"device-lost\", \"The WebGPU device was lost; no output is available.\");\n const abort = abortRace(signal);\n try { return await Promise.race([promise, lostPromise, abort.promise]); } finally { abort.cleanup(); }\n },\n destroy(): void { if (!destroyed) { destroyed = true; device.destroy(); } }\n };\n}\n\n/** Per-operation resource scope: every buffer is destroyed when the operation settles. */\nexport class GpuResourceScope {\n readonly #runtime: WebGpuRuntime;\n readonly #buffers: GPUBuffer[] = [];\n\n constructor(runtime: WebGpuRuntime) { this.#runtime = runtime; }\n\n buffer(label: string, size: number, usage: number): GPUBuffer {\n const buffer = this.#runtime.device.createBuffer({ label, size: Math.max(16, Math.ceil(size / 16) * 16), usage });\n this.#buffers.push(buffer);\n return buffer;\n }\n\n /** Storage buffer initialised from host data (padded to a 16-byte multiple). */\n upload(label: string, data: ArrayBufferView<ArrayBuffer>, usage: number): GPUBuffer {\n const buffer = this.buffer(label, data.byteLength, usage | BUFFER_USAGE.COPY_DST);\n if (data.byteLength > 0) this.#runtime.device.queue.writeBuffer(buffer, 0, data.buffer, data.byteOffset, data.byteLength);\n return buffer;\n }\n\n release(): void {\n for (const buffer of this.#buffers.splice(0)) buffer.destroy();\n }\n}\n\n/** Workgroup grid for a 1-D invocation count (2-D when it exceeds one dimension). */\nexport function linearDispatch(invocations: number, workgroupSize: number, maximumPerDimension: number): [number, number] {\n const groups = Math.max(1, Math.ceil(invocations / workgroupSize));\n const x = Math.min(groups, maximumPerDimension), y = Math.ceil(groups / x);\n if (y > maximumPerDimension) {\n throw new ImageKernelExecutionError(\"insufficient-limits\", \"Band exceeds the adapter's dispatch limits.\");\n }\n return [x, y];\n}\n\n/**\n * Submits one compute pass and copies `outputs` into mappable staging buffers, then maps them. Validation\n * and out-of-memory errors are captured with error scopes and reported as domain errors.\n */\nexport async function runComputePass(runtime: WebGpuRuntime, scope: GpuResourceScope, pipeline: GPUComputePipeline,\n bindGroup: GPUBindGroup | readonly GPUBindGroup[], workgroups: readonly [number, number], outputs: readonly { buffer: GPUBuffer; bytes: number }[],\n signal: AbortSignal | undefined): Promise<ArrayBuffer[]> {\n const device = runtime.device;\n device.pushErrorScope(\"out-of-memory\");\n device.pushErrorScope(\"validation\");\n const staging = outputs.map(({ bytes }, i) =>\n scope.buffer(`photivra-readback-${i}`, bytes, BUFFER_USAGE.MAP_READ | BUFFER_USAGE.COPY_DST));\n const encoder = device.createCommandEncoder({ label: \"photivra-image-kernel\" });\n const pass = encoder.beginComputePass();\n pass.setPipeline(pipeline);\n // One bind group at index 0, or several at indices 0, 1, … (the display-view kernel binds its material module at 1).\n (Array.isArray(bindGroup) ? bindGroup as readonly GPUBindGroup[] : [bindGroup as GPUBindGroup]).forEach((group, index) => pass.setBindGroup(index, group));\n pass.dispatchWorkgroups(workgroups[0], workgroups[1]);\n pass.end();\n outputs.forEach(({ buffer }, i) => encoder.copyBufferToBuffer(buffer, 0, staging[i]!, 0, staging[i]!.size));\n device.queue.submit([encoder.finish()]);\n // Pop both scopes before any await so an abort or loss never leaves the device's scope stack unbalanced.\n const validationScope = device.popErrorScope(), memoryScope = device.popErrorScope();\n const validation = await runtime.guard(validationScope, signal);\n const memory = await runtime.guard(memoryScope, signal);\n if (memory) throw new ImageKernelExecutionError(\"out-of-memory\", \"The WebGPU device ran out of memory.\");\n if (validation) throw new ImageKernelExecutionError(\"gpu-validation\", \"WebGPU rejected an image-kernel command.\");\n const results: ArrayBuffer[] = [];\n for (let i = 0; i < staging.length; i++) {\n const buffer = staging[i]!;\n await runtime.guard(buffer.mapAsync(MAP_MODE_READ), signal);\n results.push(buffer.getMappedRange(0, Math.ceil(outputs[i]!.bytes / 4) * 4).slice(0));\n buffer.unmap();\n }\n return results;\n}\n\n/** Compiles a compute pipeline; compilation/validation failures are domain errors. */\nexport async function createComputePipeline(runtime: WebGpuRuntime, label: string, code: string,\n signal: AbortSignal | undefined): Promise<GPUComputePipeline> {\n const device = runtime.device;\n device.pushErrorScope(\"validation\");\n const module = device.createShaderModule({ label, code });\n const pending = device.createComputePipelineAsync({ label, layout: \"auto\", compute: { module, entryPoint: \"main\" } });\n const scope = device.popErrorScope();\n let pipeline: GPUComputePipeline;\n try {\n pipeline = await runtime.guard(pending, signal);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) throw error;\n throw new ImageKernelExecutionError(\"gpu-validation\", `WebGPU could not compile the ${label} kernel.`);\n }\n if (await runtime.guard(scope, signal)) {\n throw new ImageKernelExecutionError(\"gpu-validation\", `WebGPU could not compile the ${label} kernel.`);\n }\n return pipeline;\n}\n"]}
1
+ {"version":3,"file":"webgpu-runtime.js","sourceRoot":"","sources":["../../src/gpu/webgpu-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Internal WebGPU device handling for the image-kernel accelerator: getting the adapter and device,\n * limits, tracking device loss, racing work against abort and loss, error scopes, banded buffers and\n * cleanup. Not exported from the package root. WebGPU types never appear in the published type\n * declarations, so consumers do not need WebGPU typings.\n */\nimport { ImageKernelExecutionError, type ImageKernelAdapterDescription, type ImageKernelFallbackReason } from \"./image-kernel-execution.js\";\n\n/** WebGPU spec constants (GPUBufferUsage, GPUMapMode), defined here so no global is needed at import time. */\nexport const BUFFER_USAGE = /* @__PURE__ */ Object.freeze({ MAP_READ: 0x0001, COPY_SRC: 0x0004, COPY_DST: 0x0008, UNIFORM: 0x0040, STORAGE: 0x0080 });\nconst MAP_MODE_READ = 0x0001;\n\n/** Minimum per-binding storage this backend requires (the WebGPU default is 128 MiB). */\nexport const WEBGPU_MINIMUM_STORAGE_BINDING_BYTES = 16 * 1024 * 1024;\n/** Upper limit requested from the adapter, so one band never asks for more than this per binding. */\nexport const WEBGPU_REQUESTED_STORAGE_BINDING_BYTES = 1024 * 1024 * 1024;\n\nexport interface WebGpuRuntime {\n readonly device: GPUDevice;\n /** #481: the adapter's `info`, or null when the implementation has none. */\n readonly adapter: ImageKernelAdapterDescription | null;\n readonly maximumStorageBindingBytes: number;\n readonly maximumWorkgroupsPerDimension: number;\n /** Set once `device.lost` settles; every later wait rejects with `device-lost`. */\n isLost(): boolean;\n /** Races a GPU promise with device loss and the operation's abort signal. */\n guard<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;\n destroy(): void;\n}\n\nexport type WebGpuAcquisition = { runtime: WebGpuRuntime } | { failure: ImageKernelFallbackReason; message: string };\n\nfunction hostIsLittleEndian(): boolean {\n return new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;\n}\n\nexport function abortedError(): ImageKernelExecutionError {\n return new ImageKernelExecutionError(\"aborted\", \"Image-kernel execution was aborted; no output is available.\");\n}\n\n/** Rejects when the signal aborts; the returned cleanup removes the listener. */\nfunction abortRace(signal: AbortSignal | undefined): { promise: Promise<never>; cleanup: () => void } {\n if (!signal) return { promise: new Promise<never>(() => undefined), cleanup: () => undefined };\n let cleanup = (): void => undefined;\n const promise = new Promise<never>((_, reject) => {\n if (signal.aborted) { reject(abortedError()); return; }\n const listener = (): void => reject(abortedError());\n signal.addEventListener(\"abort\", listener, { once: true });\n cleanup = (): void => signal.removeEventListener(\"abort\", listener);\n });\n promise.catch(() => undefined);\n return { promise, cleanup };\n}\n\n/**\n * Requests a device with high limits from `gpu` (normally `navigator.gpu`). It does not throw when\n * WebGPU is unavailable; the caller decides whether to fall back to the CPU or fail. An abort during\n * acquisition does throw.\n */\nexport async function acquireWebGpuRuntime(gpu: unknown, powerPreference: \"low-power\" | \"high-performance\" | undefined,\n signal: AbortSignal | undefined): Promise<WebGpuAcquisition> {\n if (!hostIsLittleEndian()) return { failure: \"unsupported-platform-endianness\", message: \"WebGPU buffers require a little-endian host.\" };\n const api = gpu as Partial<GPU> | null | undefined;\n if (!api || typeof api !== \"object\" || typeof api.requestAdapter !== \"function\") {\n return { failure: \"webgpu-unavailable\", message: \"WebGPU is not available in this runtime.\" };\n }\n const abort = abortRace(signal);\n try {\n let adapter: GPUAdapter | null;\n try {\n adapter = await Promise.race([api.requestAdapter(powerPreference ? { powerPreference } : undefined), abort.promise]);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) throw error;\n adapter = null;\n }\n if (!adapter) return { failure: \"adapter-unavailable\", message: \"No WebGPU adapter is available.\" };\n const adapterStorage = adapter.limits.maxStorageBufferBindingSize, adapterBuffer = adapter.limits.maxBufferSize;\n if (!(adapterStorage >= WEBGPU_MINIMUM_STORAGE_BINDING_BYTES && adapterBuffer >= WEBGPU_MINIMUM_STORAGE_BINDING_BYTES)) {\n return { failure: \"insufficient-limits\", message: \"The WebGPU adapter's storage-buffer limits are below the backend minimum.\" };\n }\n const storage = Math.min(adapterStorage, adapterBuffer, WEBGPU_REQUESTED_STORAGE_BINDING_BYTES);\n let device: GPUDevice, pending: Promise<GPUDevice> | undefined;\n try {\n pending = adapter.requestDevice({ label: \"photivra-image-kernels\",\n requiredLimits: { maxStorageBufferBindingSize: storage, maxBufferSize: storage } });\n device = await Promise.race([pending, abort.promise]);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) {\n // The abort won the race. A device that still arrives later is destroyed, so it never leaks.\n if (pending) void Promise.resolve(pending).then(late => late.destroy(), () => undefined);\n throw error;\n }\n return { failure: \"device-request-failed\", message: \"The WebGPU device request failed.\" };\n }\n if (signal?.aborted) { device.destroy(); throw abortedError(); }\n return { runtime: createRuntime(device, storage, describeAdapter(adapter)) };\n } finally {\n abort.cleanup();\n }\n}\n\n/** `GPUAdapter.info` as plain strings (missing or non-string fields read as empty), or null when the adapter has no `info`. */\nfunction describeAdapter(adapter: GPUAdapter): ImageKernelAdapterDescription | null {\n const info = (adapter as { info?: Partial<Record<keyof ImageKernelAdapterDescription, unknown>> | null }).info;\n if (!info || typeof info !== \"object\") return null;\n const text = (value: unknown): string => (typeof value === \"string\" ? value : \"\");\n return { vendor: text(info.vendor), architecture: text(info.architecture), device: text(info.device), description: text(info.description) };\n}\n\nfunction createRuntime(device: GPUDevice, storage: number, adapter: ImageKernelAdapterDescription | null): WebGpuRuntime {\n let lost = false, destroyed = false;\n let rejectLost: (error: ImageKernelExecutionError) => void = () => undefined;\n const lostPromise = new Promise<never>((_, reject) => { rejectLost = reject; });\n lostPromise.catch(() => undefined);\n void device.lost.then(() => {\n lost = true;\n rejectLost(new ImageKernelExecutionError(\"device-lost\", \"The WebGPU device was lost; no output is available.\"));\n });\n // Errors are collected through error scopes; this only keeps uncaptured errors out of the console.\n device.onuncapturederror = (event: GPUUncapturedErrorEvent): void => { event.preventDefault(); };\n const maximumWorkgroupsPerDimension = device.limits.maxComputeWorkgroupsPerDimension;\n return {\n device, adapter,\n maximumStorageBindingBytes: Math.min(storage, device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize),\n maximumWorkgroupsPerDimension,\n isLost: (): boolean => lost,\n async guard<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n promise.catch(() => undefined);\n if (lost) throw new ImageKernelExecutionError(\"device-lost\", \"The WebGPU device was lost; no output is available.\");\n const abort = abortRace(signal);\n try { return await Promise.race([promise, lostPromise, abort.promise]); } finally { abort.cleanup(); }\n },\n destroy(): void { if (!destroyed) { destroyed = true; device.destroy(); } }\n };\n}\n\n/** Per-operation resource scope: every buffer is destroyed when the operation settles. */\nexport class GpuResourceScope {\n readonly #runtime: WebGpuRuntime;\n readonly #buffers: GPUBuffer[] = [];\n\n constructor(runtime: WebGpuRuntime) { this.#runtime = runtime; }\n\n buffer(label: string, size: number, usage: number): GPUBuffer {\n const buffer = this.#runtime.device.createBuffer({ label, size: Math.max(16, Math.ceil(size / 16) * 16), usage });\n this.#buffers.push(buffer);\n return buffer;\n }\n\n /** Storage buffer initialised from host data (padded to a 16-byte multiple). */\n upload(label: string, data: ArrayBufferView<ArrayBuffer>, usage: number): GPUBuffer {\n const buffer = this.buffer(label, data.byteLength, usage | BUFFER_USAGE.COPY_DST);\n if (data.byteLength > 0) this.#runtime.device.queue.writeBuffer(buffer, 0, data.buffer, data.byteOffset, data.byteLength);\n return buffer;\n }\n\n release(): void {\n for (const buffer of this.#buffers.splice(0)) buffer.destroy();\n }\n}\n\n/** Workgroup grid for a 1-D invocation count (2-D when it exceeds one dimension). */\nexport function linearDispatch(invocations: number, workgroupSize: number, maximumPerDimension: number): [number, number] {\n const groups = Math.max(1, Math.ceil(invocations / workgroupSize));\n const x = Math.min(groups, maximumPerDimension), y = Math.ceil(groups / x);\n if (y > maximumPerDimension) {\n throw new ImageKernelExecutionError(\"insufficient-limits\", \"Band exceeds the adapter's dispatch limits.\");\n }\n return [x, y];\n}\n\n/**\n * Submits one compute pass and copies `outputs` into mappable staging buffers, then maps them. Validation\n * and out-of-memory errors are captured with error scopes and reported as domain errors.\n */\nexport async function runComputePass(runtime: WebGpuRuntime, scope: GpuResourceScope, pipeline: GPUComputePipeline,\n bindGroup: GPUBindGroup | readonly GPUBindGroup[], workgroups: readonly [number, number], outputs: readonly { buffer: GPUBuffer; bytes: number }[],\n signal: AbortSignal | undefined): Promise<ArrayBuffer[]> {\n const device = runtime.device;\n device.pushErrorScope(\"out-of-memory\");\n device.pushErrorScope(\"validation\");\n const staging = outputs.map(({ bytes }, i) =>\n scope.buffer(`photivra-readback-${i}`, bytes, BUFFER_USAGE.MAP_READ | BUFFER_USAGE.COPY_DST));\n const encoder = device.createCommandEncoder({ label: \"photivra-image-kernel\" });\n const pass = encoder.beginComputePass();\n pass.setPipeline(pipeline);\n // One bind group at index 0, or several at indices 0, 1, … (the display-view kernel binds its material module at 1).\n (Array.isArray(bindGroup) ? bindGroup as readonly GPUBindGroup[] : [bindGroup as GPUBindGroup]).forEach((group, index) => pass.setBindGroup(index, group));\n pass.dispatchWorkgroups(workgroups[0], workgroups[1]);\n pass.end();\n outputs.forEach(({ buffer }, i) => encoder.copyBufferToBuffer(buffer, 0, staging[i]!, 0, staging[i]!.size));\n device.queue.submit([encoder.finish()]);\n // Pop both scopes before any await so an abort or loss never leaves the device's scope stack unbalanced.\n const validationScope = device.popErrorScope(), memoryScope = device.popErrorScope();\n const validation = await runtime.guard(validationScope, signal);\n const memory = await runtime.guard(memoryScope, signal);\n if (memory) throw new ImageKernelExecutionError(\"out-of-memory\", \"The WebGPU device ran out of memory.\");\n if (validation) throw new ImageKernelExecutionError(\"gpu-validation\", \"WebGPU rejected an image-kernel command.\");\n const results: ArrayBuffer[] = [];\n for (let i = 0; i < staging.length; i++) {\n const buffer = staging[i]!;\n await runtime.guard(buffer.mapAsync(MAP_MODE_READ), signal);\n results.push(buffer.getMappedRange(0, Math.ceil(outputs[i]!.bytes / 4) * 4).slice(0));\n buffer.unmap();\n }\n return results;\n}\n\n/** Compiles a compute pipeline; compilation/validation failures are domain errors. */\nexport async function createComputePipeline(runtime: WebGpuRuntime, label: string, code: string,\n signal: AbortSignal | undefined): Promise<GPUComputePipeline> {\n const device = runtime.device;\n device.pushErrorScope(\"validation\");\n const module = device.createShaderModule({ label, code });\n const pending = device.createComputePipelineAsync({ label, layout: \"auto\", compute: { module, entryPoint: \"main\" } });\n const scope = device.popErrorScope();\n let pipeline: GPUComputePipeline;\n try {\n pipeline = await runtime.guard(pending, signal);\n } catch (error) {\n if (error instanceof ImageKernelExecutionError) throw error;\n throw new ImageKernelExecutionError(\"gpu-validation\", `WebGPU could not compile the ${label} kernel.`);\n }\n if (await runtime.guard(scope, signal)) {\n throw new ImageKernelExecutionError(\"gpu-validation\", `WebGPU could not compile the ${label} kernel.`);\n }\n return pipeline;\n}\n"]}