如何让winit应用以稳定60FPS帧率运行?
解决winit 0.30.5稳定60FPS定时更新的问题
你的代码无法稳定60FPS的核心问题是:仅在main函数中设置了一次ControlFlow::WaitUntil,事件循环到达该时间点后没有更新下一个等待时间,导致后续只会进入ControlFlow::Wait状态,无法持续触发定时逻辑。此外,依赖RedrawRequested重复请求重绘的方式容易受系统窗口管理器限制,无法保证稳定帧率。
需要对ApplicationHandler实现添加以下修改:
具体修改步骤
- 在App结构体中添加计时字段:记录上次更新的时间,用于计算下一个60FPS的时间点。
- 实现
about_to_wait方法:这是winit新事件循环模式中,事件循环即将进入等待状态前的回调,适合设置下一次定时触发的时间并请求重绘/执行逻辑。 - 更新控制流逻辑:在
about_to_wait中动态设置下一个WaitUntil的时间点,确保事件循环每隔16.67ms(约60FPS)被唤醒一次。 - 调整RedrawRequested逻辑:移除重复调用
request_redraw(),改为由定时触发的about_to_wait主动请求重绘,避免不必要的系统交互。
修改后的完整代码
use std::time::{Duration, Instant}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowId}; const NANOS_PER_SEC: u64 = 1_000_000_000; const FRAME_DURATION: Duration = Duration::from_nanos(NANOS_PER_SEC / 60); #[derive(Default)] struct App { window: Option<Window>, last_update_time: Option<Instant>, } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap()); self.last_update_time = Some(Instant::now()); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { match event { WindowEvent::CloseRequested => { println!("The close button was pressed; stopping"); event_loop.exit(); } WindowEvent::RedrawRequested => { // 在这里执行渲染/逻辑更新 if let Some(last_time) = self.last_update_time { let elapsed = last_time.elapsed(); println!("FPS: {:>6.2}", 1.0 / elapsed.as_secs_f64()); self.last_update_time = Some(Instant::now()); } // 不需要重复调用request_redraw,由about_to_wait定时触发 } _ => () } } // 添加about_to_wait方法,处理定时逻辑 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { let next_update_time = self.last_update_time.unwrap_or_else(Instant::now) + FRAME_DURATION; event_loop.set_control_flow(ControlFlow::WaitUntil(next_update_time)); // 请求重绘,触发RedrawRequested事件 if let Some(window) = &self.window { window.request_redraw(); } } } fn main() { let event_loop = EventLoop::new().unwrap(); // 初始控制流设置为第一次等待时间 let initial_wait_time = Instant::now() + FRAME_DURATION; event_loop.set_control_flow(ControlFlow::WaitUntil(initial_wait_time)); let mut app = App::default(); event_loop.run_app(&mut app).unwrap(); }
关键修改说明
about_to_wait方法:这是winit 0.29+版本引入的新回调,专门用于处理事件循环空闲前的逻辑。在这里动态更新下一次唤醒的时间,确保每帧间隔稳定在~16.67ms。- 帧间隔常量:定义
FRAME_DURATION统一管理60FPS的时间间隔,避免重复计算。 - 移除冗余的
request_redraw():原代码在RedrawRequested中重复请求重绘可能导致系统额外的事件调度,改为由定时逻辑主动触发,更可控。 - 初始化
last_update_time:在resumed方法中初始化计时起点,避免首次计算出现错误。
这样修改后,事件循环会稳定每隔约16.67ms唤醒一次,触发about_to_wait并请求重绘,最终实现稳定的60FPS逻辑运行。
内容的提问来源于stack exchange,提问作者TwiceUponATime
相关产品推荐
相关产品推荐

