mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
Merge PR #1548
# Conflicts: # crates/ui-egui/locales/de-formats.json # crates/ui-egui/locales/de.json # crates/ui-egui/locales/es-formats.json # crates/ui-egui/locales/es.json # crates/ui-egui/locales/fr-formats.json # crates/ui-egui/locales/fr.json # crates/ui-egui/locales/ja-formats.json # crates/ui-egui/locales/ja.json # crates/ui-egui/locales/pt-br-formats.json # crates/ui-egui/locales/pt-br.json # crates/ui-egui/locales/ru-formats.json # crates/ui-egui/locales/ru.json # crates/ui-egui/locales/uk-formats.json # crates/ui-egui/locales/uk.json # crates/ui-egui/locales/zh-hans-formats.json # crates/ui-egui/locales/zh-hans.json # crates/ui-egui/locales/zh-hant-formats.json # crates/ui-egui/locales/zh-hant.json # docs/parity-checklist.md # docs/whats-new.md
This commit is contained in:
+129
-1
@@ -163,6 +163,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
|
/// 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.
|
/// own profiles first (Linux: colord / DisplayCAL; macOS: ColorSync), then the system's.
|
||||||
fn display_profile_dir() -> Option<std::path::PathBuf> {
|
fn display_profile_dir() -> Option<std::path::PathBuf> {
|
||||||
@@ -829,7 +869,7 @@ fn main() -> eframe::Result {
|
|||||||
let library_dir = library_dir.or_else(lightcraft_engine::library::default_dir);
|
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
|
// GPU compute: off if the preference says so, or if the last launch died creating the device
|
||||||
// (issue #136) — before anything can create it
|
// (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();
|
let gpu_on = prefs.as_ref().is_none_or(|u| u.settings.gpu) && gpu_crash.is_none();
|
||||||
lightcraft_engine::gpu::set_enabled(gpu_on);
|
lightcraft_engine::gpu::set_enabled(gpu_on);
|
||||||
// the window's renderer: probed (Windows) and with a fallback chain, so a driver that can't
|
// the window's renderer: probed (Windows) and with a fallback chain, so a driver that can't
|
||||||
@@ -838,6 +878,9 @@ fn main() -> eframe::Result {
|
|||||||
let window_gpu::Chosen { renderer, wgpu_options, which: window_renderer, notice: window_notice } = window_gpu::choose(&win_files);
|
let window_gpu::Chosen { renderer, wgpu_options, which: window_renderer, notice: window_notice } = window_gpu::choose(&win_files);
|
||||||
win_files.arm(window_renderer);
|
win_files.arm(window_renderer);
|
||||||
let win_files_app = win_files.clone();
|
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 {
|
let options = eframe::NativeOptions {
|
||||||
renderer,
|
renderer,
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
@@ -875,6 +918,7 @@ fn main() -> eframe::Result {
|
|||||||
Box::new(move |cc| {
|
Box::new(move |cc| {
|
||||||
// the window's renderer is up (eframe calls this after creating it)
|
// the window's renderer is up (eframe calls this after creating it)
|
||||||
win_files_app.started(window_renderer);
|
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());
|
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
|
// 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)
|
// (never required: without it, AI masks offer to download it; see docs/ai-masks.md)
|
||||||
@@ -936,6 +980,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
|
// a panic that still escaped the renderer's start (the probe and error handler should
|
||||||
// prevent it): an error like any other failed start
|
// 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())));
|
let started = started.unwrap_or_else(|_| Err(eframe::Error::AppCreation("the window's renderer panicked while starting".into())));
|
||||||
@@ -950,6 +1019,34 @@ fn main() -> eframe::Result {
|
|||||||
started
|
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
|
/// 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
|
/// 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).
|
/// no console on Windows, and an app started from Finder shows no standard error (issue #260).
|
||||||
@@ -1152,6 +1249,37 @@ mod tests {
|
|||||||
let _ = std::fs::remove_dir_all(&d);
|
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.
|
/// Issue #136: a GPU init marker left by a crashed launch is reported once and removed.
|
||||||
#[test]
|
#[test]
|
||||||
fn gpu_crash_marker_is_reported_once() {
|
fn gpu_crash_marker_is_reported_once() {
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} s AV1-Video",
|
"{s} s of AV1 video": "{s} s AV1-Video",
|
||||||
"Slideshow exported: {what}": "Diashow exportiert: {what}",
|
"Slideshow exported: {what}": "Diashow exportiert: {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · {n} übersprungen (nicht darstellbar)",
|
" · {n} skipped (could not be rendered)": " · {n} übersprungen (nicht darstellbar)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} RAW-Dateien können jetzt aus ihren Rohdaten entwickelt werden"
|
"{n} raws can now be developed from their raw data": "{n} RAW-Dateien können jetzt aus ihren Rohdaten entwickelt werden",
|
||||||
|
"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}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2282,5 +2282,6 @@
|
|||||||
"Same folder as original photo": "Im selben Ordner wie das Originalfoto",
|
"Same folder as original photo": "Im selben Ordner wie das Originalfoto",
|
||||||
"Tidying the thumbnail cache": "Miniaturcache wird aufgeräumt",
|
"Tidying the thumbnail cache": "Miniaturcache wird aufgeräumt",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Alle Bearbeitungen zurücksetzen (⌘⇧R)",
|
"Reset all edits (Cmd+Shift+R)": "Alle Bearbeitungen zurücksetzen (⌘⇧R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Intelligente Alben aktualisieren sich selbst: Ändern Sie ihre Regeln oder verwenden Sie Foto → Foto löschen"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Intelligente Alben aktualisieren sich selbst: Ändern Sie ihre Regeln oder verwenden Sie Foto → Foto löschen",
|
||||||
|
"LightCraft stopped": "LightCraft wurde beendet"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} s de vídeo AV1",
|
"{s} s of AV1 video": "{s} s de vídeo AV1",
|
||||||
"Slideshow exported: {what}": "Presentación exportada: {what}",
|
"Slideshow exported: {what}": "Presentación exportada: {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · {n} omitidas (no se pudieron representar)",
|
" · {n} skipped (could not be rendered)": " · {n} omitidas (no se pudieron representar)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} RAW ya se pueden revelar a partir de sus datos RAW"
|
"{n} raws can now be developed from their raw data": "{n} RAW ya se pueden revelar a partir de sus datos RAW",
|
||||||
|
"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}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2281,5 +2281,6 @@
|
|||||||
"Same folder as original photo": "Misma carpeta que la foto original",
|
"Same folder as original photo": "Misma carpeta que la foto original",
|
||||||
"Tidying the thumbnail cache": "Ordenando la caché de miniaturas",
|
"Tidying the thumbnail cache": "Ordenando la caché de miniaturas",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Restablecer todas las ediciones (Cmd+Mayús+R)",
|
"Reset all edits (Cmd+Shift+R)": "Restablecer todas las ediciones (Cmd+Mayús+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Los álbumes inteligentes se actualizan solos: cambie sus reglas o use Foto → Eliminar foto"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Los álbumes inteligentes se actualizan solos: cambie sus reglas o use Foto → Eliminar foto",
|
||||||
|
"LightCraft stopped": "LightCraft se detuvo"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} s de vidéo AV1",
|
"{s} s of AV1 video": "{s} s de vidéo AV1",
|
||||||
"Slideshow exported: {what}": "Diaporama exporté : {what}",
|
"Slideshow exported: {what}": "Diaporama exporté : {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · {n} ignorées (rendu impossible)",
|
" · {n} skipped (could not be rendered)": " · {n} ignorées (rendu impossible)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} fichiers RAW peuvent désormais être développés à partir de leurs données RAW"
|
"{n} raws can now be developed from their raw data": "{n} fichiers RAW peuvent désormais être développés à partir de leurs données RAW",
|
||||||
|
"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}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2279,5 +2279,6 @@
|
|||||||
"Same folder as original photo": "Même dossier que la photo d’origine",
|
"Same folder as original photo": "Même dossier que la photo d’origine",
|
||||||
"Tidying the thumbnail cache": "Rangement du cache des miniatures",
|
"Tidying the thumbnail cache": "Rangement du cache des miniatures",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Réinitialiser toutes les retouches (Cmd+Maj+R)",
|
"Reset all edits (Cmd+Shift+R)": "Réinitialiser toutes les retouches (Cmd+Maj+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Les albums intelligents se mettent à jour d'eux-mêmes : modifiez leurs règles ou utilisez Photo → Supprimer la photo"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Les albums intelligents se mettent à jour d'eux-mêmes : modifiez leurs règles ou utilisez Photo → Supprimer la photo",
|
||||||
|
"LightCraft stopped": "LightCraft s’est arrêté"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "AV1 ビデオ {s} 秒",
|
"{s} s of AV1 video": "AV1 ビデオ {s} 秒",
|
||||||
"Slideshow exported: {what}": "スライドショーを書き出しました:{what}",
|
"Slideshow exported: {what}": "スライドショーを書き出しました:{what}",
|
||||||
" · {n} skipped (could not be rendered)": " · {n} 枚をスキップ(レンダリングできません)",
|
" · {n} skipped (could not be rendered)": " · {n} 枚をスキップ(レンダリングできません)",
|
||||||
"{n} raws can now be developed from their raw data": "{n}枚のRAWをRAWデータから現像できるようになりました"
|
"{n} raws can now be developed from their raw data": "{n}枚のRAWをRAWデータから現像できるようになりました",
|
||||||
|
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraftが予期せず終了しました: {e}\n\n詳しくはログファイルをご覧ください: {path}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}": "LightCraftが予期せず終了しました: {e}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2281,5 +2281,6 @@
|
|||||||
"Same folder as original photo": "元の写真と同じフォルダー",
|
"Same folder as original photo": "元の写真と同じフォルダー",
|
||||||
"Tidying the thumbnail cache": "サムネールキャッシュを整理中",
|
"Tidying the thumbnail cache": "サムネールキャッシュを整理中",
|
||||||
"Reset all edits (Cmd+Shift+R)": "すべての編集をリセット(⌘⇧R)",
|
"Reset all edits (Cmd+Shift+R)": "すべての編集をリセット(⌘⇧R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "スマートアルバムは自動で更新されます。ルールを変更するか、写真 → 写真を削除を使ってください"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "スマートアルバムは自動で更新されます。ルールを変更するか、写真 → 写真を削除を使ってください",
|
||||||
|
"LightCraft stopped": "LightCraftが終了しました"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} s de vídeo AV1",
|
"{s} s of AV1 video": "{s} s de vídeo AV1",
|
||||||
"Slideshow exported: {what}": "Apresentação exportada: {what}",
|
"Slideshow exported: {what}": "Apresentação exportada: {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · {n} ignoradas (não foi possível renderizar)",
|
" · {n} skipped (could not be rendered)": " · {n} ignoradas (não foi possível renderizar)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} RAW agora podem ser revelados a partir dos dados RAW"
|
"{n} raws can now be developed from their raw data": "{n} RAW agora podem ser revelados a partir dos dados RAW",
|
||||||
|
"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}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2121,5 +2121,6 @@
|
|||||||
"Same folder as original photo": "Mesma pasta da foto original",
|
"Same folder as original photo": "Mesma pasta da foto original",
|
||||||
"Tidying the thumbnail cache": "Organizando o cache de miniaturas",
|
"Tidying the thumbnail cache": "Organizando o cache de miniaturas",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Redefinir todas as edições (Cmd+Shift+R)",
|
"Reset all edits (Cmd+Shift+R)": "Redefinir todas as edições (Cmd+Shift+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Álbuns inteligentes se atualizam sozinhos: altere as regras ou use Foto → Excluir foto"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Álbuns inteligentes se atualizam sozinhos: altere as regras ou use Foto → Excluir foto",
|
||||||
|
"LightCraft stopped": "O LightCraft parou"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} с видео AV1",
|
"{s} s of AV1 video": "{s} с видео AV1",
|
||||||
"Slideshow exported: {what}": "Слайд-шоу экспортировано: {what}",
|
"Slideshow exported: {what}": "Слайд-шоу экспортировано: {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · пропущено: {n} (не удалось отрисовать)",
|
" · {n} skipped (could not be rendered)": " · пропущено: {n} (не удалось отрисовать)",
|
||||||
"{n} raws can now be developed from their raw data": "RAW-файлов, которые теперь можно проявлять из RAW-данных: {n}"
|
"{n} raws can now be developed from their raw data": "RAW-файлов, которые теперь можно проявлять из RAW-данных: {n}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft неожиданно остановился: {e}\n\nПодробности в файле журнала: {path}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}": "LightCraft неожиданно остановился: {e}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2274,5 +2274,6 @@
|
|||||||
"Same folder as original photo": "Та же папка, что и у оригинала",
|
"Same folder as original photo": "Та же папка, что и у оригинала",
|
||||||
"Tidying the thumbnail cache": "Упорядочивание кэша миниатюр",
|
"Tidying the thumbnail cache": "Упорядочивание кэша миниатюр",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Сбросить все правки (Cmd+Shift+R)",
|
"Reset all edits (Cmd+Shift+R)": "Сбросить все правки (Cmd+Shift+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Смарт-альбомы обновляются сами: измените их правила или выберите Фото → Удалить фото"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Смарт-альбомы обновляются сами: измените их правила или выберите Фото → Удалить фото",
|
||||||
|
"LightCraft stopped": "LightCraft остановился"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} с відео AV1",
|
"{s} s of AV1 video": "{s} с відео AV1",
|
||||||
"Slideshow exported: {what}": "Слайд-шоу експортовано: {what}",
|
"Slideshow exported: {what}": "Слайд-шоу експортовано: {what}",
|
||||||
" · {n} skipped (could not be rendered)": " · пропущено: {n} (не вдалося відтворити)",
|
" · {n} skipped (could not be rendered)": " · пропущено: {n} (не вдалося відтворити)",
|
||||||
"{n} raws can now be developed from their raw data": "RAW-файлів, які тепер можна проявляти з RAW-даних: {n}"
|
"{n} raws can now be developed from their raw data": "RAW-файлів, які тепер можна проявляти з RAW-даних: {n}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft несподівано зупинився: {e}\n\nПодробиці у файлі журналу: {path}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}": "LightCraft несподівано зупинився: {e}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2279,5 +2279,6 @@
|
|||||||
"Same folder as original photo": "Та сама папка, що й оригінал",
|
"Same folder as original photo": "Та сама папка, що й оригінал",
|
||||||
"Tidying the thumbnail cache": "Упорядкування кешу мініатюр",
|
"Tidying the thumbnail cache": "Упорядкування кешу мініатюр",
|
||||||
"Reset all edits (Cmd+Shift+R)": "Скинути всі зміни (Cmd+Shift+R)",
|
"Reset all edits (Cmd+Shift+R)": "Скинути всі зміни (Cmd+Shift+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Розумні альбоми оновлюються самі: змініть їхні правила або виберіть Фото → Видалити фото"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "Розумні альбоми оновлюються самі: змініть їхні правила або виберіть Фото → Видалити фото",
|
||||||
|
"LightCraft stopped": "LightCraft зупинився"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} 秒 AV1 视频",
|
"{s} s of AV1 video": "{s} 秒 AV1 视频",
|
||||||
"Slideshow exported: {what}": "幻灯片放映已导出:{what}",
|
"Slideshow exported: {what}": "幻灯片放映已导出:{what}",
|
||||||
" · {n} skipped (could not be rendered)": " · 已跳过 {n} 张(无法渲染)",
|
" · {n} skipped (could not be rendered)": " · 已跳过 {n} 张(无法渲染)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} 个 RAW 现在可以从 RAW 数据冲印"
|
"{n} raws can now be developed from their raw data": "{n} 个 RAW 现在可以从 RAW 数据冲印",
|
||||||
|
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft 意外停止:{e}\n\n详细信息见日志文件:{path}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}": "LightCraft 意外停止:{e}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2120,5 +2120,6 @@
|
|||||||
"Same folder as original photo": "与原始照片相同的文件夹",
|
"Same folder as original photo": "与原始照片相同的文件夹",
|
||||||
"Tidying the thumbnail cache": "正在整理缩略图缓存",
|
"Tidying the thumbnail cache": "正在整理缩略图缓存",
|
||||||
"Reset all edits (Cmd+Shift+R)": "重置所有编辑 (Cmd+Shift+R)",
|
"Reset all edits (Cmd+Shift+R)": "重置所有编辑 (Cmd+Shift+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "智能相册会自动更新:请更改其规则,或使用“照片 → 删除照片”"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "智能相册会自动更新:请更改其规则,或使用“照片 → 删除照片”",
|
||||||
|
"LightCraft stopped": "LightCraft 已停止"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,5 +283,7 @@
|
|||||||
"{s} s of AV1 video": "{s} 秒 AV1 視訊",
|
"{s} s of AV1 video": "{s} 秒 AV1 視訊",
|
||||||
"Slideshow exported: {what}": "幻燈片播放已匯出:{what}",
|
"Slideshow exported: {what}": "幻燈片播放已匯出:{what}",
|
||||||
" · {n} skipped (could not be rendered)": " · 已略過 {n} 張(無法算繪)",
|
" · {n} skipped (could not be rendered)": " · 已略過 {n} 張(無法算繪)",
|
||||||
"{n} raws can now be developed from their raw data": "{n} 個 RAW 現在可以從 RAW 資料沖印"
|
"{n} raws can now be developed from their raw data": "{n} 個 RAW 現在可以從 RAW 資料沖印",
|
||||||
|
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft 意外停止:{e}\n\n詳細資訊請見記錄檔:{path}",
|
||||||
|
"LightCraft stopped unexpectedly: {e}": "LightCraft 意外停止:{e}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2082,5 +2082,6 @@
|
|||||||
"Same folder as original photo": "與原始照片相同的資料夾",
|
"Same folder as original photo": "與原始照片相同的資料夾",
|
||||||
"Tidying the thumbnail cache": "正在整理縮圖快取",
|
"Tidying the thumbnail cache": "正在整理縮圖快取",
|
||||||
"Reset all edits (Cmd+Shift+R)": "重設所有編輯(Cmd+Shift+R)",
|
"Reset all edits (Cmd+Shift+R)": "重設所有編輯(Cmd+Shift+R)",
|
||||||
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "智慧型相簿會自動更新:請變更其規則,或使用「照片 → 刪除照片」"
|
"Smart albums update automatically: change their rules, or use Photo ▸ Delete Photo": "智慧型相簿會自動更新:請變更其規則,或使用「照片 → 刪除照片」",
|
||||||
|
"LightCraft stopped": "LightCraft 已停止"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,6 +214,17 @@ pub fn startup_failed_message(error: &str, log_file: Option<&str>) -> (String, S
|
|||||||
(tr("LightCraft could not start").to_string(), text)
|
(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 {
|
pub fn builtin_label(source: &str, builtin: bool) -> &str {
|
||||||
if builtin { tr(source) } else { source }
|
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
|
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)`).
|
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*
|
**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
|
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` →
|
walks the whole system drive while the window's GPU device is created (`D3D12CreateDevice` →
|
||||||
|
|||||||
@@ -465,7 +465,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-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-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-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-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-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` | |
|
| LR-BEHAV-TOAST | Toast notifications | P1 | ✅ | `crates/ui-egui/src/panels/mod.rs` | |
|
||||||
@@ -843,6 +843,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
|
|||||||
| 2026-10-11 | minor | LR-EXP-LOCATION: Export to "Same folder as original photo" (`destinationMode`), issue #956; status unchanged (✅) |
|
| 2026-10-11 | minor | LR-EXP-LOCATION: Export to "Same folder as original photo" (`destinationMode`), issue #956; status unchanged (✅) |
|
||||||
| 2026-10-12 | minor | LR-IMP-CAMERA-COVERAGE: 117 Nikon and Fujifilm Adobe models verified on one CC0 file each; COOLPIX 5700 / 8400 / 8800 found decoding wrongly; status unchanged (🟡) |
|
| 2026-10-12 | minor | LR-IMP-CAMERA-COVERAGE: 117 Nikon and Fujifilm Adobe models verified on one CC0 file each; COOLPIX 5700 / 8400 / 8800 found decoding wrongly; status unchanged (🟡) |
|
||||||
| 2026-10-13 | patch | LR-EXP-QUALITY: JPEG exports use Huffman tables made for each image (same pixels, 12–14 % smaller files, issue #1373); status unchanged |
|
| 2026-10-13 | patch | LR-EXP-QUALITY: JPEG exports use Huffman tables made for each image (same pixels, 12–14 % smaller files, issue #1373); status unchanged |
|
||||||
|
| 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 | 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-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 |
|
| 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
@@ -71,6 +71,10 @@
|
|||||||
carries Huffman tables made for its own picture instead of the standard example tables. Every viewer opens them
|
carries Huffman tables made for its own picture instead of the standard example tables. Every viewer opens them
|
||||||
as before.
|
as before.
|
||||||
|
|
||||||
|
### 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
|
### 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
|
- 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.
|
changing pixels along their edge. The hard edge now leaves pixels on the radius unchanged.
|
||||||
@@ -87,10 +91,6 @@
|
|||||||
models in a `LightCraftData` folder beside it instead of the per-user settings folder (issue #1200), as
|
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.
|
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
|
### Reliability
|
||||||
- Importing a preset zip that unpacks to far more than any preset (a "zip bomb") is refused with a message naming
|
- 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.
|
the file, instead of asking for gigabytes of memory, which could end LightCraft.
|
||||||
@@ -187,6 +187,10 @@
|
|||||||
and a smaller export look like the full-size result scaled down; 1:1 views and full-size exports are unchanged.
|
and a smaller export look like the full-size result scaled down; 1:1 views and full-size exports are unchanged.
|
||||||
Like in Lightroom, judge noise reduction at 1:1: at small sizes the noise it removes is mostly averaged away.
|
Like in Lightroom, judge noise reduction at 1:1: at small sizes the noise it removes is mostly averaged away.
|
||||||
Cached thumbnails are redrawn.
|
Cached thumbnails are redrawn.
|
||||||
|
- 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
|
### 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
|
- 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