# Photograph Pipeline Decisions

This page records high-impact architecture decisions for the image pipeline using [Architecture Decision Records (ADR)](https://adr.github.io/).

## Decision Index

| ID | Decision | Status |
| --- | --- | --- |
| ADR-001 | Require Vulkan GPU (prefer discrete, allow integrated) for normal runtime | Accepted |
| ADR-002 | Allow CPU fallback only with explicit debug env flag | Accepted |
| ADR-003 | Use one GPU pipeline for both preview and export | Accepted |
| ADR-004 | Keep async preview generation/cancellation semantics | Accepted |
| ADR-005 | Use parity tests with guarded fill-skip behavior | Accepted |
| ADR-006 | Apply highlight recovery during RAW develop, before sRGB gamma | Accepted |

## ADR-001: Require Vulkan GPU (Prefer Discrete, Allow Integrated)

- Context: The project is optimized for Linux Vulkan execution, but integrated-GPU-only systems are still valid runtime targets.
- Decision: Initialize [`wgpu`](https://wgpu.rs/) with Vulkan-only backend and select the best non-CPU adapter, preferring discrete when available.
- Why: Keeps predictable GPU execution while avoiding unnecessary startup rejection on integrated-only systems.
- Implementation: `src/processing/gpu_pipeline.rs` (`init_gpu_context`).

## ADR-002: CPU Fallback Is Debug-Only

- Context: Silent runtime fallback to CPU hides performance regressions and policy violations.
- Decision: CPU fallback is disabled by default and only enabled via `PHOTOGRAPH_DEBUG_ALLOW_CPU_FALLBACK=1`.
- Why: Keeps performance expectations deterministic in normal operation.
- Implementation:
  - `src/processing/gpu_pipeline.rs` (`DEBUG_ALLOW_CPU_FALLBACK_ENV`, `allow_debug_cpu_fallback`)
  - `src/main.rs` (startup enforcement)
  - `src/viewer.rs` and `src/app.rs` (preview/export behavior)

## ADR-003: One Processing Backend Contract for Preview and Export

- Context: Divergent preview/export backends increase parity bugs and maintenance cost.
- Decision: Both preview and export attempt `gpu_pipeline::try_apply` first and follow the same fallback policy.
- Why: One contract improves predictability and testing leverage.
- Implementation:
  - Preview: `src/viewer.rs`
  - Export: `src/app.rs`

## ADR-004: Preserve Generation-Based Preview Cancellation

- Context: Rapid UI updates can apply stale frames when background jobs complete out of order.
- Decision: Keep generation tokens and stale-result dropping in viewer background processing.
- Why: Ensures responsive editing without visual rollback artifacts.
- Implementation: `src/viewer.rs`.

## ADR-005: Guard Against False-Positive Parity Tests

- Context: Simple fill-pixel skipping can hide severe regressions (for example near-black outputs).
- Decision: Keep fill-aware comparisons, but bound the allowed skipped-fill ratio.
- Why: Maintains tolerance for boundary interpolation differences while still catching broken output.
- Implementation: `src/processing/gpu_pipeline.rs` test helpers.

## ADR-006: Apply Highlight Recovery During RAW Develop

- Context: The default `RawDevelop` pipeline applies sRGB gamma then converts to u16, clipping any channel values above 1.0 in linear space. In overexposed RAW regions where only some channels are sensor-saturated, the unclipped channels carry recoverable scene information that is lost by this clipping.
- Decision: Use a custom `RawDevelop` pipeline that omits the `SRgb` step, apply highlight reconstruction on the linear f32 intermediate data, then apply sRGB gamma before conversion to `DynamicImage`.
- Why: Operating on linear f32 data between calibration and gamma allows reconstruction of partially-clipped highlights. Clipped channels are rebuilt from a luminance/chroma decomposition so warm/cool highlight bias is preserved better, and a near-clip shoulder rolloff reduces hard clipping without darkening broad bright tones. This improves highlight gradation without changing the downstream GPU/CPU processing pipeline.
- Algorithm: Two-pass over linear RGB pixels:
  1. **Channel reconstruction**: Pixels with 1 or 2 channels above clip threshold (0.99) rebuild clipped channels from a luminance/chroma decomposition anchored by unclipped-channel luminance.
  2. **Near-clip shoulder**: An exponential shoulder rolloff engages only above 0.95, compressing near-clip values gently into [0, 1].
- Implementation:
  - `src/processing/highlights.rs` (recovery algorithm)
  - `src/thumbnail.rs` (`develop_raw_with_recovery`)
- Consistency: Both preview (Stage B full decode) and export flow through `open_image()`, so recovery policy is shared automatically (per ADR-003).

## Decision Relationship

```mermaid
flowchart TD
    A[ADR-001: Vulkan GPU policy] --> B[ADR-002: Debug-only CPU fallback]
    A --> C[ADR-003: Shared preview/export GPU contract]
    C --> E[ADR-005: Strong parity checks]
    D[ADR-004: Generation cancellation] --> C
    D --> E
    F[ADR-006: RAW highlight recovery] --> C
```

## Revisit Triggers

- Hardware target changes from Vulkan-focused Linux profile to broader cross-platform release goals.
- GPU texture-size limits materially impact export workflows.
- Future `wgpu`/driver changes require backend policy adjustments.
