aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/display.rs
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2020-05-04 02:29:11 +0300
committerGitHub <noreply@github.com>2020-05-03 23:29:11 +0000
commit04f0bcaf54ed373128ca0f84ee8fcdd8e52bce23 (patch)
tree44f65d044e8c76d44e2cb1fb424a009cc4acc646 /alacritty/src/display.rs
parenta425fe6bfe734bb80dce334d6f1c53e0bd41c0db (diff)
downloadr-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.rs154
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.