Integration: Calibration keeps the Camera Raw preview on the CPU develop (#5128, #6923)

#6923 develops the Camera Raw dialog's preview on the GPU whenever no spatial stage is set,
from `per_pixel`'s stages. #5128 adds Calibration (primaries and Shadows tint) to the CPU
develop between white balance and Dehaze, which those stages don't run, so with both merged
a Calibration-only change previewed unchanged on the GPU while Open applied it. `per_pixel`
now returns None when Calibration is set, so the dialog develops on the CPU then. The
white and black points stay as they were, so the dialog's points cache is still right once
Calibration goes back to neutral. The hardware parity doc and BUG-204-3's note now say so.

Co-authored-by: Fabio Lissi <3223836+FabioLissi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Mann
2026-10-25 13:08:06 +01:00
parent 7eec69f8f5
commit a222c2aee4
5 changed files with 22 additions and 6 deletions
+15
View File
@@ -1257,6 +1257,21 @@ mod tests {
assert!(v[0][0] < 0.35 && (v[(h / 2) * w + w / 2][0] - 0.5).abs() < 0.01, "{:?}", v[0]);
}
#[test]
fn calibration_keeps_the_develop_off_the_per_pixel_gpu_stages() {
// The per-pixel stages (the Camera Raw dialog's GPU preview) don't run Calibration, so
// settings with it must fall back to the CPU develop instead of previewing without it.
for p in [
CameraRaw { shadow_tint: 40.0, ..Default::default() },
CameraRaw { calibration_hue: [0.0, 0.0, 30.0], whites: 20.0, ..Default::default() },
CameraRaw { calibration_sat: [-50.0, 0.0, 0.0], exposure: 0.5, ..Default::default() },
] {
assert!(!p.spatial());
assert!(per_pixel(&p, 2, 2, false, Some((0.0, 1.0))).is_none(), "{p:?}");
}
assert!(per_pixel(&CameraRaw { exposure: 0.5, ..Default::default() }, 2, 2, false, None).is_some());
}
#[test]
fn calibration_turns_primaries_keeps_neutrals_and_tints_shadows() {
let w = calibration_weights(0.0);
+4 -3
View File
@@ -202,10 +202,11 @@ pub fn white_black_points(px: &[[f32; 4]], w: usize, h: usize, p: &CameraRaw) ->
}
/// Develop's stages for settings without spatial stages, on a `w × h` image; `points` are the
/// image's [`white_black_points`]. `None` when `p` needs a spatial stage, or Whites or Blacks is
/// set without `points`.
/// image's [`white_black_points`]. `None` when `p` needs a spatial stage or Calibration (these
/// stages don't run it), or Whites or Blacks is set without `points`. The points themselves
/// leave Calibration out, so a caller may cache them across Calibration changes.
pub fn per_pixel(p: &CameraRaw, w: usize, h: usize, float: bool, points: Option<(f32, f32)>) -> Option<PerPixel> {
if p.spatial() {
if p.spatial() || !p.calibration_neutral() {
return None;
}
let identity = p.is_identity();
+1 -1
View File
@@ -19,7 +19,7 @@ HDR display to verify.
|---|---|---|---|
| GPU canvas, zoom/pan, rotate view | Metal (macOS), DirectX/OpenCL (Windows) | wgpu 30: Metal, Vulkan, DX12, GL; backend selectable | done |
| GPU compositing of layers, blend modes, effects | partial (Photoshop composites mostly on CPU) | wgpu compositor, ≤ 1/255 vs CPU oracle; Multichannel and > texture-limit regions fall back to CPU | done (ahead in places) |
| GPU filters (Blur Gallery, Liquify, Smart Sharpen, Camera Raw) | yes | CPU (rayon) for filters; GPU for compose and effects. Camera Raw's preview runs its per-pixel stages (white balance to grain) in one wgpu pass, about 2-3 ms vs 11-28 ms on the CPU for the 858×572 proxy, depending on the machine; spatial stages and OK still develop on the CPU (#6916) | partial |
| GPU filters (Blur Gallery, Liquify, Smart Sharpen, Camera Raw) | yes | CPU (rayon) for filters; GPU for compose and effects. Camera Raw's preview runs its per-pixel stages (white balance to grain) in one wgpu pass, about 2-3 ms vs 11-28 ms on the CPU for the 858×572 proxy, depending on the machine; spatial stages, Calibration and OK still develop on the CPU (#6916) | partial |
| Documents larger than the GPU texture limit | yes (tiled) | CPU fallback; tiling in progress (#49) | partial |
| CPU-only fallback when no GPU | yes, reduced | `cpu` canvas preference; fails to start without an adapter on some Linux systems (#2519, #2412) | partial |
| Choosing the GPU on a multi-GPU machine | Preferences › Performance › Use Graphics Processor | Linux only: Preferences › Performance › Advanced › GPU adapter and `PHOTOCRAFT_GPU_ADAPTER` (#1885); ignored on Windows and macOS | partial |
+1 -1
View File
@@ -292,7 +292,7 @@ Never-crash attribute on 30 of 30 crate roots.
| BUG-203-6 | Failure-injection tests: mid-write, rename and read-only dir errors keep original bytes | done | [#203](https://github.com/storytold/photocraft/issues/203) | format/src/atomic/tests.rs: Faulty AtomicFs injects mid-write StorageFull, rename failure and a read-only dir; locked-target retries, symlink and file-mode preservation covered too |
| BUG-204-1 | Interface prefs (UI scale, font size, channels in colour, sliders, tooltips) wired or hidden | done | [#204](https://github.com/storytold/photocraft/issues/204) | prefs_ui.rs tick reads ui_scale (sync_display_scale), ui_font_size (theme::set_ui_font_size) and show_tooltips (sync_tooltips); show_channels_in_color is wired in channel_thumbs and the canvas channel view (#2385); dynamic_color_sliders is in prefs::HIDDEN_UNTIL_IMPLEMENTED |
| BUG-204-2 | workspace.large_tabs, animated_zoom, auto_show_home_screen wired or hidden | done | [#204](https://github.com/storytold/photocraft/issues/204) | workspace.large_tabs controls document tab height in canvas.rs; auto_show_home_screen drives the Home screen; general.animated_zoom gates discrete canvas zoom transitions and continuous held Zoom tool presses (zoom_hold.rs, #6210), and is exposed in General preferences |
| BUG-204-3 | RAW prefs and ask_before_saving_layered_tiff read, or hidden | done | [#204](https://github.com/storytold/photocraft/issues/204) | ask_before_saving_layered_tiff drives the TIFF Options prompt (ui-egui tiff_options_ui.rs); raw_defaults.open_in_camera_raw drives the Camera Raw dialog (camera_raw_ui.rs); the other raw_defaults fields are in prefs::HIDDEN_UNTIL_IMPLEMENTED |
| BUG-204-3 | RAW prefs and ask_before_saving_layered_tiff read, or hidden | done | [#204](https://github.com/storytold/photocraft/issues/204) | ask_before_saving_layered_tiff drives the TIFF Options prompt (ui-egui tiff_options_ui.rs); raw_defaults.open_in_camera_raw drives the Camera Raw dialog (camera_raw_ui.rs); the other raw_defaults fields drive the raw open and Camera Raw (#5128) |
| BUG-204-4 | Recent files persisted through prefs across restarts | done | [#204](https://github.com/storytold/photocraft/issues/204) | crates/ui-egui/src/lib.rs::push_recent stores the list in preferences; prefs_ui::load restores it and recent_files_survive_a_restart_and_honour_the_count verifies reload |
| BUG-204-5 | Recent-files list honours recent_file_count (default 20) | done | [#204](https://github.com/storytold/photocraft/issues/204) | lib.rs push_recent caps the list with recent_cap (recent_file_count); menus.rs covers default and custom limits |
| BUG-204-6 | Test: every Prefs field is read somewhere or on a hidden allowlist | done | [#204](https://github.com/storytold/photocraft/issues/204) | crates/engine/tests/it/prefs_usage.rs every_preference_is_read_or_hidden: each field is read outside prefs.rs or listed in prefs::HIDDEN_UNTIL_IMPLEMENTED, never both |
+1 -1
View File
@@ -67,7 +67,7 @@ id = "BUG-204-3"
target = "RAW prefs and ask_before_saving_layered_tiff read, or hidden"
status = "done"
issue = 204
note = "ask_before_saving_layered_tiff drives the TIFF Options prompt (ui-egui tiff_options_ui.rs); raw_defaults.open_in_camera_raw drives the Camera Raw dialog (camera_raw_ui.rs); the other raw_defaults fields are in prefs::HIDDEN_UNTIL_IMPLEMENTED"
note = "ask_before_saving_layered_tiff drives the TIFF Options prompt (ui-egui tiff_options_ui.rs); raw_defaults.open_in_camera_raw drives the Camera Raw dialog (camera_raw_ui.rs); the other raw_defaults fields drive the raw open and Camera Raw (#5128)"
[[item]]
id = "BUG-204-4"