Skip to content

Commit

Permalink
Fix flickering during resize on Wayland
Browse files Browse the repository at this point in the history
This also fixes an issue of windows not being rendered while resizing.

Fixes alacritty#6069.
  • Loading branch information
kchibisov authored Jun 9, 2022
1 parent 6dc670c commit 90552e3
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion alacritty/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
88 changes: 65 additions & 23 deletions alacritty/src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RendererUpdate>,

is_damage_supported: bool,
debug_damage: bool,
damage_rects: Vec<DamageRect>,
Expand All @@ -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<E>(
config: &UiConfig,
Expand Down Expand Up @@ -425,15 +441,15 @@ 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)
};

// 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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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<T>(
&mut self,
terminal: &mut Term<T>,
Expand All @@ -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);
Expand All @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion alacritty/src/renderer/text/gles2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// <https://github.com/servo/webrender/blob/master/webrender/doc/text-rendering.md>.
///
/// Rendering is split into three passes.
u_rendering_pass: GLint,
Expand Down
15 changes: 8 additions & 7 deletions alacritty/src/renderer/text/glyph_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<L: LoadGlyph>(&mut self, loader: &mut L) {
/// Reset currently cached data in both GL and the registry to default state.
pub fn reset_glyph_cache<L: LoadGlyph>(&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<L: LoadGlyph>(
/// 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);
Expand Down Expand Up @@ -304,8 +307,6 @@ impl GlyphCache {
self.metrics = metrics;
self.builtin_box_drawing = font.builtin_box_drawing;

self.clear_glyph_cache(loader);

Ok(())
}

Expand Down
3 changes: 3 additions & 0 deletions alacritty/src/window_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions alacritty_terminal/src/term/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.;
Expand Down

0 comments on commit 90552e3

Please sign in to comment.