diff --git a/apps/lightcraft/src/window_gpu.rs b/apps/lightcraft/src/window_gpu.rs index fefa65a51..69418028a 100644 --- a/apps/lightcraft/src/window_gpu.rs +++ b/apps/lightcraft/src/window_gpu.rs @@ -142,8 +142,13 @@ fn user_notice(failed: Option, forced: Option, p } /// eframe's defaults with the app's backends and DX12 shader compiler (issues #136, #471). +/// +/// Taken from `NativeOptions::default()`, whose surface is egui-wgpu's `LOW_LATENCY` (one frame in +/// flight; two drawables on Metal). `WgpuConfiguration::default()` is `HIGH_THROUGHPUT` instead (two +/// frames queued, three drawables): every drag, slider and crop handle reached the screen a frame +/// later than eframe's default. Live window resizing still gets three drawables from egui-wgpu. pub fn base_options() -> WgpuConfiguration { - let mut c = WgpuConfiguration::default(); + let mut c = eframe::NativeOptions::default().wgpu_options; if let WgpuSetup::CreateNew(n) = &mut c.wgpu_setup { n.instance_descriptor.backends = backend::window_backends(); n.instance_descriptor.backend_options = backend::backend_options(); @@ -290,6 +295,23 @@ mod tests { assert!(died.contains("last time") && !died.contains("Validation Error"), "{died}"); } + /// The window keeps eframe's low-latency surface: one frame queued (two Metal drawables), not + /// the high-throughput `WgpuConfiguration::default()`, which held input a frame longer. The + /// probed (Windows) and forced-backend configurations are built on the same options. + #[test] + fn window_surface_keeps_one_frame_in_flight() { + let surface = base_options().surface; + assert_eq!(surface, eframe::NativeOptions::default().wgpu_options.surface); + assert_eq!(surface, eframe::egui_wgpu::SurfaceConfig::LOW_LATENCY); + assert_eq!(surface.desired_maximum_frame_latency, Some(1)); + assert_eq!(surface.present_mode, wgpu::PresentMode::AutoVsync); + assert_ne!(surface, WgpuConfiguration::default().surface, "egui-wgpu's own default is the high-throughput one"); + // what `main` hands eframe (on Windows `choose` probes the adapters first; not here) + if !cfg!(windows) { + assert_eq!(choose(&Files::new(None)).wgpu_options.surface, surface); + } + } + /// The crash report: a pipeline that fails validation must come back as an error, never as /// wgpu's default panic. Needs an adapter; skipped where there is none (CI without a GPU). #[test] diff --git a/docs/gpu-pipeline.md b/docs/gpu-pipeline.md index f88f81c39..0370e6407 100644 --- a/docs/gpu-pipeline.md +++ b/docs/gpu-pipeline.md @@ -113,6 +113,15 @@ renderer last. `LIGHTCRAFT_GPU_BACKEND=dx12 | vulkan | gl | glow` (or `WGPU_BACK first because a broken Vulkan driver can crash the process uncatchably (#136). Compute keeps its own device and falls back to the CPU as before. +**The window's surface: one frame in flight.** The window's wgpu options start from eframe's defaults +(`NativeOptions::default().wgpu_options`, `window_gpu::base_options`), whose surface is egui-wgpu's +`SurfaceConfig::LOW_LATENCY`: vsync, at most one frame queued ahead of the display (two drawables on +Metal). egui-wgpu's bare `WgpuConfiguration::default()` is `HIGH_THROUGHPUT` (two frames queued, three +drawables), and the window used it from #136 until this change, so a drag reached the screen one frame +(8.3 ms at 120 Hz, 16.7 ms at 60 Hz) later. The window's own GPU work is small (egui's meshes and +texture uploads; renders run on the compute device), which is what `LOW_LATENCY` is meant for. +During a live window resize egui-wgpu still uses three drawables on macOS. + **Troubleshooting a crash at startup (Windows).** Start LightCraft from a `.cmd` file or a terminal with `set LIGHTCRAFT_GPU_BACKEND=dx12` (the default since #136), or `=off` to keep the GPU out of rendering; `set VK_LOADER_DRIVERS_DISABLE=*igvk64*` (Vulkan loader) hides a specific Vulkan driver diff --git a/docs/whats-new.md b/docs/whats-new.md index c6c259200..3c5a0b187 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -1,5 +1,15 @@ # What's new in LightCraft +## 2026-10-25 + +### Drags reach the screen a frame sooner +- The window queues one frame ahead of the display again instead of two, as eframe does by default (since #136 it + used egui-wgpu's high-throughput setting). Sliders, crop handles, brushes and every other drag show up one display + frame sooner: 8.3 ms at 120 Hz, 16.7 ms at 60 Hz. On a MacBook Pro at 120 Hz, a heavy develop drag went from + about 18 ms to about 10 ms between a frame being handed to macOS and macOS compositing it, with the same 120 frames + a second. On a Mac the window also holds one drawable less: its width × height × 4 bytes, 15–22 MB for a + 1200–1500-point-wide window on a Retina display. + ## 2026-10-24 ### Relative-date filters and smart albums follow the clock