diff --git a/README.md b/README.md index 8133e9e6..2aea14d8 100644 --- a/README.md +++ b/README.md @@ -409,6 +409,9 @@ nix build github:storytold/lightcraft # → ./result/bin/{light nix develop github:storytold/lightcraft # rust toolchain + native deps + fonts ``` +On NixOS, run a `cargo` build from `nix develop`: outside it the app can't find `libvulkan.so.1`, finds no GPU and +renders on the CPU, much more slowly (it says so once at start-up). `nix run` and `nix build` set the path themselves. + In a flake configuration (NixOS, home-manager, nix-darwin): ```nix diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index 08937461..e308ba74 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -857,6 +857,7 @@ fn main() -> eframe::Result { app.notices.extend(prefs_warning); app.notices.extend(launch_notices); app.notices.extend(window_notice); + app.gpu_notice = true; // what's on disk now: only changes are written let writer = PrefsWriter { path: if in_memory { None } else { prefs_path() }, diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json index 7f6fe61e..a7101e61 100644 --- a/crates/ui-egui/locales/de.json +++ b/crates/ui-egui/locales/de.json @@ -2111,5 +2111,7 @@ "Adding to Apple Photos…": "Wird zu Apple Fotos hinzugefügt…", "not added to Apple Photos (cancelled)": "nicht zu Apple Fotos hinzugefügt (abgebrochen)", "The first time, macOS asks whether LightCraft may control Photos.": "Beim ersten Mal fragt macOS, ob LightCraft Fotos steuern darf.", - "Adding to Apple Photos": "Wird zu Apple Fotos hinzugefügt" + "Adding to Apple Photos": "Wird zu Apple Fotos hinzugefügt", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "Kein Grafikprozessor gefunden: LightCraft rendert auf der CPU, was viel langsamer ist. Details unter Hilfe → Systeminformationen.", + "On NixOS, start it from `nix develop`.": "Unter NixOS starten Sie es aus `nix develop`." } diff --git a/crates/ui-egui/locales/es.json b/crates/ui-egui/locales/es.json index e3090b6a..d564559f 100644 --- a/crates/ui-egui/locales/es.json +++ b/crates/ui-egui/locales/es.json @@ -1988,5 +1988,7 @@ "Adding to Apple Photos…": "Añadiendo a Fotos de Apple…", "not added to Apple Photos (cancelled)": "no se añadieron a Fotos de Apple (cancelado)", "The first time, macOS asks whether LightCraft may control Photos.": "La primera vez, macOS pregunta si LightCraft puede controlar Fotos.", - "Adding to Apple Photos": "Añadiendo a Fotos de Apple" + "Adding to Apple Photos": "Añadiendo a Fotos de Apple", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "No se encontró ningún procesador gráfico: LightCraft renderiza en la CPU, lo que es mucho más lento. Detalles en Ayuda → Información del sistema.", + "On NixOS, start it from `nix develop`.": "En NixOS, inícielo desde `nix develop`." } diff --git a/crates/ui-egui/locales/fr.json b/crates/ui-egui/locales/fr.json index 23ed6119..f872d5e6 100644 --- a/crates/ui-egui/locales/fr.json +++ b/crates/ui-egui/locales/fr.json @@ -1952,5 +1952,7 @@ "Adding to Apple Photos…": "Ajout à Apple Photos…", "not added to Apple Photos (cancelled)": "non ajouté à Apple Photos (annulé)", "The first time, macOS asks whether LightCraft may control Photos.": "La première fois, macOS demande si LightCraft peut contrôler Photos.", - "Adding to Apple Photos": "Ajout à Apple Photos" + "Adding to Apple Photos": "Ajout à Apple Photos", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "Aucun processeur graphique trouvé : LightCraft effectue le rendu sur le processeur, ce qui est beaucoup plus lent. Détails dans Aide → Informations système.", + "On NixOS, start it from `nix develop`.": "Sous NixOS, lancez-le depuis `nix develop`." } diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index a2e5993b..203211a6 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -1954,5 +1954,7 @@ "Adding to Apple Photos…": "Appleの写真に追加中…", "not added to Apple Photos (cancelled)": "Appleの写真には追加されませんでした(キャンセル)", "The first time, macOS asks whether LightCraft may control Photos.": "初回は、LightCraftに写真の操作を許可するかmacOSが確認します。", - "Adding to Apple Photos": "Appleの写真に追加中" + "Adding to Apple Photos": "Appleの写真に追加中", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "グラフィックプロセッサが見つかりません。LightCraftはCPUで描画するため、かなり遅くなります。詳細は「ヘルプ → システム情報」を参照してください。", + "On NixOS, start it from `nix develop`.": "NixOSでは `nix develop` から起動してください。" } diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 80bf5a49..900ea9e4 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -1991,5 +1991,7 @@ "Choose a model below (Use) to start.": "Escolha um modelo abaixo (Usar) para começar.", "Face detector": "Detector de rostos", "Detect Faces (Photo menu) needs it: a small one-time download.": "Detectar rostos (menu Foto) precisa dele: um pequeno download único.", - "Recognition models": "Modelos de reconhecimento" + "Recognition models": "Modelos de reconhecimento", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "Nenhum processador gráfico encontrado: o LightCraft renderiza na CPU, o que é muito mais lento. Detalhes em Ajuda → Informações do sistema.", + "On NixOS, start it from `nix develop`.": "No NixOS, inicie-o a partir de `nix develop`." } diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 91db0770..6886f621 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -2144,5 +2144,7 @@ "Adding to Apple Photos…": "Добавление в Apple Фото…", "not added to Apple Photos (cancelled)": "не добавлено в Apple Фото (отменено)", "The first time, macOS asks whether LightCraft may control Photos.": "В первый раз macOS спросит, может ли LightCraft управлять приложением «Фото».", - "Adding to Apple Photos": "Добавление в Apple Фото" + "Adding to Apple Photos": "Добавление в Apple Фото", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "Графический процессор не найден: LightCraft выполняет рендеринг на CPU, а это намного медленнее. Подробности: Справка → Сведения о системе.", + "On NixOS, start it from `nix develop`.": "В NixOS запускайте его из `nix develop`." } diff --git a/crates/ui-egui/locales/uk.json b/crates/ui-egui/locales/uk.json index c0d174c0..d640ae33 100644 --- a/crates/ui-egui/locales/uk.json +++ b/crates/ui-egui/locales/uk.json @@ -2146,5 +2146,7 @@ "Adding to Apple Photos…": "Додавання до Apple Фото…", "not added to Apple Photos (cancelled)": "не додано до Apple Фото (скасовано)", "The first time, macOS asks whether LightCraft may control Photos.": "Першого разу macOS запитає, чи може LightCraft керувати програмою «Фото».", - "Adding to Apple Photos": "Додавання до Apple Фото" + "Adding to Apple Photos": "Додавання до Apple Фото", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "Графічний процесор не знайдено: LightCraft виконує рендеринг на ЦП, а це набагато повільніше. Докладніше: Довідка → Інформація про систему.", + "On NixOS, start it from `nix develop`.": "У NixOS запускайте його з `nix develop`." } diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 55ea8a0b..26173155 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -1990,5 +1990,7 @@ "Adding to Apple Photos…": "正在添加到 Apple 照片…", "not added to Apple Photos (cancelled)": "未添加到 Apple 照片(已取消)", "The first time, macOS asks whether LightCraft may control Photos.": "首次使用时,macOS 会询问是否允许 LightCraft 控制“照片”。", - "Adding to Apple Photos": "正在添加到 Apple 照片" + "Adding to Apple Photos": "正在添加到 Apple 照片", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "未找到图形处理器:LightCraft 改用 CPU 渲染,速度会慢很多。详情见“帮助 → 系统信息”。", + "On NixOS, start it from `nix develop`.": "在 NixOS 上,请从 `nix develop` 启动。" } diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 5c6eb681..abeb97d4 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -1952,5 +1952,7 @@ "Adding to Apple Photos…": "正在加入 Apple 照片…", "not added to Apple Photos (cancelled)": "未加入 Apple 照片(已取消)", "The first time, macOS asks whether LightCraft may control Photos.": "第一次使用時,macOS 會詢問是否允許 LightCraft 控制「照片」。", - "Adding to Apple Photos": "正在加入 Apple 照片" + "Adding to Apple Photos": "正在加入 Apple 照片", + "No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.": "找不到圖形處理器:LightCraft 改用 CPU 算繪,速度會慢很多。詳情請見「說明 → 系統資訊」。", + "On NixOS, start it from `nix develop`.": "在 NixOS 上,請從 `nix develop` 啟動。" } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index f0ce4ca5..9df64f71 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -381,6 +381,9 @@ pub struct LightcraftApp { pub window_is_fullscreen: bool, /// The GPU preference last applied (`app.gpu`), to apply Settings changes once. gpu_applied: Option, + /// Say once, when the GPU device has been tried, that no graphics processor was found + /// ([`panels::notices::gpu_notice`]). The desktop app sets it; headless sessions and tests don't. + pub gpu_notice: bool, /// The memory budget setting last applied (MB, 0 = automatic). memory_applied: Option, /// The display profile setting last applied (`app.displayProfile`). @@ -463,6 +466,7 @@ impl LightcraftApp { hover_preview: None, window_is_fullscreen: false, gpu_applied: None, + gpu_notice: false, memory_applied: None, display_applied: None, display_error: None, diff --git a/crates/ui-egui/src/panels/notices.rs b/crates/ui-egui/src/panels/notices.rs index 619efe1d..016323e9 100644 --- a/crates/ui-egui/src/panels/notices.rs +++ b/crates/ui-egui/src/panels/notices.rs @@ -8,10 +8,36 @@ use crate::theme::Tokens; use crate::widgets::register; use crate::{LightcraftApp, QuitPrompt}; -/// Collect the library's settings-file warnings (each is shown once). +/// Collect the library's settings-file warnings (each is shown once), and the no-GPU notice once the GPU device has +/// been tried. pub fn logic(app: &mut LightcraftApp) { let new = app.session.take_library_warnings(); app.notices.extend(new); + use lightcraft_engine::gpu; + if app.gpu_notice && app.gpu_applied == Some(true) && gpu::ready() { + app.gpu_notice = false; + if !gpu::available() { + app.notices.extend(gpu_notice(gpu::unavailable_reason().as_deref(), app.ui.settings.gpu)); + } + } +} + +/// What to tell the user when GPU rendering is on (`enabled`) but the GPU could not be used for `reason`: only when no +/// graphics adapter was found at all ("no GPU adapter among …", e.g. no Vulkan loader on Linux), which makes every +/// render much slower without saying so. Nothing when it was turned off (preference, `LIGHTCRAFT_GPU`), a software +/// adapter was skipped, or it failed later; Settings ▸ Performance and Help ▸ System Info show those. +pub fn gpu_notice(reason: Option<&str>, enabled: bool) -> Option { + if !enabled || !reason?.starts_with("no GPU adapter") { + return None; + } + let mut text = + crate::i18n::tr("No graphics processor found: LightCraft renders on the CPU, which is much slower. Details in Help → System Info.") + .to_string(); + if cfg!(target_os = "linux") { + text.push(' '); + text.push_str(crate::i18n::tr("On NixOS, start it from `nix develop`.")); + } + Some(text) } /// The window may close now (`true`), or the quit prompt is shown (`false`). Running tasks that @@ -143,3 +169,27 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } } } + +#[cfg(test)] +mod tests { + use super::gpu_notice; + + #[test] + fn the_gpu_notice_is_only_for_a_missing_adapter() { + let none = "no GPU adapter among Backends(VULKAN | GL) (No suitable graphics adapter found)"; + let text = gpu_notice(Some(none), true).expect("no adapter: say so"); + assert!(text.starts_with("No graphics processor found") && text.contains("System Info"), "{text}"); + assert_eq!(text.contains("nix develop"), cfg!(target_os = "linux"), "{text}"); + assert_eq!(gpu_notice(Some(none), false), None, "the preference is off"); + for reason in [ + "disabled by LIGHTCRAFT_GPU=0", + "disabled by LIGHTCRAFT_GPU_BACKEND=off", + "disabled by the GPU rendering preference (app.gpu)", + "software adapter (llvmpipe (LLVM 19.1.7, 256 bits)) skipped: the CPU pipeline is faster", + "stopped after a GPU failure: device lost", + ] { + assert_eq!(gpu_notice(Some(reason), true), None, "{reason}"); + } + assert_eq!(gpu_notice(None, true), None, "the GPU is in use"); + } +} diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index 02568703..f19eff3d 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -471,7 +471,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts). | LR-BEHAV-BG-TASKS | Background tasks | P0 | ✅ | `cmd:activity.list`, `cmd:activity.cancel`, `crates/engine/src/activity.rs`, `crates/ui-egui/src/panels/activity.rs`, `docs/background-tasks.md`, `crates/preview/src/lib.rs` (`JobPool`) | renders off the UI thread; exports run several photos of a batch side by side (`export_parallelism`, memory-gated; files, names and order as one at a time, #496); one activity stack in the top-left corner under the top bar, as in Lightroom Classic (issue #345): imports, folder scans, exports (and the contact sheet PDF), Synchronize Folder (its scan and its run), Build / Smart Previews, Lightroom catalog inspect / import, Photo Merge, model downloads (SAM 3, face, AI Denoise), the face scan, AI Denoise and Find Missing Photos each show a row (name, progress bar or a sweep while the amount isn't known, count and current item) once 0.5 s old, three rows then "+N more"; ✕ cancels the jobs that can stop ("Stopping…" until they do); quitting asks while a cancellable task runs; agents and `ui.inspect` (`activity`) see the same list. No pause (Lightroom can pause some tasks) | | LR-BEHAV-OFFLINE | Offline editing | P1 | ✅ | | local-first: everything works offline | | LR-BEHAV-WEB-SAFETY | Library safety in the browser build | P1 | 🟡 | `cmd:file.backupLibrary`, `cmd:file.restoreLibrary`, `apps/lightcraft-web/src/safety.rs`, `apps/lightcraft-web/src/backup.rs`, `apps/lightcraft-web/src/files.rs` | web only (experimental): Back Up Library (zip of the catalog and every stored original) and a non-destructive Restore; failed saves show as unsaved and are retried; a photo that can't be stored isn't added; one tab per library (Web Locks); notices for storage that isn't persistent and for a library that can't open; `?reset` asks first; a panic shows a message. Missing: `?safe` start, deleting originals of removed photos, zip64 (backups > 4 GB) | -| LR-BEHAV-GPU | GPU acceleration | P0 | ✅ | `crates/gpu/src/render.rs`, `cmd:app.gpu`, `docs/gpu-pipeline.md` | CPU fallback per render on device limits, out of memory, device errors / loss, incomplete or black results (`perf.gpuReason`, `perf.gpuFallback`; each render reports its own reason — `lightcraft_gpu::try_render`, `gpuFallback` per exported file — issue #675); short submissions for slow iGPUs; backend choice (DX12 only on Windows, `LIGHTCRAFT_GPU_BACKEND` / `WGPU_BACKEND`), device created after the window opens, crash sentinel (issue #136); quitting waits (up to 2 s) for renders in flight and starts no new GPU work, so none is inside the driver when the process exits (`crates/gpu/src/exit.rs`, issue #620; seen crashing only on Linux + NVIDIA, other drivers unverified); no tiling beyond the buffer limit (CPU) | +| LR-BEHAV-GPU | GPU acceleration | P0 | ✅ | `crates/gpu/src/render.rs`, `cmd:app.gpu`, `docs/gpu-pipeline.md`, `crates/ui-egui/src/panels/notices.rs` | CPU fallback per render on device limits, out of memory, device errors / loss, incomplete or black results (`perf.gpuReason`, `perf.gpuFallback`; each render reports its own reason — `lightcraft_gpu::try_render`, `gpuFallback` per exported file — issue #675); short submissions for slow iGPUs; backend choice (DX12 only on Windows, `LIGHTCRAFT_GPU_BACKEND` / `WGPU_BACKEND`), device created after the window opens, crash sentinel (issue #136); quitting waits (up to 2 s) for renders in flight and starts no new GPU work, so none is inside the driver when the process exits (`crates/gpu/src/exit.rs`, issue #620; seen crashing only on Linux + NVIDIA, other drivers unverified); with GPU rendering on and no adapter found at all (e.g. no Vulkan loader on NixOS outside `nix develop`), the desktop app says once per session that it renders on the CPU and points to Help ▸ System Info (`gpu_notice`; not when the GPU is turned off or only a software adapter exists); no tiling beyond the buffer limit (CPU) | | LR-BEHAV-RENDER-FIDELITY | Rendering matches Lightroom | P1 | 🟡 | `crates/pipeline/src/lib.rs`, `crates/pipeline/src/tone.rs` | every slider exists and works, but the character of the result (tone curve shape, highlight recovery, texture / clarity / dehaze, noise reduction, sharpening, default look) is tuned by eye; there is no systematic side-by-side comparison against Lightroom on the same CC0 raws. Needs a fidelity suite: Lightroom reference renders kept only in the local `plan/` (never committed), compared per slider and preset with a perceptual metric. Tuning that changes how existing settings render ships as a new process version (LRC-DEV-PROCESS, `docs/process-versions.md`) | | LR-BEHAV-DRAGDROP | Drag and drop | P1 | ✅ | `crates/ui-egui/src/lib.rs`, `crates/ui-egui/src/panels/grid.rs` (`drag_feedback`), `crates/ui-egui/src/panels/left.rs` (`drop_target`, `album_drag_over`, `top_level_drop_target`, `drag_auto_scroll`) | files → app (import); grid photos → an album row (adds the selection, with a count badge while dragging); sidebar album / folder rows → a folder (moves it; its name follows the pointer); a drag of albums or grid photos near the top or bottom edge of the sidebar scrolls it (faster nearer the edge, only while the pointer is over the sidebar) | | LR-BEHAV-TOAST | Toast notifications | P1 | ✅ | `crates/ui-egui/src/panels/mod.rs` | | diff --git a/docs/whats-new.md b/docs/whats-new.md index aeca5de4..89935b75 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -54,6 +54,10 @@ ### Older raw cameras - Olympus E-10 raws open with the camera's white balance (White Balance ▸ As Shot) and without the grey haze in the shadows: the black level recorded in the file is now subtracted. +### Told when there is no GPU +- When GPU rendering is on but no graphics processor can be found, LightCraft now says so once at start-up instead + of quietly rendering everything on the CPU, much more slowly. Help ▸ System Info says why. On NixOS this happens + when a `cargo` build runs outside `nix develop` (no Vulkan loader); start it from there. ## 2026-10-11