| skipped 5 lines |
6 | 6 | | use std::mem::{self, ManuallyDrop}; |
7 | 7 | | use std::num::NonZeroU32; |
8 | 8 | | use std::ops::{Deref, DerefMut}; |
9 | | - | #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] |
10 | 9 | | use std::sync::atomic::Ordering; |
| 10 | + | use std::time::{Duration, Instant}; |
11 | 11 | | |
12 | 12 | | use glutin::context::{NotCurrentContext, PossiblyCurrentContext}; |
13 | 13 | | use glutin::prelude::*; |
14 | 14 | | use glutin::surface::{Rect as DamageRect, Surface, SwapInterval, WindowSurface}; |
15 | 15 | | |
16 | | - | use log::{debug, info, warn}; |
| 16 | + | use log::{debug, info}; |
17 | 17 | | use parking_lot::MutexGuard; |
18 | 18 | | use serde::{Deserialize, Serialize}; |
19 | 19 | | use winit::dpi::PhysicalSize; |
| skipped 26 lines |
46 | 46 | | use crate::display::hint::{HintMatch, HintState}; |
47 | 47 | | use crate::display::meter::Meter; |
48 | 48 | | use crate::display::window::Window; |
49 | | - | use crate::event::{Mouse, SearchState}; |
| 49 | + | use crate::event::{Event, EventType, Mouse, SearchState}; |
50 | 50 | | use crate::message_bar::{MessageBuffer, MessageType}; |
51 | 51 | | use crate::renderer::rects::{RenderLine, RenderLines, RenderRect}; |
52 | 52 | | use crate::renderer::{self, GlyphCache, Renderer}; |
| 53 | + | use crate::scheduler::{Scheduler, TimerId, Topic}; |
53 | 54 | | use crate::string::{ShortenDirection, StrShortener}; |
54 | 55 | | |
55 | 56 | | pub mod content; |
| skipped 311 lines |
367 | 368 | | /// The ime on the given display. |
368 | 369 | | pub ime: Ime, |
369 | 370 | | |
| 371 | + | /// The state of the timer for frame scheduling. |
| 372 | + | pub frame_timer: FrameTimer, |
| 373 | + | |
370 | 374 | | // Mouse point position when highlighting hints. |
371 | 375 | | hint_mouse_point: Option<Point>, |
372 | 376 | | |
| skipped 114 lines |
487 | 491 | | (Vec::new(), Vec::new()) |
488 | 492 | | }; |
489 | 493 | | |
490 | | - | // We use vsync everywhere except wayland. |
491 | | - | if !is_wayland { |
492 | | - | if let Err(err) = |
493 | | - | surface.set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) |
494 | | - | { |
495 | | - | warn!("Error setting vsync: {:?}", err); |
496 | | - | } |
| 494 | + | // Disable vsync. |
| 495 | + | if let Err(err) = surface.set_swap_interval(&context, SwapInterval::DontWait) { |
| 496 | + | info!("Failed to disable vsync: {}", err); |
497 | 497 | | } |
498 | 498 | | |
499 | 499 | | Ok(Self { |
| skipped 10 lines |
510 | 510 | | vi_highlighted_hint: None, |
511 | 511 | | is_wayland, |
512 | 512 | | cursor_hidden: false, |
| 513 | + | frame_timer: FrameTimer::new(), |
513 | 514 | | visual_bell: VisualBell::from(&config.bell), |
514 | 515 | | colors: List::from(&config.colors), |
515 | 516 | | pending_update: Default::default(), |
| skipped 235 lines |
751 | 752 | | pub fn draw<T: EventListener>( |
752 | 753 | | &mut self, |
753 | 754 | | mut terminal: MutexGuard<'_, Term<T>>, |
| 755 | + | scheduler: &mut Scheduler, |
754 | 756 | | message_buffer: &MessageBuffer, |
755 | 757 | | config: &UiConfig, |
756 | 758 | | search_state: &SearchState, |
| skipped 209 lines |
966 | 968 | | self.draw_hyperlink_preview(config, cursor_point, display_offset); |
967 | 969 | | } |
968 | 970 | | |
969 | | - | // Frame event should be requested before swaping buffers, since it requires surface |
970 | | - | // `commit`, which is done by swap buffers under the hood. |
971 | | - | #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] |
972 | | - | self.request_frame(&self.window); |
| 971 | + | // Frame event should be requested before swapping buffers on Wayland, since it requires |
| 972 | + | // surface `commit`, which is done by swap buffers under the hood. |
| 973 | + | if self.is_wayland { |
| 974 | + | self.request_frame(scheduler); |
| 975 | + | } |
973 | 976 | | |
974 | 977 | | // Clearing debug highlights from the previous frame requires full redraw. |
975 | 978 | | self.swap_buffers(); |
| skipped 6 lines |
982 | 985 | | self.renderer.finish(); |
983 | 986 | | } |
984 | 987 | | |
| 988 | + | // XXX: Request the new frame after swapping buffers, so the |
| 989 | + | // time to finish OpenGL operations is accounted for in the timeout. |
| 990 | + | if !self.is_wayland { |
| 991 | + | self.request_frame(scheduler); |
| 992 | + | } |
| 993 | + | |
985 | 994 | | self.damage_rects.clear(); |
986 | 995 | | |
987 | 996 | | // Append damage rects we've enqueued for the next frame. |
| skipped 388 lines |
1376 | 1385 | | } |
1377 | 1386 | | |
1378 | 1387 | | /// Requst a new frame for a window on Wayland. |
1379 | | - | #[inline] |
1380 | | - | #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] |
1381 | | - | fn request_frame(&self, window: &Window) { |
1382 | | - | let surface = match window.wayland_surface() { |
1383 | | - | Some(surface) => surface, |
1384 | | - | None => return, |
1385 | | - | }; |
| 1388 | + | fn request_frame(&mut self, scheduler: &mut Scheduler) { |
| 1389 | + | // Mark that we've used a frame. |
| 1390 | + | self.window.has_frame.store(false, Ordering::Relaxed); |
1386 | 1391 | | |
1387 | | - | let should_draw = self.window.should_draw.clone(); |
| 1392 | + | #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] |
| 1393 | + | if let Some(surface) = self.window.wayland_surface() { |
| 1394 | + | let has_frame = self.window.has_frame.clone(); |
| 1395 | + | // Request a new frame. |
| 1396 | + | surface.frame().quick_assign(move |_, _, _| { |
| 1397 | + | has_frame.store(true, Ordering::Relaxed); |
| 1398 | + | }); |
1388 | 1399 | | |
1389 | | - | // Mark that window was drawn. |
1390 | | - | should_draw.store(false, Ordering::Relaxed); |
| 1400 | + | return; |
| 1401 | + | } |
1391 | 1402 | | |
1392 | | - | // Request a new frame. |
1393 | | - | surface.frame().quick_assign(move |_, _, _| { |
1394 | | - | should_draw.store(true, Ordering::Relaxed); |
1395 | | - | }); |
| 1403 | + | // Get the display vblank interval. |
| 1404 | + | let monitor_vblank_interval = 1_000_000. |
| 1405 | + | / self |
| 1406 | + | .window |
| 1407 | + | .current_monitor() |
| 1408 | + | .and_then(|monitor| monitor.refresh_rate_millihertz()) |
| 1409 | + | .unwrap_or(60_000) as f64; |
| 1410 | + | |
| 1411 | + | // Now convert it to micro seconds. |
| 1412 | + | let monitor_vblank_interval = |
| 1413 | + | Duration::from_micros((1000. * monitor_vblank_interval) as u64); |
| 1414 | + | |
| 1415 | + | let swap_timeout = self.frame_timer.compute_timeout(monitor_vblank_interval); |
| 1416 | + | |
| 1417 | + | let window_id = self.window.id(); |
| 1418 | + | let timer_id = TimerId::new(Topic::Frame, window_id); |
| 1419 | + | let event = Event::new(EventType::Frame, window_id); |
| 1420 | + | |
| 1421 | + | scheduler.schedule(event, swap_timeout, false, timer_id); |
1396 | 1422 | | } |
1397 | 1423 | | } |
1398 | 1424 | | |
| skipped 132 lines |
1531 | 1557 | | impl<T> DerefMut for Replaceable<T> { |
1532 | 1558 | | fn deref_mut(&mut self) -> &mut Self::Target { |
1533 | 1559 | | self.get_mut() |
| 1560 | + | } |
| 1561 | + | } |
| 1562 | + | |
| 1563 | + | /// The frame timer state. |
| 1564 | + | pub struct FrameTimer { |
| 1565 | + | /// Base timestamp used to compute sync points. |
| 1566 | + | base: Instant, |
| 1567 | + | |
| 1568 | + | /// The last timestamp we synced to. |
| 1569 | + | last_synced_timestamp: Instant, |
| 1570 | + | |
| 1571 | + | /// The refresh rate we've used to compute sync timestamps. |
| 1572 | + | refresh_interval: Duration, |
| 1573 | + | } |
| 1574 | + | |
| 1575 | + | impl FrameTimer { |
| 1576 | + | pub fn new() -> Self { |
| 1577 | + | let now = Instant::now(); |
| 1578 | + | Self { base: now, last_synced_timestamp: now, refresh_interval: Duration::ZERO } |
| 1579 | + | } |
| 1580 | + | |
| 1581 | + | /// Compute the delay that we should use to achieve the target frame |
| 1582 | + | /// rate. |
| 1583 | + | pub fn compute_timeout(&mut self, refresh_interval: Duration) -> Duration { |
| 1584 | + | let now = Instant::now(); |
| 1585 | + | |
| 1586 | + | // Handle refresh rate change. |
| 1587 | + | if self.refresh_interval != refresh_interval { |
| 1588 | + | self.base = now; |
| 1589 | + | self.last_synced_timestamp = now; |
| 1590 | + | self.refresh_interval = refresh_interval; |
| 1591 | + | return refresh_interval; |
| 1592 | + | } |
| 1593 | + | |
| 1594 | + | let next_frame = self.last_synced_timestamp + self.refresh_interval; |
| 1595 | + | |
| 1596 | + | if next_frame < now { |
| 1597 | + | // Redraw immediately if we haven't drawn in over `refresh_interval` microseconds. |
| 1598 | + | let elapsed_micros = (now - self.base).as_micros() as u64; |
| 1599 | + | let refresh_micros = self.refresh_interval.as_micros() as u64; |
| 1600 | + | self.last_synced_timestamp = |
| 1601 | + | now - Duration::from_micros(elapsed_micros % refresh_micros); |
| 1602 | + | Duration::ZERO |
| 1603 | + | } else { |
| 1604 | + | // Redraw on the next `refresh_interval` clock tick. |
| 1605 | + | self.last_synced_timestamp = next_frame; |
| 1606 | + | next_frame - now |
| 1607 | + | } |
1534 | 1608 | | } |
1535 | 1609 | | } |
1536 | 1610 | | |
| skipped 32 lines |