diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 7259b18150..2ffa96ea92 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -951,6 +951,9 @@ fn ensure_gpu(app: &mut PhotocraftApp, idx: usize, visible: DRect) -> bool { log::info!("{e}; using the CPU compositor"); } app.perf.record(r.kind, r.px, r.composite_ms, r.upload_ms); + if r.kind == "full" { + crate::notices::slow_cpu_refresh(app, id.0, r.composite_ms + r.upload_ms, r.fallback.as_deref()); + } if r.kind.starts_with("gpu") { app.perf.gpu_uploads = r.uploads; } else { diff --git a/crates/ui-egui/src/gpu_canvas.rs b/crates/ui-egui/src/gpu_canvas.rs index 5197836d5a..8f6aa2e37e 100644 --- a/crates/ui-egui/src/gpu_canvas.rs +++ b/crates/ui-egui/src/gpu_canvas.rs @@ -386,7 +386,7 @@ impl GpuCanvas { // compositor does it, and edits in the view (damage rects) stay on the GPU. if region == doc.bounds() && !comp.fits_budget(doc, region) { res.compositor = Some(comp); - return Err(photocraft_gpu::Unsupported("layers exceed the GPU memory budget; full refresh on the CPU".into())); + return Err(photocraft_gpu::Unsupported(OVER_BUDGET.into())); } if fresh { let tex = DocTextures::new(device, res, size, self.tile, format); @@ -1040,6 +1040,9 @@ fn texel_to_f32(format: wgpu::TextureFormat, b: &[u8]) -> [f32; 4] { /// Floor of the compositor's memory budget: enough for a viewport's pages of a dozen layers. pub const MIN_GPU_BUDGET: u64 = 512 << 20; +/// Why a full refresh whose layer pages don't fit [`memory_budget`] goes to the CPU compositor. +pub const OVER_BUDGET: &str = "layers exceed the GPU memory budget; full refresh on the CPU"; + /// GPU memory the wgpu compositor may hold for layer pages and effect maps: what Memory Usage /// (`allowance`, Preferences › Performance) leaves after the document's pixels and History /// (`pixels`), at most a quarter of physical memory (`ram`; 16 GB assumed when unknown), and at diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index fd6c4853ad..d776b7b90c 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -2196,3 +2196,8 @@ cameraRaw Color Barvy Fuchsia Fuchsiová Violet Fialová Reset Obnovit + Large document: redrawing on the CPU Velký dokument: překreslování na CPU + This redraw took {seconds} s on the CPU. Toto překreslení na CPU trvalo {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Úpravy, které mění celý dokument, například režim prolnutí, krytí nebo viditelnost vrstvy, jej překreslí celý. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Vrstvy se nevejdou do rozpočtu paměti GPU. Zvýšením hodnoty Využití paměti v Předvolbách › Výkon se rozpočet zvětší, nejvýše na čtvrtinu paměti tohoto počítače. + The GPU compositor wasn't used: {reason} Kompozitor GPU nebyl použit: {reason} diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 456bbf1757..23fe6ba526 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -2235,3 +2235,8 @@ layerLabel No Color Keine Farbe Indigo Indigo Fuchsia Fuchsia Violet Violett + Large document: redrawing on the CPU Großes Dokument: Neuzeichnen auf der CPU + This redraw took {seconds} s on the CPU. Dieses Neuzeichnen auf der CPU dauerte {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Änderungen, die das ganze Dokument betreffen, etwa Füllmethode, Deckkraft oder Sichtbarkeit einer Ebene, zeichnen es vollständig neu. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Die Ebenen passen nicht in das GPU-Speicherbudget. Eine höhere Speichernutzung unter Voreinstellungen › Leistung erhöht das Budget, bis zu einem Viertel des Arbeitsspeichers dieses Computers. + The GPU compositor wasn't used: {reason} Der GPU-Compositor wurde nicht verwendet: {reason} diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index c4410a195e..2417fd05d4 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -2266,3 +2266,8 @@ cameraRaw Amount Cantidad Fuchsia Fucsia Violet Violeta Reset Restaurar + Large document: redrawing on the CPU Documento grande: redibujando en la CPU + This redraw took {seconds} s on the CPU. Este redibujado en la CPU tardó {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Las ediciones que cambian todo el documento, como el modo de fusión, la opacidad o la visibilidad de una capa, lo redibujan por completo. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Las capas no caben en el presupuesto de memoria de la GPU. Aumentar Uso de memoria en Preferencias › Rendimiento amplía el presupuesto, hasta una cuarta parte de la memoria de este equipo. + The GPU compositor wasn't used: {reason} No se usó el compositor de GPU: {reason} diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 88daf50544..bfd8a1c709 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -2624,3 +2624,8 @@ cameraRaw Blending Fusion Indigo Indigo Fuchsia Fuchsia Violet Violet + Large document: redrawing on the CPU Document volumineux : redessin sur le CPU + This redraw took {seconds} s on the CPU. Ce redessin sur le CPU a pris {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Les modifications qui touchent tout le document, comme le mode de fusion, l'opacité ou la visibilité d'un calque, le redessinent entièrement. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Les calques dépassent le budget de mémoire GPU. Augmenter Utilisation de la mémoire dans Préférences › Performances relève ce budget, jusqu'au quart de la mémoire de cet ordinateur. + The GPU compositor wasn't used: {reason} Le compositeur GPU n'a pas été utilisé : {reason} diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 78227d2484..44003d1970 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -2611,3 +2611,8 @@ cameraRaw Blending Pembauran Indigo Nila Fuchsia Fuksia Violet Ungu + Large document: redrawing on the CPU Dokumen besar: menggambar ulang di CPU + This redraw took {seconds} s on the CPU. Penggambaran ulang di CPU ini memakan waktu {seconds} dtk. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Suntingan yang mengubah seluruh dokumen, seperti mode campuran, opasitas, atau visibilitas layer, menggambar ulang semuanya. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Layer tidak muat dalam anggaran memori GPU. Menaikkan Pemakaian memori di Preferensi › Performa menaikkan anggaran, hingga seperempat memori komputer ini. + The GPU compositor wasn't used: {reason} Kompositor GPU tidak digunakan: {reason} diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 28e6a3bb7f..ab0f0540dc 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -2267,3 +2267,8 @@ cameraRaw Amount Quantità Indigo Indaco Fuchsia Fucsia Violet Viola + Large document: redrawing on the CPU Documento grande: ridisegno sulla CPU + This redraw took {seconds} s on the CPU. Questo ridisegno sulla CPU ha richiesto {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Le modifiche che cambiano tutto il documento, come metodo di fusione, opacità o visibilità di un livello, lo ridisegnano per intero. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. I livelli non rientrano nel budget di memoria della GPU. Aumentare Uso memoria in Preferenze › Prestazioni alza il budget, fino a un quarto della memoria di questo computer. + The GPU compositor wasn't used: {reason} Il compositore GPU non è stato usato: {reason} diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 78a2694fd8..ddb901dd5b 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -2179,3 +2179,8 @@ cameraRaw Color カラー Fuchsia フクシア Violet 紫 Reset 初期化 + Large document: redrawing on the CPU 大きなドキュメント:CPUで再描画しています + This redraw took {seconds} s on the CPU. この再描画はCPUで{seconds}秒かかりました。 + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. レイヤーの描画モード、不透明度、表示/非表示など、ドキュメント全体に関わる編集では全体が再描画されます。 + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. レイヤーがGPUメモリの予算に収まりません。環境設定 › パフォーマンスのメモリ使用量を増やすと、このコンピューターのメモリの4分の1まで予算が増えます。 + The GPU compositor wasn't used: {reason} GPUコンポジターは使用されませんでした:{reason} diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 726cb4b0cb..f43ef7baa6 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -2779,3 +2779,8 @@ cameraRaw Color 색상 Seafoam 바다 거품색 Indigo 남색 Fuchsia 자홍색 + Large document: redrawing on the CPU 큰 문서: CPU에서 다시 그리는 중 + This redraw took {seconds} s on the CPU. 이번 다시 그리기는 CPU에서 {seconds}초 걸렸습니다. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. 레이어의 혼합 모드, 불투명도, 표시 여부처럼 문서 전체를 바꾸는 편집은 문서 전체를 다시 그립니다. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. 레이어가 GPU 메모리 예산에 맞지 않습니다. 환경 설정 › 성능에서 메모리 사용량을 늘리면 이 컴퓨터 메모리의 4분의 1까지 예산이 늘어납니다. + The GPU compositor wasn't used: {reason} GPU 합성기를 사용하지 않았습니다: {reason} diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index ad8ddccc1b..c933b37db5 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3228,3 +3228,8 @@ cameraRaw Roughness Chropowatość Seafoam Morski Indigo Indygo Fuchsia Fuksja + Large document: redrawing on the CPU Duży dokument: odświeżanie na CPU + This redraw took {seconds} s on the CPU. To odświeżenie na CPU trwało {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Zmiany obejmujące cały dokument, takie jak tryb mieszania, krycie lub widoczność warstwy, odświeżają go w całości. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Warstwy nie mieszczą się w budżecie pamięci GPU. Zwiększenie wartości Użycie pamięci w Preferencje › Wydajność zwiększa budżet, maksymalnie do jednej czwartej pamięci tego komputera. + The GPU compositor wasn't used: {reason} Kompozytor GPU nie został użyty: {reason} diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 7198a40a26..ad5ac9fe73 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2631,3 +2631,8 @@ cameraRaw Whites Brancos Indigo Índigo Fuchsia Fúcsia Violet Violeta + Large document: redrawing on the CPU Documento grande: redesenhando na CPU + This redraw took {seconds} s on the CPU. Este redesenho na CPU levou {seconds} s. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Edições que mudam o documento inteiro, como modo de mesclagem, opacidade ou visibilidade de uma camada, redesenham tudo. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. As camadas não cabem no orçamento de memória da GPU. Aumentar Uso de memória em Preferências › Desempenho eleva o orçamento, até um quarto da memória deste computador. + The GPU compositor wasn't used: {reason} O compositor de GPU não foi usado: {reason} diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index ff4d7ba603..898a51b815 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -2471,3 +2471,8 @@ cameraRaw Roughness Неровность Indigo Индиго Fuchsia Фуксия Violet Фиолетовый + Large document: redrawing on the CPU Большой документ: перерисовка на CPU + This redraw took {seconds} s on the CPU. Эта перерисовка на CPU заняла {seconds} с. + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. Правки, затрагивающие весь документ, например режим наложения, непрозрачность или видимость слоя, перерисовывают его целиком. + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. Слои не помещаются в бюджет памяти GPU. Увеличение параметра «Память» в разделе Настройки › Производительность повышает бюджет, но не больше четверти памяти этого компьютера. + The GPU compositor wasn't used: {reason} Компоновщик GPU не использовался: {reason} diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index f65777e360..138eeec4ed 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -2204,3 +2204,8 @@ cameraRaw Blending 混合 Fuchsia 紫红色 Violet 紫罗兰色 Reset 复位 + Large document: redrawing on the CPU 大型文档:正在用 CPU 重绘 + This redraw took {seconds} s on the CPU. 此次 CPU 重绘耗时 {seconds} 秒。 + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. 更改整个文档的编辑(如图层的混合模式、不透明度或可见性)会重绘整个文档。 + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. 图层超出 GPU 内存预算。在 首选项 › 性能 中提高内存使用量可增加预算,最多为本机内存的四分之一。 + The GPU compositor wasn't used: {reason} 未使用 GPU 合成器:{reason} diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 8e41ff56dc..128584351d 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2173,3 +2173,8 @@ cameraRaw Blending 混合 Fuchsia 紫紅色 Violet 紫羅蘭色 Reset 重設 + Large document: redrawing on the CPU 大型文件:正在以 CPU 重繪 + This redraw took {seconds} s on the CPU. 此次 CPU 重繪耗時 {seconds} 秒。 + Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it. 變更整份文件的編輯(例如圖層的混合模式、不透明度或可見度)會重繪整份文件。 + Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory. 圖層超出 GPU 記憶體預算。在 偏好設定 › 效能 中提高記憶體使用量可增加預算,最多為本機記憶體的四分之一。 + The GPU compositor wasn't used: {reason} 未使用 GPU 合成器:{reason} diff --git a/crates/ui-egui/src/notices.rs b/crates/ui-egui/src/notices.rs index 3459c3efe1..8c47b760c2 100644 --- a/crates/ui-egui/src/notices.rs +++ b/crates/ui-egui/src/notices.rs @@ -12,6 +12,8 @@ const WAYLAND_FILE_DROP_DISMISSED: &str = "ui.waylandFileDropGuidanceDismissed"; pub const MAX_NOTICES: usize = 3; /// Lines shown per notice before "…and N more". const MAX_LINES: usize = 8; +/// A full canvas refresh on the CPU compositor slower than this (ms) gets a notice. +const SLOW_CPU_REFRESH_MS: f64 = 1000.0; #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct Notice { @@ -62,6 +64,30 @@ pub fn wayland_file_drop_guidance(app: &mut PhotocraftApp) { ); } +/// A full refresh of document `doc` fell back to the CPU compositor (`reason`, the GPU's) and took +/// `ms`. The canvas doesn't respond while it runs, so once per document say why it was slow and +/// which edits will do it again (#1015). +pub fn slow_cpu_refresh(app: &mut PhotocraftApp, doc: u64, ms: f64, reason: Option<&str>) { + if ms.is_nan() || ms < SLOW_CPU_REFRESH_MS || app.ui.slow_refresh_noticed.contains(&doc) { + return; + } + app.ui.slow_refresh_noticed.push(doc); + let seconds = format!("{:.1}", ms / 1000.0); + let mut lines = vec![ + crate::i18n::fmt(tl!("This redraw took {seconds} s on the CPU."), &[("seconds", &seconds)]), + tl!("Edits that change the whole document, such as a layer's blend mode, opacity or visibility, redraw all of it.").into(), + ]; + if reason == Some(crate::gpu_canvas::OVER_BUDGET) { + lines.push( + tl!("Its layers don't fit the GPU memory budget. Raising Memory usage in Preferences › Performance raises the budget, up to a quarter of this computer's memory.") + .into(), + ); + } else if let Some(r) = reason { + lines.push(crate::i18n::fmt(tl!("The GPU compositor wasn't used: {reason}"), &[("reason", r)])); + } + post(app, tl!("Large document: redrawing on the CPU"), lines, false, None); +} + fn dismiss(app: &mut PhotocraftApp, id: u64) { let dismiss_pref = app.ui.notices.iter().find(|notice| notice.id == id).and_then(|notice| notice.dismiss_pref.clone()); app.ui.notices.retain(|notice| notice.id != id); @@ -206,6 +232,31 @@ mod tests { assert!(app.ui.notices.is_empty()); } + #[test] + fn a_slow_cpu_refresh_is_explained_once_per_document() { + let mut app = PhotocraftApp::new(Session::new(), Services::default()); + slow_cpu_refresh(&mut app, 1, 400.0, Some(crate::gpu_canvas::OVER_BUDGET)); + slow_cpu_refresh(&mut app, 1, f64::NAN, Some(crate::gpu_canvas::OVER_BUDGET)); + assert!(app.ui.notices.is_empty(), "a quick refresh needs no explanation"); + + slow_cpu_refresh(&mut app, 1, 55_000.0, Some(crate::gpu_canvas::OVER_BUDGET)); + assert_eq!(app.ui.notices.len(), 1); + let text = app.ui.notices[0].lines.join(" "); + assert!(text.contains("55.0 s"), "{text}"); + assert!(text.contains("blend mode, opacity or visibility"), "{text}"); + assert!(text.contains("Memory usage in Preferences › Performance"), "{text}"); + assert!(!app.ui.notices[0].error); + + slow_cpu_refresh(&mut app, 1, 55_000.0, Some(crate::gpu_canvas::OVER_BUDGET)); + assert_eq!(app.ui.notices.len(), 1, "once per document"); + + slow_cpu_refresh(&mut app, 2, 3_000.0, Some("Blend If on `Overlay` (composited on the CPU)")); + assert_eq!(app.ui.notices.len(), 2); + let text = app.ui.notices[1].lines.join(" "); + assert!(text.contains("Blend If on `Overlay`"), "{text}"); + assert!(!text.contains("Memory usage"), "the budget hint is only for the budget fallback: {text}"); + } + #[test] fn dismissing_a_notice_writes_its_own_preference_key() { let mut app = PhotocraftApp::new(Session::new(), Services::default()); diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index d6b22c5efe..b558db2176 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -873,6 +873,9 @@ pub struct UiState { /// Pending GPU fallback warning, visible to automation. #[serde(default)] pub gpu_fallback_notice: Option, + /// Documents (ids) whose slow full refresh on the CPU compositor has had its notice. + #[serde(default)] + pub slow_refresh_noticed: Vec, /// Status bar info field, Home screen (see `chrome_ui`). #[serde(default)] pub chrome: crate::chrome_ui::ChromeState, @@ -939,6 +942,7 @@ impl Default for UiState { status_error: false, notices: Vec::new(), gpu_fallback_notice: None, + slow_refresh_noticed: Vec::new(), chrome: Default::default(), camera_raw_scope: Default::default(), camera_raw_preview: Default::default(),