mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 22:17:53 -06:00
LRC-DEV-PROCESS: process versions, so a look change can ship without changing existing edits (#146)
Each photo's develop settings record the rendering process they are interpreted by (`DevelopSettings::process`). There is one process, V1, the pipeline as it is; nothing renders differently (180 CPU 8/16-bit and GPU renders of procedural scenes, and the saved settings JSON and hash64, are byte-identical to main). Settings saved without the field load as V1 (a field-level serde default), new settings get the latest (`Default`), and V1 is left out when saving, so files and preview-cache keys stay as they were. A damaged value (anything but a whole number up to 2^32 - 1) reads as V1 too, so it never costs the rest of the settings (an unparsable `lc:settings` sidecar is ignored). Reset moves a photo to the latest process, section and slider resets don't; virtual copies inherit it; copy / paste / sync / Auto Sync / presets don't carry it; a number from a newer LightCraft is kept and renders with the newest known process. The process is not an edit (Edited badges, import-look checks). The base tone map is chosen per process in one place (`finish::base_tone`, shared by CPU and GPU). Photo > Update to Current Process (`develop.updateProcess`) moves selected photos on an older process to the latest as one undo step. Its menu enablement follows the selection; a call that names photos (`ids` / `id`) is validated on those instead, through a new opt-in `CommandSpec::explicit_targets` (execute checks enablement before the handler sees `ids`). `develop.get` reports the process. The rule and how to add V2: docs/process-versions.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8f924a9e60
commit
2e817a45f4
@@ -0,0 +1,75 @@
|
||||
# Process versions
|
||||
|
||||
Every photo's develop settings record the **rendering process** they are interpreted by
|
||||
(`DevelopSettings::process`, a `ProcessVersion` in `crates/develop/src/settings.rs`). Lightroom calls
|
||||
this the process version. It lets the look of LightCraft improve (a new default tone curve, issue #146;
|
||||
stronger Light sliders, issue #196) without changing a single photo someone has already edited.
|
||||
|
||||
Today there is one process, **V1**: LightCraft's rendering from before process versions existed.
|
||||
|
||||
## The rule
|
||||
|
||||
**A change that alters how existing settings render ships as a new process. V1 is frozen.**
|
||||
|
||||
- A deliberate change of look (tone model, slider response, default curve, how a stage interprets a
|
||||
value) goes into a new process. Photos stay on the process they have until someone runs Update to
|
||||
Current Process (or Reset).
|
||||
- Bug fixes that make output correct where it was broken (a crash, NaN or black output, a decoder
|
||||
error, CPU and GPU disagreeing) are not looks and can land in place, as before.
|
||||
- Changes to how a file is decoded or which camera colour it gets change the source, not how settings
|
||||
are interpreted; the process doesn't reach that stage today.
|
||||
|
||||
## Adding a process
|
||||
|
||||
1. Add a variant to `Process` (`V2`), append it to `Process::ALL` (oldest first), give it its number in
|
||||
`Process::version` and make it `Process::LATEST`.
|
||||
2. The compiler then points at every `match` on `Process`. The base tone map is chosen in one place,
|
||||
`lightcraft_pipeline::finish::base_tone` (both the CPU and the GPU renderer take it from there): give
|
||||
V2 its own arm and leave V1's alone. A process that changes another stage branches there the same
|
||||
way, on `s.process.process()`; if that stage is cached (`Plan::lin_key`, the planes in `local.rs`),
|
||||
add the process to its key.
|
||||
3. Keep `settings_saved_before_process_versions_load_as_v1` (`crates/develop/src/tests_process.rs`)
|
||||
passing: settings saved without the field must stay V1, not become the new latest. That is the
|
||||
field's `#[serde(default = "ProcessVersion::legacy")]`; the struct-level `Default` gives new
|
||||
settings the latest instead.
|
||||
4. Render a few procedural scenes on both processes and compare, and update `docs/parity.md`.
|
||||
|
||||
## What gets which process
|
||||
|
||||
| | Process |
|
||||
|---|---|
|
||||
| A newly imported, merged or Lightroom-imported photo | the latest |
|
||||
| Settings saved without the field (catalogs and their snapshots, versions and history, `lc:settings` in XMP sidecars, browser libraries: everything from before process versions), or with a damaged value | V1 |
|
||||
| Reset (`develop.reset`) | the latest |
|
||||
| Reset of a section or a slider | unchanged |
|
||||
| Update to Current Process (`develop.updateProcess`) | the latest, for photos on an older one; one undo step. The menu item follows the selection; a call with `ids` / `id` acts on those photos whatever is selected |
|
||||
| Virtual copy, duplicate | the source's |
|
||||
| Copy / paste / sync / Auto Sync / presets | unchanged (see below) |
|
||||
| `crs:` edits (XMP from other raw developers, Lightroom catalogs, imported presets) | unchanged; a new photo has the latest |
|
||||
| Restoring a version or history step | the one it had |
|
||||
|
||||
**Copy, paste, sync and presets** never carry the process: it is in no settings group, so the photos
|
||||
they change keep their own and interpret the pasted values with it. (Lightroom offers the process
|
||||
version as a separate checkbox when syncing; LightCraft's groups are coarser and keep it out.) Partial
|
||||
settings JSON that names `process` itself (`develop.merge`, a hand-written preset) sets it.
|
||||
|
||||
**Lightroom edits** are re-interpreted by LightCraft's mapper (`crates/engine/src/crs.rs`), which is
|
||||
written against the current rendering. `crs:ProcessVersion` numbers Adobe's renderer, not ours, and is
|
||||
ignored; a photo imported with Lightroom edits is on LightCraft's latest process, and an existing
|
||||
LightCraft photo that receives them keeps its own.
|
||||
|
||||
**A number this build doesn't know** (a library or sidecar written by a newer LightCraft) loads, is
|
||||
kept when saved, and renders with the newest process this build has. Update to Current Process never
|
||||
moves a photo to an older process; Reset does move it to this build's latest.
|
||||
|
||||
**A damaged value** (anything but a whole number from 0 to 2^32 - 1; `2.0` counts as 2) reads as V1,
|
||||
like a missing field, and costs nothing else: the rest of the settings load from the catalog, a
|
||||
snapshot, the op log or an `lc:settings` sidecar as usual. The damaged value isn't kept: the photo is
|
||||
saved as V1.
|
||||
|
||||
## Storage
|
||||
|
||||
The process is saved as `"process": <number>` in the settings JSON, except V1, which is left out.
|
||||
Settings made before process versions existed are therefore saved exactly as before, and their
|
||||
preview-cache keys (`DevelopSettings::hash64`) don't change. `develop.get` (MCP `get_develop`) always
|
||||
states the process.
|
||||
Reference in New Issue
Block a user