Integration: one gpu test binary, no GPU pass for neutral settings (#6923)

- crates/gpu/tests/camera_raw.rs was its own test binary taking
  testkit's gpu_lock; since #8644 the crate's integration tests are one
  binary (tests/it) sharing crate::GPU_LOCK, so in that binary it would
  open a device next to the other modules. Moved into tests/it with
  GPU_LOCK; the testkit dev-dependency goes.
- Default (neutral) settings uploaded, rendered and read back the proxy
  although the CPU develop returns at once. CameraRawGpu::develop now
  leaves them to the CPU; neutral_settings_skip_the_gpu_round_trip fails
  without it (backend "gpu").
- docs/hardware-parity.md: the CPU range was one backend's (21-28 ms);
  measured 11-16 ms on an RTX 5070 Ti, so 11-28 ms, machine-dependent.

Co-authored-by: p0rps <134095787+fjamesprice@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Mann
2026-10-22 20:45:52 +02:00
parent 894351cd6d
commit 7a70220777
6 changed files with 22 additions and 6 deletions
Generated
-1
View File
@@ -4889,7 +4889,6 @@ dependencies = [
"photocraft-doc",
"photocraft-geom",
"photocraft-raster",
"photocraft-testkit",
"photocraft-vector",
"pollster 1.0.1",
"rayon",
-1
View File
@@ -32,4 +32,3 @@ pollster = "1"
photocraft-vector = { workspace = true }
# A document-tagged CMYK profile in the tile conversion test.
photocraft-cms = { workspace = true }
photocraft-testkit = { workspace = true }
@@ -19,7 +19,7 @@ struct Gpu {
}
fn gpu() -> Option<Gpu> {
let lock = photocraft_testkit::gpu_lock();
let lock = crate::GPU_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
// `WGPU_BACKEND` picks the backend; `PHOTOCRAFT_TEST_FALLBACK_ADAPTER=1` the software one (WARP).
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
let fallback = std::env::var_os("PHOTOCRAFT_TEST_FALLBACK_ADAPTER").is_some();
+1
View File
@@ -4,6 +4,7 @@
/// creates a device takes this lock and holds it until the device is dropped.
static GPU_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
mod camera_raw;
mod device_loss;
mod parity;
mod timing;
+19 -2
View File
@@ -119,9 +119,10 @@ impl CameraRawGpu {
/// Develops `px` (`w × h`) with `p`, or `None` when the CPU must: a spatial stage, the pass
/// still building, a fault, or a GPU error (logged; the dialog then stays on the CPU). `key` names the pixels when they
/// are the dialog's unchanged proxy ([`source_key`]), so they upload once.
/// are the dialog's unchanged proxy ([`source_key`]), so they upload once. Neutral settings
/// are `None` too: the CPU develop returns them at once, a GPU round trip would only cost.
pub(crate) fn develop(&self, px: &[[f32; 4]], key: Option<u64>, w: usize, h: usize, p: &CameraRaw, float: bool) -> Option<Vec<[f32; 4]>> {
if p.spatial() || self.failed.load(Ordering::Relaxed) || !self.health.is_ok() {
if p.spatial() || p.is_identity() || self.failed.load(Ordering::Relaxed) || !self.health.is_ok() {
return None;
}
#[cfg(target_arch = "wasm32")]
@@ -264,6 +265,22 @@ mod tests {
}
}
#[test]
fn neutral_settings_skip_the_gpu_round_trip() {
let _gpu = photocraft_testkit::gpu_lock();
let Some((mut app, ctx)) = gpu_app(8) else { return };
force_pass(&app);
crate::camera_raw_ui::open(&mut app, &ctx).unwrap();
if app.camera_raw.as_ref().is_some_and(|d| d.gpu.is_none()) {
eprintln!("skipping: the device can't run the Camera Raw pass");
return;
}
let (backend, _, _) = developed(&mut app, &ctx, CameraRaw { exposure: 0.5, ..Default::default() });
assert_eq!(backend, "gpu", "a per-pixel setting develops on the GPU");
let (backend, shown, cpu) = developed(&mut app, &ctx, CameraRaw::default());
assert_eq!((backend, shown == cpu), ("cpu", true), "default settings: the CPU returns them unchanged");
}
#[test]
fn rendering_mode_cpu_keeps_the_cpu_develop() {
let _gpu = photocraft_testkit::gpu_lock();
+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, 2.7 ms vs 21-28 ms on the CPU for the 858×572 proxy; 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 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 |