diff --git a/src/app.rs b/src/app.rs index 0f2a525..f6ad1dd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1804,15 +1804,15 @@ impl LauncherApp { impl eframe::App for LauncherApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - // Present at a steady ~60 FPS while the window is open. egui is normally - // reactive and would idle at ~2 FPS (a 500 ms repaint), but on a G-Sync / - // FreeSync (VRR) display that sits below the VRR floor: the panel engages - // low-framerate compensation, and every hover/animation briefly spikes the - // rate and drops back, so the refresh rate keeps swinging and the panel - // visibly flickers. A constant, in-range cadence gives VRR a single rate to - // lock onto and removes the flicker. 16 ms is monitor-independent and cheap - // for a UI this small; vsync (on by default) keeps present times regular. - ctx.request_repaint_after(std::time::Duration::from_millis(16)); + // Render continuously (present every vsync) instead of reactively. egui + // normally idles at a low, bursty repaint rate; on a G-Sync / FreeSync + // (VRR) display a windowed app that presents in bursts with idle gaps + // makes DWM keep moving the window in and out of the VRR path and the + // refresh rate swing — which the panel shows as flicker. Presenting on + // every frame keeps the window continuously in VRR at the display's own + // (variable) refresh, which is stable. vsync (on by default) paces this to + // the monitor rather than spinning uncapped. + ctx.request_repaint(); self.drive_restart_queue(); egui::TopBottomPanel::top("header") diff --git a/src/main.rs b/src/main.rs index 9a0754a..61cfee9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,9 @@ fn main() -> eframe::Result<()> { .with_icon(app_icon()) .with_inner_size([1040.0, 720.0]) .with_min_inner_size([880.0, 600.0]), + // Pair vsync with the display's VRR (G-Sync + Vsync is the recommended + // combination): frames present on the monitor's own variable refresh. + vsync: true, ..Default::default() };