mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-19 02:10:25 -05:00
LR-BEHAV-GPU: Turn GPU rendering off after the GPU is lost while running (#1337)
Report it as LightCraft stopping instead of the window failing to open, and start on the CPU next time.
This commit is contained in:
1 parent
a4b53b28cc
commit
b0b86aca84
23 files changed
+204
-25
No files matched your search
+129
-1
@@ -162,6 +162,46 @@ fn gpu_crash_notice(what: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// Written when the window stopped drawing after the GPU device was lost (issue #1337): the next
|
||||
/// launch renders on the CPU and says why.
|
||||
fn gpu_lost_marker_path() -> Option<std::path::PathBuf> {
|
||||
gpu_marker_path().map(|p| p.with_file_name("gpu-lost.marker"))
|
||||
}
|
||||
|
||||
/// Why the GPU was lost, when the GPU pipeline stopped after a device failure in this process (a
|
||||
/// driver reset, a hang, the Windows watchdog): `None` for other reasons it is off, or when it
|
||||
/// never failed.
|
||||
fn gpu_lost_reason() -> Option<String> {
|
||||
lightcraft_engine::gpu::unavailable_reason().and_then(|r| r.strip_prefix("stopped after a GPU failure: ").map(str::to_string))
|
||||
}
|
||||
|
||||
/// Record that LightCraft stopped after the GPU was lost, for the next launch.
|
||||
fn record_gpu_lost(marker: Option<&std::path::Path>, reason: &str) {
|
||||
let Some(m) = marker else { return };
|
||||
if let Some(d) = m.parent() {
|
||||
let _ = std::fs::create_dir_all(d);
|
||||
}
|
||||
if let Err(e) = std::fs::write(m, format!("{reason}\n")) {
|
||||
log::warn!("could not write {}: {e}", m.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPU loss the last launch stopped on: its notice, once (the marker is removed, except by a
|
||||
/// `--memory` session, which leaves the disk as it found it).
|
||||
fn gpu_lost_check_at(marker: Option<&std::path::Path>, in_memory: bool) -> Option<String> {
|
||||
let m = marker?;
|
||||
let reason = std::fs::read_to_string(m).ok()?;
|
||||
if !in_memory {
|
||||
let _ = std::fs::remove_file(m);
|
||||
}
|
||||
Some(format!(
|
||||
"LightCraft stopped last time after the graphics driver reset the GPU ({}) while photos rendered on it, so GPU rendering \
|
||||
is now off and photos render on the CPU. To try the GPU again, turn on Settings ▸ Performance ▸ Use the GPU for rendering; \
|
||||
an up-to-date graphics driver may help.",
|
||||
reason.trim()
|
||||
))
|
||||
}
|
||||
|
||||
/// Where the system keeps monitor profiles, for the Choose Profile dialog to start in: the user's
|
||||
/// own profiles first (Linux: colord / DisplayCAL; macOS: ColorSync), then the system's.
|
||||
fn display_profile_dir() -> Option<std::path::PathBuf> {
|
||||
@@ -808,7 +848,7 @@ fn main() -> eframe::Result {
|
||||
let library_dir = library_dir.or_else(lightcraft_engine::library::default_dir);
|
||||
// GPU compute: off if the preference says so, or if the last launch died creating the device
|
||||
// (issue #136) — before anything can create it
|
||||
let gpu_crash = gpu_crash_check(in_memory);
|
||||
let gpu_crash = gpu_crash_check(in_memory).or_else(|| gpu_lost_check_at(gpu_lost_marker_path().as_deref(), in_memory));
|
||||
let gpu_on = prefs.as_ref().is_none_or(|u| u.settings.gpu) && gpu_crash.is_none();
|
||||
lightcraft_engine::gpu::set_enabled(gpu_on);
|
||||
// the window's renderer: probed (Windows) and with a fallback chain, so a driver that can't
|
||||
@@ -817,6 +857,9 @@ fn main() -> eframe::Result {
|
||||
let window_gpu::Chosen { renderer, wgpu_options, which: window_renderer, notice: window_notice } = window_gpu::choose(&win_files);
|
||||
win_files.arm(window_renderer);
|
||||
let win_files_app = win_files.clone();
|
||||
// the window came up: a failure after this is LightCraft stopping, not a start that failed
|
||||
let came_up = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let came_up_app = came_up.clone();
|
||||
let options = eframe::NativeOptions {
|
||||
renderer,
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
@@ -854,6 +897,7 @@ fn main() -> eframe::Result {
|
||||
Box::new(move |cc| {
|
||||
// the window's renderer is up (eframe calls this after creating it)
|
||||
win_files_app.started(window_renderer);
|
||||
came_up_app.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let (mut session, problem) = open_session(in_memory, library_dir, seed_demo && files.is_empty());
|
||||
// AI masks: the SAM 3 checkpoint (facebook/sam3) in <config>/models/sam3, or LIGHTCRAFT_SAM3_DIR
|
||||
// (never required: without it, AI masks offer to download it; see docs/ai-masks.md)
|
||||
@@ -915,6 +959,31 @@ fn main() -> eframe::Result {
|
||||
}),
|
||||
)
|
||||
}));
|
||||
if came_up.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
// the window was up: a panic that escaped it now is LightCraft stopping, typically the
|
||||
// window's renderer after the graphics driver reset the GPU (issue #1337: egui-wgpu panics
|
||||
// on the lost device). Not a failed start: no other renderer is tried, and the message
|
||||
// says what happened.
|
||||
let stopped = match started {
|
||||
Ok(r) => r,
|
||||
Err(_) => Err(eframe::Error::AppCreation("its window stopped drawing (the renderer panicked)".into())),
|
||||
};
|
||||
if let Err(e) = &stopped {
|
||||
let lost = gpu_lost_reason();
|
||||
if let Some(reason) = &lost
|
||||
&& !in_memory
|
||||
{
|
||||
record_gpu_lost(gpu_lost_marker_path().as_deref(), reason);
|
||||
}
|
||||
// (not "app creation error: …": the app had been created)
|
||||
let what = match e {
|
||||
eframe::Error::AppCreation(m) => m.to_string(),
|
||||
e => e.to_string(),
|
||||
};
|
||||
stopped_while_running(&stopped_text(&what, lost.as_deref()), log_file.as_deref());
|
||||
}
|
||||
return stopped;
|
||||
}
|
||||
// a panic that still escaped the renderer's start (the probe and error handler should
|
||||
// prevent it): an error like any other failed start
|
||||
let started = started.unwrap_or_else(|_| Err(eframe::Error::AppCreation("the window's renderer panicked while starting".into())));
|
||||
@@ -929,6 +998,34 @@ fn main() -> eframe::Result {
|
||||
started
|
||||
}
|
||||
|
||||
/// What to tell the user when LightCraft stopped while running (`error`), and why when the GPU
|
||||
/// was lost (`lost`): GPU rendering is off from the next start.
|
||||
fn stopped_text(error: &str, lost: Option<&str>) -> String {
|
||||
match lost {
|
||||
Some(reason) => format!(
|
||||
"{error}. The graphics driver reset the GPU ({reason}). LightCraft renders on the CPU from its next start; \
|
||||
Settings ▸ Performance ▸ Use the GPU for rendering turns the GPU back on"
|
||||
),
|
||||
None => error.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// LightCraft stopped while its window was up: say so in the log and in a native message box
|
||||
/// naming the log file (as [`startup_failed`] does for a start that failed).
|
||||
fn stopped_while_running(text: &str, log_file: Option<&std::path::Path>) {
|
||||
log::error!("LightCraft stopped: {text}");
|
||||
if std::env::var_os("LIGHTCRAFT_NO_PREFS").is_some() {
|
||||
return; // tests and scripts: no dialog to click away
|
||||
}
|
||||
let (title, text) = lightcraft_ui_egui::i18n::stopped_message(text, log_file.map(|p| p.display().to_string()).as_deref());
|
||||
let _ = rfd::MessageDialog::new()
|
||||
.set_level(rfd::MessageLevel::Error)
|
||||
.set_title(title)
|
||||
.set_description(text)
|
||||
.set_buttons(rfd::MessageButtons::Ok)
|
||||
.show();
|
||||
}
|
||||
|
||||
/// The window could not start (e.g. no usable graphics device): say so in the log and in a native
|
||||
/// message box naming the log file, instead of exiting without a trace — the release builds have
|
||||
/// no console on Windows, and an app started from Finder shows no standard error (issue #260).
|
||||
@@ -1131,6 +1228,37 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
}
|
||||
|
||||
/// Issue #1337: the window stopped drawing after the graphics driver reset the GPU during an
|
||||
/// export, and the dialog said the window "could not open". The stop is recorded; the next
|
||||
/// launch says why once and renders on the CPU (a `--memory` session leaves the record).
|
||||
#[test]
|
||||
fn a_gpu_lost_while_running_turns_the_gpu_off_next_time() {
|
||||
let d = dir("gpu-lost");
|
||||
let m = d.join("gpu-lost.marker");
|
||||
assert_eq!(gpu_lost_check_at(Some(&m), false), None, "nothing recorded");
|
||||
assert_eq!(gpu_lost_check_at(None, false), None, "no settings folder");
|
||||
record_gpu_lost(Some(&m), "device lost (Unknown): Device is lost");
|
||||
let notice = gpu_lost_check_at(Some(&m), true).unwrap();
|
||||
assert!(
|
||||
notice.contains("(device lost (Unknown): Device is lost)")
|
||||
&& notice.contains("GPU rendering is now off")
|
||||
&& notice.contains("Use the GPU for rendering"),
|
||||
"{notice}"
|
||||
);
|
||||
assert!(m.exists(), "a --memory session leaves it");
|
||||
assert!(gpu_lost_check_at(Some(&m), false).is_some());
|
||||
assert!(!m.exists());
|
||||
assert_eq!(gpu_lost_check_at(Some(&m), false), None, "said once");
|
||||
// the message box: LightCraft stopped (not "could not open its window"), and why
|
||||
let text = stopped_text("its window stopped drawing (the renderer panicked)", Some("device lost (Unknown): Device is lost"));
|
||||
assert!(text.contains("reset the GPU (device lost (Unknown)") && text.contains("renders on the CPU from its next start"), "{text}");
|
||||
assert_eq!(stopped_text("its window stopped drawing", None), "its window stopped drawing");
|
||||
let (title, body) = lightcraft_ui_egui::i18n::stopped_message(&text, Some("/logs/lightcraft.log"));
|
||||
assert_eq!(title, "LightCraft stopped");
|
||||
assert!(body.starts_with("LightCraft stopped unexpectedly: its window stopped drawing") && body.ends_with("/logs/lightcraft.log"), "{body}");
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
}
|
||||
|
||||
/// Issue #136: a GPU init marker left by a crashed launch is reported once and removed.
|
||||
#[test]
|
||||
fn gpu_crash_marker_is_reported_once() {
|
||||
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "{n} Fotos zur Bearbeitung geöffnet; jedes ist mit seinem Original gestapelt",
|
||||
"{name} opened in the other editor": "{name} im anderen Editor geöffnet",
|
||||
"{n} originals opened in the other editor": "{n} Originale im anderen Editor geöffnet",
|
||||
"· {in_source} identical copies": "· {in_source} identische Kopien"
|
||||
"· {in_source} identical copies": "· {in_source} identische Kopien",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft wurde unerwartet beendet: {e}\n\nEinzelheiten stehen in der Protokolldatei: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft wurde unerwartet beendet: {e}"
|
||||
}
|
||||
@@ -2118,5 +2118,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Dateien mit demselben Inhalt wie eine andere Datei dieses Imports: Nur die erste wird importiert."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Dateien mit demselben Inhalt wie eine andere Datei dieses Imports: Nur die erste wird importiert.",
|
||||
"LightCraft stopped": "LightCraft wurde beendet"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "{n} fotos abiertas para editar; cada una está apilada con su original",
|
||||
"{name} opened in the other editor": "{name} abierto en el otro editor",
|
||||
"{n} originals opened in the other editor": "{n} originales abiertos en el otro editor",
|
||||
"· {in_source} identical copies": "· {in_source} copias idénticas"
|
||||
"· {in_source} identical copies": "· {in_source} copias idénticas",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft se detuvo inesperadamente: {e}\n\nLos detalles están en el archivo de registro: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft se detuvo inesperadamente: {e}"
|
||||
}
|
||||
@@ -1995,5 +1995,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Archivos con el mismo contenido que otro archivo de esta importación: solo se importa el primero."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Archivos con el mismo contenido que otro archivo de esta importación: solo se importa el primero.",
|
||||
"LightCraft stopped": "LightCraft se detuvo"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "{n} photos ouvertes pour modification ; chacune est empilée avec son original",
|
||||
"{name} opened in the other editor": "{name} ouvert dans l’autre éditeur",
|
||||
"{n} originals opened in the other editor": "{n} originaux ouverts dans l’autre éditeur",
|
||||
"· {in_source} identical copies": "· {in_source} copies identiques"
|
||||
"· {in_source} identical copies": "· {in_source} copies identiques",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft s’est arrêté de façon inattendue : {e}\n\nConsultez le fichier journal pour plus de détails : {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft s’est arrêté de façon inattendue : {e}"
|
||||
}
|
||||
@@ -1957,5 +1957,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Fichiers au contenu identique à un autre fichier de cette importation : seul le premier est importé."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Fichiers au contenu identique à un autre fichier de cette importation : seul le premier est importé.",
|
||||
"LightCraft stopped": "LightCraft s’est arrêté"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "{n}枚の写真を編集用に開きました。それぞれ元の写真とスタックされています",
|
||||
"{name} opened in the other editor": "{name}を別のエディタで開きました",
|
||||
"{n} originals opened in the other editor": "{n}個の元ファイルを別のエディタで開きました",
|
||||
"· {in_source} identical copies": "・同一のコピー {in_source} 件"
|
||||
"· {in_source} identical copies": "・同一のコピー {in_source} 件",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraftが予期せず終了しました: {e}\n\n詳しくはログファイルをご覧ください: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraftが予期せず終了しました: {e}"
|
||||
}
|
||||
@@ -1961,5 +1961,6 @@
|
||||
"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` から起動してください。",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "この読み込み内の別のファイルと内容が同じファイルです。最初のファイルだけが読み込まれます。"
|
||||
"Files with the same content as another file in this import: only the first is imported.": "この読み込み内の別のファイルと内容が同じファイルです。最初のファイルだけが読み込まれます。",
|
||||
"LightCraft stopped": "LightCraftが終了しました"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "{n} fotos abertas para edição; cada uma está empilhada com a original",
|
||||
"{name} opened in the other editor": "{name} aberto no outro editor",
|
||||
"{n} originals opened in the other editor": "{n} originais abertos no outro editor",
|
||||
"· {in_source} identical copies": "· {in_source} cópias idênticas"
|
||||
"· {in_source} identical copies": "· {in_source} cópias idênticas",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "O LightCraft parou inesperadamente: {e}\n\nO arquivo de log tem os detalhes: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "O LightCraft parou inesperadamente: {e}"
|
||||
}
|
||||
@@ -1998,5 +1998,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Arquivos com o mesmo conteúdo de outro arquivo desta importação: apenas o primeiro é importado."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Arquivos com o mesmo conteúdo de outro arquivo desta importação: apenas o primeiro é importado.",
|
||||
"LightCraft stopped": "O LightCraft parou"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "Открыто для редактирования фото: {n}; каждое объединено в стопку с оригиналом",
|
||||
"{name} opened in the other editor": "{name} открыт в другом редакторе",
|
||||
"{n} originals opened in the other editor": "Открыто оригиналов в другом редакторе: {n}",
|
||||
"· {in_source} identical copies": "· идентичных копий: {in_source}"
|
||||
"· {in_source} identical copies": "· идентичных копий: {in_source}",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft неожиданно остановился: {e}\n\nПодробности в файле журнала: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft неожиданно остановился: {e}"
|
||||
}
|
||||
@@ -2151,5 +2151,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Файлы с тем же содержимым, что и другой файл этого импорта: импортируется только первый."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Файлы с тем же содержимым, что и другой файл этого импорта: импортируется только первый.",
|
||||
"LightCraft stopped": "LightCraft остановился"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "Відкрито для редагування фото: {n}; кожне об’єднано в стос з оригіналом",
|
||||
"{name} opened in the other editor": "{name} відкрито в іншому редакторі",
|
||||
"{n} originals opened in the other editor": "Відкрито оригіналів в іншому редакторі: {n}",
|
||||
"· {in_source} identical copies": "· ідентичних копій: {in_source}"
|
||||
"· {in_source} identical copies": "· ідентичних копій: {in_source}",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft несподівано зупинився: {e}\n\nПодробиці у файлі журналу: {path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft несподівано зупинився: {e}"
|
||||
}
|
||||
@@ -2151,5 +2151,6 @@
|
||||
"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`.",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Файли з тим самим вмістом, що й інший файл цього імпорту: імпортується лише перший."
|
||||
"Files with the same content as another file in this import: only the first is imported.": "Файли з тим самим вмістом, що й інший файл цього імпорту: імпортується лише перший.",
|
||||
"LightCraft stopped": "LightCraft зупинився"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "已打开 {n} 张照片进行编辑;每张都与其原片堆叠在一起",
|
||||
"{name} opened in the other editor": "已在其他编辑器中打开 {name}",
|
||||
"{n} originals opened in the other editor": "已在其他编辑器中打开 {n} 个原始文件",
|
||||
"· {in_source} identical copies": "· {in_source} 个相同副本"
|
||||
"· {in_source} identical copies": "· {in_source} 个相同副本",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft 意外停止:{e}\n\n详细信息见日志文件:{path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft 意外停止:{e}"
|
||||
}
|
||||
@@ -1997,5 +1997,6 @@
|
||||
"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` 启动。",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "与本次导入中另一个文件内容相同的文件:只导入第一个。"
|
||||
"Files with the same content as another file in this import: only the first is imported.": "与本次导入中另一个文件内容相同的文件:只导入第一个。",
|
||||
"LightCraft stopped": "LightCraft 已停止"
|
||||
}
|
||||
@@ -247,5 +247,7 @@
|
||||
"{n} photos opened for editing; each is stacked with its original": "已開啟 {n} 張相片進行編輯;每張都與其原始相片堆疊在一起",
|
||||
"{name} opened in the other editor": "已在其他編輯器中開啟 {name}",
|
||||
"{n} originals opened in the other editor": "已在其他編輯器中開啟 {n} 個原始檔案",
|
||||
"· {in_source} identical copies": "· {in_source} 個相同副本"
|
||||
"· {in_source} identical copies": "· {in_source} 個相同副本",
|
||||
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft 意外停止:{e}\n\n詳細資訊請見記錄檔:{path}",
|
||||
"LightCraft stopped unexpectedly: {e}": "LightCraft 意外停止:{e}"
|
||||
}
|
||||
@@ -1959,5 +1959,6 @@
|
||||
"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` 啟動。",
|
||||
"Files with the same content as another file in this import: only the first is imported.": "與本次匯入中另一個檔案內容相同的檔案:只匯入第一個。"
|
||||
"Files with the same content as another file in this import: only the first is imported.": "與本次匯入中另一個檔案內容相同的檔案:只匯入第一個。",
|
||||
"LightCraft stopped": "LightCraft 已停止"
|
||||
}
|
||||
@@ -212,6 +212,17 @@ pub fn startup_failed_message(error: &str, log_file: Option<&str>) -> (String, S
|
||||
(tr("LightCraft could not start").to_string(), text)
|
||||
}
|
||||
|
||||
/// The title and text of the message box shown when LightCraft stopped while its window was up
|
||||
/// (issue #1337: the window's renderer after the graphics driver reset the GPU), in the UI
|
||||
/// language: what happened and, when there is one, the log file to look in.
|
||||
pub fn stopped_message(error: &str, log_file: Option<&str>) -> (String, String) {
|
||||
let text = match log_file {
|
||||
Some(path) => tr_format!("LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}", e = error, path = path),
|
||||
None => tr_format!("LightCraft stopped unexpectedly: {e}", e = error),
|
||||
};
|
||||
(tr("LightCraft stopped").to_string(), text)
|
||||
}
|
||||
|
||||
pub fn builtin_label(source: &str, builtin: bool) -> &str {
|
||||
if builtin { tr(source) } else { source }
|
||||
}
|
||||
|
||||
@@ -105,6 +105,14 @@ rendering; `set VK_LOADER_DRIVERS_DISABLE=*igvk64*` (Vulkan loader) hides a spec
|
||||
from every program started with it. Help ▸ System Info and `app.gpu` show the adapter and backend in
|
||||
use (e.g. `Intel(R) UHD Graphics 630 (Dx12)`).
|
||||
|
||||
**LightCraft stops during an export or a render; the log says "device lost" (Windows, issue #1337).**
|
||||
When the graphics driver resets the GPU (the Windows watchdog, a driver hang: DXGI `0x887A0005`, "GPU
|
||||
device instance has been suspended"), renders fall back to the CPU, but the window's renderer (egui-wgpu)
|
||||
panics on the lost device and LightCraft stops. It says so ("LightCraft stopped unexpectedly", not "could
|
||||
not open its window") and records it in `gpu-lost.marker` in the settings folder: the next start turns
|
||||
*Use the GPU for rendering* off and says why once. Turn it back on in Settings ▸ Performance after a
|
||||
driver update.
|
||||
|
||||
**Window never appears, "not responding" (Windows, NVIDIA; issue #374).** With *Shader Cache Size*
|
||||
set to **Disabled** in NVIDIA Control Panel (Manage 3D settings → Global Settings), NVIDIA's driver
|
||||
walks the whole system drive while the window's GPU device is created (`D3D12CreateDevice` →
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Lightroom parity checklist
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (integration batch 35: KEYC-SELECTONLYACTIVE, Select Only Active Photo ⌘⇧D; LR-BEHAV-AUTOSAVE, damaged snapshots repaired on open; LR-EDIT-OPTICS-PROFILE, Nikon Z 6II / Z 8 / Z f in-file distortion; LRC-LIB-CATALOG-IMPORT, WAL-only catalogs open; LR-EXP-DIM, enlargements stop at 512 MP; LR-IMP-CAMERA-COVERAGE, Canon EOS RP CR3 verified; model folder retry) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (LR-BEHAV-GPU: a GPU lost while running turns GPU rendering off for the next start (#1337); integration batch 35: KEYC-SELECTONLYACTIVE, Select Only Active Photo ⌘⇧D; LR-BEHAV-AUTOSAVE, damaged snapshots repaired on open; LR-EDIT-OPTICS-PROFILE, Nikon Z 6II / Z 8 / Z f in-file distortion; LRC-LIB-CATALOG-IMPORT, WAL-only catalogs open; LR-EXP-DIM, enlargements stop at 512 MP; LR-IMP-CAMERA-COVERAGE, Canon EOS RP CR3 verified; model folder retry) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
|
||||
The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
|
||||
have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
|
||||
@@ -459,7 +459,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`, `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-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); a GPU lost while LightCraft runs (driver reset) stops it with that message and turns GPU rendering off for the next start, which says why (issue #1337; the window's renderer can't survive the lost device); 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` | |
|
||||
@@ -815,6 +815,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
|
||||
| 2026-10-13 | patch | LR-BEHAV-AUTOSAVE: a damaged snapshot that still parses is repaired on open (ids, stacks, folder loops, id counters) instead of panicking or hanging; status unchanged |
|
||||
| 2026-10-13 | minor | LR-EDIT-OPTICS-PROFILE: Nikon Z 6II / Z 8 / Z f NEFs use the camera's in-file distortion correction (NikonNEFInfo); status unchanged (🟡) |
|
||||
| 2026-10-11 | minor | Model setup folder action, opener error and retry (LR-MASK-OBJECTS). |
|
||||
| 2026-10-13 | patch | LR-BEHAV-GPU: a GPU lost while LightCraft runs (driver reset) is reported as a stop, not a failed start, and the next start renders on the CPU and says why (issue #1337); status unchanged |
|
||||
| 2026-10-11 | minor | KEY-SLIDER-TYPE: honor immediate Return/Escape and keep typed keys in the value editor; add regression references |
|
||||
| 2026-10-11 | minor | LR-PROF-CAMERACOLOR: raws with a black-and-white camera JPEG take its tone curve (issue #577) |
|
||||
| 2026-10-11 | minor | LR-VIEW-ZOOM and LR-VIEW-COMPARE: Compare and Reference are sharp at 1:1 (zoom windows per pane, two originals kept decoded while they are open); statuses unchanged |
|
||||
|
||||
+8
-4
@@ -2,6 +2,10 @@
|
||||
|
||||
## 2026-10-14
|
||||
|
||||
### Reliability
|
||||
- A photo whose Defringe amount was stored far past the sliders' 20 (in a damaged catalog or sidecar) no longer
|
||||
crashes LightCraft each time it renders: the amount acts as 20.
|
||||
|
||||
### Hard-edged Heal and Clone spots
|
||||
- A spot with Feather at 0 no longer draws black pixels exactly on its radius. This also fixes spots at 0% Opacity
|
||||
changing pixels along their edge. The hard edge now leaves pixels on the radius unchanged.
|
||||
@@ -18,10 +22,6 @@
|
||||
models in a `LightCraftData` folder beside it instead of the per-user settings folder (issue #1200), as
|
||||
PhotoCraft does. If that folder can't be written, the usual folder is used and the log says why.
|
||||
|
||||
### Reliability
|
||||
- A photo whose Defringe amount was stored far past the sliders' 20 (in a damaged catalog or sidecar) no longer
|
||||
crashes LightCraft each time it renders: the amount acts as 20.
|
||||
|
||||
### Reliability
|
||||
- Importing a preset zip that unpacks to far more than any preset (a "zip bomb") is refused with a message naming
|
||||
the file, instead of asking for gigabytes of memory, which could end LightCraft.
|
||||
@@ -108,6 +108,10 @@
|
||||
- An export enlarged far past the photo's size (20000 typed for 2000 with Don't Enlarge off) no longer ends
|
||||
LightCraft: an enlargement stops at 512 megapixels, as in Lightroom, and one that needs more memory than the
|
||||
computer has is refused with the size it would take, before it starts.
|
||||
- When the graphics driver resets the GPU while LightCraft is exporting or rendering (issue #1337, seen on Windows
|
||||
with NVIDIA), LightCraft still stops, because its window can't keep drawing on the lost device, but it now says
|
||||
"LightCraft stopped unexpectedly" and why, instead of "could not open its window", and the next start renders
|
||||
on the CPU and explains how to turn the GPU back on.
|
||||
|
||||
### Slider drags on large raws
|
||||
- Dragging a slider no longer re-renders the edited photo's filmstrip thumbnail on every frame; it follows once you
|
||||
|
||||
Reference in new issue
Block a user