diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2020-05-04 02:29:11 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-03 23:29:11 +0000 |
commit | 04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23 (patch) | |
tree | 44f65d044e8c76d44e2cb1fb424a009cc4acc646 /alacritty/src/display.rs | |
parent | a425fe6bfe734bb80dce334d6f1c53e0bd41c0db (diff) | |
download | r-alacritty-04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23.tar.gz r-alacritty-04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23.tar.bz2 r-alacritty-04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23.zip |
Use frame callbacks instead of vsync on Wayland
Instead of blocking on vsync, Alacritty now requests a notification from
wayland about when the next frame should be rendered. this helps with
input latency, since it gives alacritty more time to process events
before a redraw. it also prevents alacritty from drawing unless the
compositor tells it to do so.
Fixes #2851.
Diffstat (limited to 'alacritty/src/display.rs')
-rw-r--r-- | alacritty/src/display.rs | 154 |
1 files changed, 102 insertions, 52 deletions
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index 980b0ea0..aabd2631 100644 --- a/alacritty/src/display.rs +++ b/alacritty/src/display.rs @@ -16,6 +16,8 @@ //! GPU drawing. use std::f64; use std::fmt::{self, Formatter}; +#[cfg(not(any(target_os = "macos", windows)))] +use std::sync::atomic::Ordering; use std::time::Instant; use glutin::dpi::{PhysicalPosition, PhysicalSize}; @@ -26,6 +28,8 @@ use glutin::platform::unix::EventLoopWindowTargetExtUnix; use glutin::window::CursorIcon; use log::{debug, info}; use parking_lot::MutexGuard; +#[cfg(not(any(target_os = "macos", windows)))] +use wayland_client::{Display as WaylandDisplay, EventQueue}; use font::{self, Rasterize}; @@ -47,16 +51,16 @@ use crate::window::{self, Window}; #[derive(Debug)] pub enum Error { - /// Error with window management + /// Error with window management. Window(window::Error), - /// Error dealing with fonts + /// Error dealing with fonts. Font(font::Error), - /// Error in renderer + /// Error in renderer. Render(renderer::Error), - /// Error during buffer swap + /// Error during buffer swap. ContextError(glutin::ContextError), } @@ -106,7 +110,7 @@ impl From<glutin::ContextError> for Error { } } -/// The display wraps a window, font rasterizer, and GPU renderer +/// The display wraps a window, font rasterizer, and GPU renderer. pub struct Display { pub size_info: SizeInfo, pub window: Window, @@ -115,6 +119,9 @@ pub struct Display { /// Currently highlighted URL. pub highlighted_url: Option<Url>, + #[cfg(not(any(target_os = "macos", windows)))] + pub wayland_event_queue: Option<EventQueue>, + renderer: QuadRenderer, glyph_cache: GlyphCache, meter: Meter, @@ -124,11 +131,11 @@ pub struct Display { impl Display { pub fn new(config: &Config, event_loop: &EventLoop<Event>) -> Result<Display, Error> { - // Guess DPR based on first monitor + // Guess DPR based on first monitor. let estimated_dpr = event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.); - // Guess the target window dimensions + // Guess the target window dimensions. let metrics = GlyphCache::static_metrics(config.font.clone(), estimated_dpr)?; let (cell_width, cell_height) = compute_cell_size(config, &metrics); let dimensions = @@ -138,19 +145,37 @@ impl Display { debug!("Estimated Cell Size: {} x {}", cell_width, cell_height); debug!("Estimated Dimensions: {:?}", dimensions); - // Create the window where Alacritty will be displayed + #[cfg(not(any(target_os = "macos", windows)))] + let mut wayland_event_queue = None; + + // Initialize Wayland event queue, to handle Wayland callbacks. + #[cfg(not(any(target_os = "macos", windows)))] + { + if let Some(display) = event_loop.wayland_display() { + let display = unsafe { WaylandDisplay::from_external_display(display as _) }; + wayland_event_queue = Some(display.create_event_queue()); + } + } + + // Create the window where Alacritty will be displayed. let size = dimensions.map(|(width, height)| PhysicalSize::new(width, height)); - // Spawn window - let mut window = Window::new(event_loop, &config, size)?; + // Spawn window. + let mut window = Window::new( + event_loop, + &config, + size, + #[cfg(not(any(target_os = "macos", windows)))] + wayland_event_queue.as_ref(), + )?; let dpr = window.scale_factor(); info!("Device pixel ratio: {}", dpr); - // get window properties for initializing the other subsystems + // get window properties for initializing the other subsystems. let viewport_size = window.inner_size(); - // Create renderer + // Create renderer. let mut renderer = QuadRenderer::new()?; let (glyph_cache, cell_width, cell_height) = @@ -169,7 +194,7 @@ impl Display { window.set_inner_size(PhysicalSize::new(width, height)); } } else if config.window.dynamic_padding { - // Make sure additional padding is spread evenly + // Make sure additional padding is spread evenly. padding_x = dynamic_padding(padding_x, viewport_size.width as f32, cell_width); padding_y = dynamic_padding(padding_y, viewport_size.height as f32, cell_height); } @@ -180,7 +205,7 @@ impl Display { info!("Cell Size: {} x {}", cell_width, cell_height); info!("Padding: {} x {}", padding_x, padding_y); - // Create new size with at least one column and row + // Create new size with at least one column and row. let size_info = SizeInfo { dpr, width: (viewport_size.width as f32).max(cell_width + 2. * padding_x), @@ -191,10 +216,10 @@ impl Display { padding_y, }; - // Update OpenGL projection + // Update OpenGL projection. renderer.resize(&size_info); - // Clear screen + // Call `clear` before showing the window, to make sure the surface is initialized. let background_color = config.colors.primary.background; renderer.with_api(&config, &size_info, |api| { api.clear(background_color); @@ -203,12 +228,10 @@ impl Display { #[cfg(not(any(target_os = "macos", windows)))] let is_x11 = event_loop.is_x11(); - // We should call `clear` when window is offscreen, so when `window.show()` happens it - // would be with background color instead of uninitialized surface. #[cfg(not(any(target_os = "macos", windows)))] { // On Wayland we can safely ignore this call, since the window isn't visible until you - // actually draw something into it. + // actually draw something into it and commit those changes. if is_x11 { window.swap_buffers(); renderer.with_api(&config, &size_info, |api| { @@ -219,10 +242,10 @@ impl Display { window.set_visible(true); - // Set window position + // Set window position. // // TODO: replace `set_position` with `with_position` once available - // Upstream issue: https://github.com/rust-windowing/winit/issues/806 + // Upstream issue: https://github.com/rust-windowing/winit/issues/806. if let Some(position) = config.window.position { window.set_outer_position(PhysicalPosition::from((position.x, position.y))); } @@ -247,6 +270,8 @@ impl Display { highlighted_url: None, #[cfg(not(any(target_os = "macos", windows)))] is_x11, + #[cfg(not(any(target_os = "macos", windows)))] + wayland_event_queue, }) } @@ -258,7 +283,7 @@ impl Display { let font = config.font.clone(); let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; - // Initialize glyph cache + // Initialize glyph cache. let glyph_cache = { info!("Initializing glyph cache..."); let init_start = Instant::now(); @@ -281,7 +306,7 @@ impl Display { Ok((glyph_cache, cw, ch)) } - /// Update font size and cell dimensions + /// Update font size and cell dimensions. fn update_glyph_cache(&mut self, config: &Config, font: Font) { let size_info = &mut self.size_info; let cache = &mut self.glyph_cache; @@ -290,7 +315,7 @@ impl Display { let _ = cache.update_font_size(font, size_info.dpr, &mut api); }); - // Update cell size + // Update cell size. let (cell_width, cell_height) = compute_cell_size(config, &self.glyph_cache.font_metrics()); size_info.cell_width = cell_width; size_info.cell_height = cell_height; @@ -313,7 +338,7 @@ impl Display { config: &Config, update_pending: DisplayUpdate, ) { - // Update font size and cell dimensions + // Update font size and cell dimensions. if let Some(font) = update_pending.font { self.update_glyph_cache(config, font); } else if update_pending.cursor { @@ -323,18 +348,18 @@ impl Display { let cell_width = self.size_info.cell_width; let cell_height = self.size_info.cell_height; - // Recalculate padding + // Recalculate padding. let mut padding_x = f32::from(config.window.padding.x) * self.size_info.dpr as f32; let mut padding_y = f32::from(config.window.padding.y) * self.size_info.dpr as f32; - // Update the window dimensions + // Update the window dimensions. if let Some(size) = update_pending.dimensions { - // Ensure we have at least one column and row + // Ensure we have at least one column and row. self.size_info.width = (size.width as f32).max(cell_width + 2. * padding_x); self.size_info.height = (size.height as f32).max(cell_height + 2. * padding_y); } - // Distribute excess padding equally on all sides + // Distribute excess padding equally on all sides. if config.window.dynamic_padding { padding_x = dynamic_padding(padding_x, self.size_info.width, cell_width); padding_y = dynamic_padding(padding_y, self.size_info.height, cell_height); @@ -345,29 +370,29 @@ impl Display { let mut pty_size = self.size_info; - // Subtract message bar lines from pty size + // Subtract message bar lines from pty size. if let Some(message) = message_buffer.message() { let lines = message.text(&self.size_info).len(); pty_size.height -= pty_size.cell_height * lines as f32; } - // Resize PTY + // Resize PTY. pty_resize_handle.on_resize(&pty_size); - // Resize terminal + // Resize terminal. terminal.resize(&pty_size); - // Resize renderer + // Resize renderer. let physical = PhysicalSize::new(self.size_info.width as u32, self.size_info.height as u32); self.window.resize(physical); self.renderer.resize(&self.size_info); } - /// Draw the screen + /// Draw the screen. /// /// A reference to Term whose state is being drawn must be provided. /// - /// This call may block if vsync is enabled + /// This call may block if vsync is enabled. pub fn draw<T>( &mut self, terminal: MutexGuard<'_, Term<T>>, @@ -393,11 +418,11 @@ impl Display { None }; - // Update IME position + // Update IME position. #[cfg(not(windows))] self.window.update_ime_position(&terminal, &self.size_info); - // Drop terminal as early as possible to free lock + // Drop terminal as early as possible to free lock. drop(terminal); self.renderer.with_api(&config, &size_info, |api| { @@ -407,20 +432,20 @@ impl Display { let mut lines = RenderLines::new(); let mut urls = Urls::new(); - // Draw grid + // Draw grid. { let _sampler = self.meter.sampler(); self.renderer.with_api(&config, &size_info, |mut api| { - // Iterate over all non-empty cells in the grid + // Iterate over all non-empty cells in the grid. for cell in grid_cells { - // Update URL underlines + // Update URL underlines. urls.update(size_info.cols().0, cell); - // Update underline/strikeout + // Update underline/strikeout. lines.update(cell); - // Draw the cell + // Draw the cell. api.render_cell(cell, glyph_cache); } }); @@ -428,7 +453,7 @@ impl Display { let mut rects = lines.rects(&metrics, &size_info); - // Update visible URLs + // Update visible URLs. self.urls = urls; if let Some(url) = self.urls.highlighted(config, mouse, mods, mouse_mode, selection) { rects.append(&mut url.rects(&metrics, &size_info)); @@ -446,14 +471,14 @@ impl Display { } } - // Highlight URLs at the vi mode cursor position + // Highlight URLs at the vi mode cursor position. if let Some(vi_mode_cursor) = vi_mode_cursor { if let Some(url) = self.urls.find_at(vi_mode_cursor.point) { rects.append(&mut url.rects(&metrics, &size_info)); } } - // Push visual bell after url/underline/strikeout rects + // Push visual bell after url/underline/strikeout rects. if visual_bell_intensity != 0. { let visual_bell_rect = RenderRect::new( 0., @@ -469,19 +494,19 @@ impl Display { if let Some(message) = message_buffer.message() { let text = message.text(&size_info); - // Create a new rectangle for the background + // Create a new rectangle for the background. let start_line = size_info.lines().0 - text.len(); let y = size_info.cell_height.mul_add(start_line as f32, size_info.padding_y); let message_bar_rect = RenderRect::new(0., y, size_info.width, size_info.height - y, message.color(), 1.); - // Push message_bar in the end, so it'll be above all other content + // Push message_bar in the end, so it'll be above all other content. rects.push(message_bar_rect); - // Draw rectangles + // Draw rectangles. self.renderer.draw_rects(&size_info, rects); - // Relay messages to the user + // Relay messages to the user. let mut offset = 1; for message_text in text.iter().rev() { self.renderer.with_api(&config, &size_info, |mut api| { @@ -495,11 +520,11 @@ impl Display { offset += 1; } } else { - // Draw rectangles + // Draw rectangles. self.renderer.draw_rects(&size_info, rects); } - // Draw render timer + // Draw render timer. if config.render_timer() { let timing = format!("{:.3} usec", self.meter.average()); let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; @@ -508,6 +533,11 @@ impl Display { }); } + // Frame event should be requested before swaping buffers, since it requires surface + // `commit`, which is done by swap buffers under the hood. + #[cfg(not(any(target_os = "macos", windows)))] + self.request_frame(&self.window); + self.window.swap_buffers(); #[cfg(not(any(target_os = "macos", windows)))] @@ -522,9 +552,29 @@ impl Display { } } } + + /// Requst a new frame for a window on Wayland. + #[inline] + #[cfg(not(any(target_os = "macos", windows)))] + fn request_frame(&self, window: &Window) { + let surface = match window.wayland_surface() { + Some(surface) => surface, + None => return, + }; + + let should_draw = self.window.should_draw.clone(); + + // Mark that window was drawn. + should_draw.store(false, Ordering::Relaxed); + + // Request a new frame. + surface.frame().quick_assign(move |_, _, _| { + should_draw.store(true, Ordering::Relaxed); + }); + } } -/// Calculate padding to spread it evenly around the terminal content +/// Calculate padding to spread it evenly around the terminal content. #[inline] fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 { padding + ((dimension - 2. * padding) % cell_dimension) / 2. |