From ae55b94287dab15911796d51e45209d444e9df69 Mon Sep 17 00:00:00 2001 From: Chad Elliott Date: Mon, 26 Oct 2026 17:06:08 -0500 Subject: [PATCH] perf(ui-egui): #3067 test apps render on two workers, not six Every LightcraftApp built by the ui-egui lib tests started the product's render pool: cores - 1, at most six workers. Several tests run at once and most build an app, so 4 test threads ran about 24 render workers and 16 ran about 96. Under cfg(test) Renderer::default now starts two (TEST_WORKERS); a test that needs more asks with with_worker_threads. The product default is unchanged. a_test_app_renders_on_two_workers fails without it (6 workers on a 16-core machine). Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/render.rs | 22 ++++++++++++++++++-- crates/ui-egui/src/render/thumbnail_tests.rs | 19 +++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/render.rs b/crates/ui-egui/src/render.rs index 31117b22e..48d6cdde4 100644 --- a/crates/ui-egui/src/render.rs +++ b/crates/ui-egui/src/render.rs @@ -267,10 +267,28 @@ impl Slot { } } +/// Render workers of an app built by this crate's own tests. Most of them build an app, several +/// run at once, and a test needs a render or two at a time, not the machine's six (issue #3067: +/// sixteen tests at once ran about 96 render threads on 16 cores). A test that needs more asks +/// for them with [`Renderer::with_worker_threads`]. +#[cfg(test)] +pub(crate) const TEST_WORKERS: usize = 2; + +/// Render workers an app starts with: the machine's job-pool size, at most six (none on wasm). +#[cfg(not(test))] +fn default_workers() -> usize { + if cfg!(target_arch = "wasm32") { 0 } else { JobPool::::default_threads().min(6) } +} + +/// Render workers an app starts with in this crate's tests: [`TEST_WORKERS`]. +#[cfg(test)] +fn default_workers() -> usize { + TEST_WORKERS +} + impl Default for Renderer { fn default() -> Self { - let threads = if cfg!(target_arch = "wasm32") { 0 } else { JobPool::::default_threads().min(6) }; - Self::with_worker_threads(threads) + Self::with_worker_threads(default_workers()) } } diff --git a/crates/ui-egui/src/render/thumbnail_tests.rs b/crates/ui-egui/src/render/thumbnail_tests.rs index 984ce0462..cbb7ae70f 100644 --- a/crates/ui-egui/src/render/thumbnail_tests.rs +++ b/crates/ui-egui/src/render/thumbnail_tests.rs @@ -446,6 +446,25 @@ fn a_panicking_render_is_a_failure_not_a_dead_worker() { assert_eq!(app.renderer.live_workers(), workers); } +/// An app built by this crate's tests renders on two workers, not the machine's six: several +/// tests run at once, and each of them used to start six (issue #3067). +#[test] +fn a_test_app_renders_on_two_workers() { + let (mut app, _, ctx) = fixture_with(Session::new(), (*photo()).clone()); + app.renderer.offload = None; // the real job pool + let slot = Slot::Thumb(PhotoId(1)); + let job = app.session.thumb_job(PhotoId(1), 256).unwrap(); + app.renderer.request(slot, job, 30); + assert_eq!(app.renderer.live_workers(), TEST_WORKERS, "the pool starts all its workers with its first job"); + let t0 = std::time::Instant::now(); + while app.renderer.is_pending(slot) { + assert!(t0.elapsed() < std::time::Duration::from_secs(20), "the render never answered"); + std::thread::sleep(std::time::Duration::from_millis(5)); + app.renderer.poll(&ctx, &mut app.session); + } + assert!(app.renderer.textures.contains_key(&slot), "{:?}", app.renderer.failure(slot)); +} + #[test] fn raw_quick_path_frames_build_no_jobs() { // an unedited raw: the grid shows its embedded preview (held here until released) and stops