Projects STRLCPY alacritty Commits 2bd26fbe
🤬
  • ■ ■ ■ ■ ■ ■
    CHANGELOG.md
    skipped 30 lines
    31 31  - Crash while typing on Wayland
    32 32  - Multi-line semantic bracket selection
    33 33  - Reduced GPU memory usage
     34 +- Low frame rate when multiple windows render at the same time
     35 +- Redraw hanging until a keypress on X11 in rare cases
    34 36   
    35 37  ## 0.11.0
    36 38   
    skipped 1064 lines
  • ■ ■ ■ ■ ■
    alacritty/src/display/mod.rs
    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
  • ■ ■ ■ ■ ■ ■
    alacritty/src/display/window.rs
    1  -#[rustfmt::skip]
    2 1  #[cfg(not(any(target_os = "macos", windows)))]
    3  -use {
    4  - std::sync::atomic::AtomicBool,
    5  - std::sync::Arc,
    6  - 
    7  - winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix},
    8  -};
     2 +use winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
    9 3   
    10 4  #[rustfmt::skip]
    11 5  #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
    skipped 16 lines
    28 22  };
    29 23   
    30 24  use std::fmt::{self, Display, Formatter};
     25 +use std::sync::atomic::AtomicBool;
     26 +use std::sync::Arc;
    31 27   
    32 28  #[cfg(target_os = "macos")]
    33 29  use cocoa::base::{id, NO, YES};
    skipped 3 lines
    37 33   
    38 34  use winit::dpi::{PhysicalPosition, PhysicalSize};
    39 35  use winit::event_loop::EventLoopWindowTarget;
     36 +use winit::monitor::MonitorHandle;
    40 37  #[cfg(target_os = "macos")]
    41 38  use winit::platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS};
    42 39  #[cfg(windows)]
    skipped 63 lines
    106 103  ///
    107 104  /// Wraps the underlying windowing library to provide a stable API in Alacritty.
    108 105  pub struct Window {
    109  - /// Flag tracking frame redraw requests from Wayland compositor.
    110  - #[cfg(not(any(target_os = "macos", windows)))]
    111  - pub should_draw: Arc<AtomicBool>,
     106 + /// Flag tracking that we have a frame we can draw.
     107 + pub has_frame: Arc<AtomicBool>,
    112 108   
    113 109   /// Attached Wayland surface to request new frame events.
    114 110   #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
    skipped 79 lines
    194 190   mouse_visible: true,
    195 191   window,
    196 192   title: identity.title,
    197  - #[cfg(not(any(target_os = "macos", windows)))]
    198  - should_draw: Arc::new(AtomicBool::new(true)),
     193 + has_frame: Arc::new(AtomicBool::new(true)),
    199 194   #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
    200 195   wayland_surface,
    201 196   scale_factor,
    skipped 184 lines
    386 381   } else {
    387 382   self.window.set_fullscreen(None);
    388 383   }
     384 + }
     385 + 
     386 + pub fn current_monitor(&self) -> Option<MonitorHandle> {
     387 + self.window.current_monitor()
    389 388   }
    390 389   
    391 390   #[cfg(target_os = "macos")]
    skipped 78 lines
  • ■ ■ ■ ■ ■ ■
    alacritty/src/event.rs
    skipped 9 lines
    10 10  use std::os::unix::io::RawFd;
    11 11  use std::path::PathBuf;
    12 12  use std::rc::Rc;
     13 +use std::sync::atomic::Ordering;
    13 14  use std::time::{Duration, Instant};
    14 15  use std::{env, f32, mem};
    15 16   
    skipped 85 lines
    101 102   BlinkCursor,
    102 103   BlinkCursorTimeout,
    103 104   SearchNext,
     105 + Frame,
    104 106  }
    105 107   
    106 108  impl From<TerminalEvent> for EventType {
    skipped 989 lines
    1096 1098   
    1097 1099   self.ctx.window().scale_factor = scale_factor;
    1098 1100   },
     1101 + EventType::Frame => {
     1102 + self.ctx.display.window.has_frame.store(true, Ordering::Relaxed);
     1103 + },
    1099 1104   EventType::SearchNext => self.ctx.goto_match(None),
    1100 1105   EventType::Scroll(scroll) => self.ctx.scroll(scroll),
    1101 1106   EventType::BlinkCursor => {
    skipped 348 lines
    1450 1455   },
    1451 1456   // Process all pending events.
    1452 1457   WinitEvent::RedrawEventsCleared => {
    1453  - *control_flow = match scheduler.update() {
    1454  - Some(instant) => ControlFlow::WaitUntil(instant),
    1455  - None => ControlFlow::Wait,
    1456  - };
    1457  - 
    1458 1458   // Check for pending frame callbacks on Wayland.
    1459 1459   #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
    1460 1460   if let Some(wayland_event_queue) = self.wayland_event_queue.as_mut() {
    skipped 12 lines
    1473 1473   WinitEvent::RedrawEventsCleared,
    1474 1474   );
    1475 1475   }
     1476 + 
     1477 + // Update the scheduler after event processing to ensure
     1478 + // the event loop deadline is as accurate as possible.
     1479 + *control_flow = match scheduler.update() {
     1480 + Some(instant) => ControlFlow::WaitUntil(instant),
     1481 + None => ControlFlow::Wait,
     1482 + };
    1476 1483   },
    1477 1484   // Process config update.
    1478 1485   WinitEvent::UserEvent(Event { payload: EventType::ConfigReload(path), .. }) => {
    skipped 131 lines
  • ■ ■ ■ ■ ■
    alacritty/src/scheduler.rs
    skipped 27 lines
    28 28   DelayedSearch,
    29 29   BlinkCursor,
    30 30   BlinkTimeout,
     31 + Frame,
    31 32  }
    32 33   
    33 34  /// Event scheduled to be emitted at a specific time.
    skipped 77 lines
  • ■ ■ ■ ■ ■ ■
    alacritty/src/window_context.rs
    skipped 6 lines
    7 7  #[cfg(not(windows))]
    8 8  use std::os::unix::io::{AsRawFd, RawFd};
    9 9  use std::rc::Rc;
    10  -#[cfg(not(any(target_os = "macos", windows)))]
    11 10  use std::sync::atomic::Ordering;
    12 11  use std::sync::Arc;
    13 12   
    skipped 464 lines
    478 477   self.mouse.hint_highlight_dirty = false;
    479 478   }
    480 479   
    481  - // Skip rendering on Wayland until we get frame event from compositor.
    482  - #[cfg(not(any(target_os = "macos", windows)))]
    483  - if self.display.is_wayland && !self.display.window.should_draw.load(Ordering::Relaxed) {
     480 + // Skip rendering until we get a new frame.
     481 + if !self.display.window.has_frame.load(Ordering::Relaxed) {
    484 482   return;
    485 483   }
    486 484   
    skipped 8 lines
    495 493   self.display.window.request_redraw();
    496 494   }
    497 495   
    498  - // Redraw screen.
    499  - self.display.draw(terminal, &self.message_buffer, &self.config, &self.search_state);
     496 + // Redraw the window.
     497 + self.display.draw(
     498 + terminal,
     499 + scheduler,
     500 + &self.message_buffer,
     501 + &self.config,
     502 + &self.search_state,
     503 + );
    500 504   }
    501 505   }
    502 506   
    skipped 80 lines
Please wait...
Page is in error, reload to recover