From 90552e3e7f8f085919a39435a8a68b3a2f633e54 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 9 Jun 2022 19:31:08 +0300 Subject: [PATCH] Fix flickering during resize on Wayland This also fixes an issue of windows not being rendered while resizing. Fixes #6069. --- CHANGELOG.md | 1 + alacritty/src/cli.rs | 2 +- alacritty/src/display/mod.rs | 88 ++++++++++++++++------ alacritty/src/renderer/text/gles2.rs | 2 +- alacritty/src/renderer/text/glyph_cache.rs | 15 ++-- alacritty/src/window_context.rs | 3 + alacritty_terminal/src/term/color.rs | 4 +- 7 files changed, 81 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69d28921..46a9d77b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Bottom gap for certain builtin box drawing characters - Incorrect built-in glyphs for `U+2567` and `U+2568` - Character mappings in the DEC special graphics character set (line drawing) +- Window flickering on resize on Wayland ## 0.10.1 diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 3c64458b..83b317b9 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -237,7 +237,7 @@ pub struct WindowIdentity { } impl WindowIdentity { - /// Override the [`WindowIdentityConfig`]'s fields with the [`WindowOptions`]. + /// Override the [`WindowIdentity`]'s fields with the [`WindowOptions`]. pub fn override_identity_config(&self, identity: &mut Identity) { if let Some(title) = &self.title { identity.title = title.clone(); diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index eb7b8d53..99df511f 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -359,6 +359,9 @@ pub struct Display { /// Unprocessed display updates. pub pending_update: DisplayUpdate, + /// The renderer update that takes place only once before the actual rendering. + pub pending_renderer_update: Option, + is_damage_supported: bool, debug_damage: bool, damage_rects: Vec, @@ -367,6 +370,19 @@ pub struct Display { meter: Meter, } +/// Pending renderer updates. +/// +/// All renderer updates are cached to be applied just before rendering, to avoid platform-specific +/// rendering issues. +#[derive(Debug, Default, Copy, Clone)] +pub struct RendererUpdate { + /// Should resize the window. + resize: bool, + + /// Clear font caches. + clear_font_cache: bool, +} + impl Display { pub fn new( config: &UiConfig, @@ -425,7 +441,7 @@ impl Display { // If the scaling factor changed update the glyph cache and mark for resize. let should_resize = (estimated_scale_factor - window.scale_factor).abs() > f64::EPSILON; let (cell_width, cell_height) = if should_resize { - Self::update_glyph_cache(&mut renderer, &mut glyph_cache, scale_factor, config, font) + Self::update_font_size(&mut glyph_cache, scale_factor, config, font) } else { (cell_width, cell_height) }; @@ -433,7 +449,7 @@ impl Display { // Load font common glyphs to accelerate rendering. debug!("Filling glyph cache with common glyphs"); renderer.with_loader(|mut api| { - glyph_cache.load_common_glyphs(&mut api); + glyph_cache.reset_glyph_cache(&mut api); }); if let Some(dimensions) = dimensions.filter(|_| should_resize) { @@ -520,6 +536,7 @@ impl Display { visual_bell: VisualBell::from(&config.bell), colors: List::from(&config.colors), pending_update: Default::default(), + pending_renderer_update: Default::default(), is_damage_supported, debug_damage, damage_rects, @@ -529,30 +546,31 @@ impl Display { /// Update font size and cell dimensions. /// /// This will return a tuple of the cell width and height. - fn update_glyph_cache( - renderer: &mut Renderer, + fn update_font_size( glyph_cache: &mut GlyphCache, scale_factor: f64, config: &UiConfig, font: &Font, ) -> (f32, f32) { - renderer.with_loader(|mut api| { - let _ = glyph_cache.update_font_size(font, scale_factor, &mut api); - }); + let _ = glyph_cache.update_font_size(font, scale_factor); // Compute new cell sizes. compute_cell_size(config, &glyph_cache.font_metrics()) } - /// Clear glyph cache. - fn clear_glyph_cache(&mut self) { + /// Reset glyph cache. + fn reset_glyph_cache(&mut self) { let cache = &mut self.glyph_cache; self.renderer.with_loader(|mut api| { - cache.clear_glyph_cache(&mut api); + cache.reset_glyph_cache(&mut api); }); } /// Process update events. + /// + /// XXX: this function must not call to any `OpenGL` related tasks. Only logical update + /// of the state is being performed here. Rendering update takes part right before the + /// actual rendering. pub fn handle_update( &mut self, terminal: &mut Term, @@ -568,31 +586,29 @@ impl Display { let (mut cell_width, mut cell_height) = (self.size_info.cell_width(), self.size_info.cell_height()); - // Ensure we're modifying the correct OpenGL context. - self.window.make_current(); + if pending_update.font().is_some() || pending_update.cursor_dirty() { + let renderer_update = self.pending_renderer_update.get_or_insert(Default::default()); + renderer_update.clear_font_cache = true + } // Update font size and cell dimensions. if let Some(font) = pending_update.font() { let scale_factor = self.window.scale_factor; - let cell_dimensions = Self::update_glyph_cache( - &mut self.renderer, - &mut self.glyph_cache, - scale_factor, - config, - font, - ); + let cell_dimensions = + Self::update_font_size(&mut self.glyph_cache, scale_factor, config, font); cell_width = cell_dimensions.0; cell_height = cell_dimensions.1; info!("Cell size: {} x {}", cell_width, cell_height); - } else if pending_update.cursor_dirty() { - self.clear_glyph_cache(); } let (mut width, mut height) = (self.size_info.width(), self.size_info.height()); if let Some(dimensions) = pending_update.dimensions() { width = dimensions.width as f32; height = dimensions.height as f32; + + let renderer_update = self.pending_renderer_update.get_or_insert(Default::default()); + renderer_update.resize = true } let padding = config.window.padding(self.window.scale_factor); @@ -618,10 +634,36 @@ impl Display { // Resize terminal. terminal.resize(self.size_info); + } + + /// Update the state of the renderer. + /// + /// NOTE: The update to the renderer is split from the display update on purpose, since + /// on some platforms, like Wayland, resize and other OpenGL operations must be performed + /// right before rendering, otherwise they could lock the back buffer resulting in + /// rendering with the buffer of old size. + /// + /// This also resolves any flickering, since the resize is now synced with frame callbacks. + pub fn process_renderer_update(&mut self) { + let renderer_update = match self.pending_renderer_update.take() { + Some(renderer_update) => renderer_update, + _ => return, + }; // Resize renderer. - let physical = PhysicalSize::new(self.size_info.width() as _, self.size_info.height() as _); - self.window.resize(physical); + if renderer_update.resize { + let physical = + PhysicalSize::new(self.size_info.width() as _, self.size_info.height() as _); + self.window.resize(physical); + } + + // Ensure we're modifying the correct OpenGL context. + self.window.make_current(); + + if renderer_update.clear_font_cache { + self.reset_glyph_cache(); + } + self.renderer.resize(&self.size_info); if self.collect_damage() { diff --git a/alacritty/src/renderer/text/gles2.rs b/alacritty/src/renderer/text/gles2.rs index 32aaa173..01470813 100644 --- a/alacritty/src/renderer/text/gles2.rs +++ b/alacritty/src/renderer/text/gles2.rs @@ -463,7 +463,7 @@ pub struct TextShaderProgram { /// /// If GL_EXT_blend_func_extended is not available, the rendering is split into 4 passes. /// One is used for the background and the rest to perform subpixel text rendering according to - /// https://github.com/servo/webrender/blob/master/webrender/doc/text-rendering.md. + /// . /// /// Rendering is split into three passes. u_rendering_pass: GLint, diff --git a/alacritty/src/renderer/text/glyph_cache.rs b/alacritty/src/renderer/text/glyph_cache.rs index b57b10b7..72415900 100644 --- a/alacritty/src/renderer/text/glyph_cache.rs +++ b/alacritty/src/renderer/text/glyph_cache.rs @@ -264,19 +264,22 @@ impl GlyphCache { loader.load_glyph(&glyph) } - /// Clear currently cached data in both GL and the registry. - pub fn clear_glyph_cache(&mut self, loader: &mut L) { + /// Reset currently cached data in both GL and the registry to default state. + pub fn reset_glyph_cache(&mut self, loader: &mut L) { loader.clear(); - self.cache = HashMap::default(); + self.cache = Default::default(); self.load_common_glyphs(loader); } - pub fn update_font_size( + /// Update the inner font size. + /// + /// NOTE: To reload the renderers's fonts [`Self::reset_glyph_cache`] should be called + /// afterwards. + pub fn update_font_size( &mut self, font: &Font, scale_factor: f64, - loader: &mut L, ) -> Result<(), crossfont::Error> { // Update dpi scaling. self.rasterizer.update_dpr(scale_factor as f32); @@ -304,8 +307,6 @@ impl GlyphCache { self.metrics = metrics; self.builtin_box_drawing = font.builtin_box_drawing; - self.clear_glyph_cache(loader); - Ok(()) } diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs index 6ead6e4e..f04a8e04 100644 --- a/alacritty/src/window_context.rs +++ b/alacritty/src/window_context.rs @@ -325,6 +325,9 @@ impl WindowContext { } if self.dirty { + // Force the display to process any pending display update. + self.display.process_renderer_update(); + self.dirty = false; // Request immediate re-draw if visual bell animation is not finished yet. diff --git a/alacritty_terminal/src/term/color.rs b/alacritty_terminal/src/term/color.rs index a9494e7a..1cfdec6b 100644 --- a/alacritty_terminal/src/term/color.rs +++ b/alacritty_terminal/src/term/color.rs @@ -20,8 +20,8 @@ pub struct Rgb { } impl Rgb { - /// Implementation of W3C's luminance algorithm: - /// https://www.w3.org/TR/WCAG20/#relativeluminancedef + /// Implementation of W3C's luminance + /// [algorithm](https://www.w3.org/TR/WCAG20/#relativeluminancedef) fn luminance(self) -> f64 { let channel_luminance = |channel| { let channel = channel as f64 / 255.;