Don't load font twice during display creation

This commit finishes the effort from a64553b to avoid reloading font
twice during startup, since the original issue is with getting font
metrics without building the glyph cache.
This commit is contained in:
Kirill Chibisov 2022-02-18 01:27:10 +03:00 committed by GitHub
parent aaab88c5c5
commit 4734b2b850
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 109 deletions

View File

@ -85,9 +85,9 @@ impl WindowConfig {
} }
#[inline] #[inline]
pub fn padding(&self, dpr: f64) -> (f32, f32) { pub fn padding(&self, scale_factor: f64) -> (f32, f32) {
let padding_x = (f32::from(self.padding.x) * dpr as f32).floor(); let padding_x = (f32::from(self.padding.x) * scale_factor as f32).floor();
let padding_y = (f32::from(self.padding.y) * dpr as f32).floor(); let padding_y = (f32::from(self.padding.y) * scale_factor as f32).floor();
(padding_x, padding_y) (padding_x, padding_y)
} }

View File

@ -5,7 +5,6 @@ use std::convert::TryFrom;
use std::fmt::{self, Formatter}; use std::fmt::{self, Formatter};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::time::Instant;
use std::{cmp, mem}; use std::{cmp, mem};
use glutin::dpi::PhysicalSize; use glutin::dpi::PhysicalSize;
@ -223,26 +222,29 @@ impl Display {
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
let is_x11 = event_loop.is_x11(); let is_x11 = event_loop.is_x11();
// Guess DPR based on first monitor. On Wayland the initial frame always renders at a DPR // Guess scale_factor based on first monitor. On Wayland the initial frame always renders at
// of 1. // a scale factor of 1.
let estimated_dpr = if cfg!(any(target_os = "macos", windows)) || is_x11 { let estimated_scale_factor = if cfg!(any(target_os = "macos", windows)) || is_x11 {
event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.) event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.)
} else { } else {
1. 1.
}; };
// Guess the target window dimensions. // Guess the target window dimensions.
let mut rasterizer = Rasterizer::new(estimated_dpr as f32, config.font.use_thin_strokes)?; debug!("Loading \"{}\" font", &config.font.normal().family);
let metrics = GlyphCache::static_metrics(&mut rasterizer, config.font.clone())?; let font = &config.font;
let rasterizer = Rasterizer::new(estimated_scale_factor as f32, font.use_thin_strokes)?;
let mut glyph_cache = GlyphCache::new(rasterizer, font)?;
let metrics = glyph_cache.font_metrics();
let (cell_width, cell_height) = compute_cell_size(config, &metrics); let (cell_width, cell_height) = compute_cell_size(config, &metrics);
// Guess the target window size if the user has specified the number of lines/columns. // Guess the target window size if the user has specified the number of lines/columns.
let dimensions = config.window.dimensions(); let dimensions = config.window.dimensions();
let estimated_size = dimensions.map(|dimensions| { let estimated_size = dimensions.map(|dimensions| {
window_size(config, dimensions, cell_width, cell_height, estimated_dpr) window_size(config, dimensions, cell_width, cell_height, estimated_scale_factor)
}); });
debug!("Estimated DPR: {}", estimated_dpr); debug!("Estimated scaling factor: {}", estimated_scale_factor);
debug!("Estimated window size: {:?}", estimated_size); debug!("Estimated window size: {:?}", estimated_size);
debug!("Estimated cell size: {} x {}", cell_width, cell_height); debug!("Estimated cell size: {} x {}", cell_width, cell_height);
@ -256,26 +258,34 @@ impl Display {
wayland_event_queue, wayland_event_queue,
)?; )?;
info!("Device pixel ratio: {}", window.dpr);
rasterizer.update_dpr(window.dpr as f32);
// Create renderer. // Create renderer.
let mut renderer = QuadRenderer::new()?; let mut renderer = QuadRenderer::new()?;
let (glyph_cache, cell_width, cell_height) = let scale_factor = window.scale_factor;
Self::new_glyph_cache(rasterizer, &mut renderer, config)?; info!("Display scale factor: {}", scale_factor);
if let Some(dimensions) = dimensions { // If the scaling factor changed update the glyph cache and mark for resize.
if (estimated_dpr - window.dpr).abs() < f64::EPSILON { let should_resize = (estimated_scale_factor - window.scale_factor).abs() > f64::EPSILON;
info!("Estimated DPR correctly, skipping resize"); let (cell_width, cell_height) = if should_resize {
} else { Self::update_glyph_cache(&mut renderer, &mut glyph_cache, scale_factor, config, font)
// Resize the window again if the DPR was not estimated correctly. } else {
let size = window_size(config, dimensions, cell_width, cell_height, window.dpr); (cell_width, cell_height)
window.set_inner_size(size); };
}
// 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);
});
if let Some(dimensions) = dimensions.filter(|_| should_resize) {
// Resize the window again if the scale factor was not estimated correctly.
let size =
window_size(config, dimensions, cell_width, cell_height, window.scale_factor);
window.set_inner_size(size);
} }
let padding = config.window.padding(window.dpr); let padding = config.window.padding(window.scale_factor);
let viewport_size = window.inner_size(); let viewport_size = window.inner_size();
// Create new size with at least one column and row. // Create new size with at least one column and row.
@ -362,49 +372,22 @@ impl Display {
}) })
} }
fn new_glyph_cache(
rasterizer: Rasterizer,
renderer: &mut QuadRenderer,
config: &UiConfig,
) -> Result<(GlyphCache, f32, f32), Error> {
let font = config.font.clone();
// Initialize glyph cache.
let glyph_cache = {
info!("Initializing glyph cache...");
let init_start = Instant::now();
let cache =
renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
let stop = init_start.elapsed();
let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
info!("... finished initializing glyph cache in {}s", stop_f);
cache
};
// Need font metrics to resize the window properly. This suggests to me the
// font metrics should be computed before creating the window in the first
// place so that a resize is not needed.
let (cw, ch) = compute_cell_size(config, &glyph_cache.font_metrics());
Ok((glyph_cache, cw, ch))
}
/// Update font size and cell dimensions. /// Update font size and cell dimensions.
/// ///
/// This will return a tuple of the cell width and height. /// This will return a tuple of the cell width and height.
fn update_glyph_cache(&mut self, config: &UiConfig, font: &Font) -> (f32, f32) { fn update_glyph_cache(
let cache = &mut self.glyph_cache; renderer: &mut QuadRenderer,
let dpr = self.window.dpr; glyph_cache: &mut GlyphCache,
scale_factor: f64,
self.renderer.with_loader(|mut api| { config: &UiConfig,
let _ = cache.update_font_size(font, dpr, &mut api); font: &Font,
) -> (f32, f32) {
renderer.with_loader(|mut api| {
let _ = glyph_cache.update_font_size(font, scale_factor, &mut api);
}); });
// Compute new cell sizes. // Compute new cell sizes.
compute_cell_size(config, &self.glyph_cache.font_metrics()) compute_cell_size(config, &glyph_cache.font_metrics())
} }
/// Clear glyph cache. /// Clear glyph cache.
@ -436,7 +419,14 @@ impl Display {
// Update font size and cell dimensions. // Update font size and cell dimensions.
if let Some(font) = pending_update.font() { if let Some(font) = pending_update.font() {
let cell_dimensions = self.update_glyph_cache(config, 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,
);
cell_width = cell_dimensions.0; cell_width = cell_dimensions.0;
cell_height = cell_dimensions.1; cell_height = cell_dimensions.1;
@ -451,7 +441,7 @@ impl Display {
height = dimensions.height as f32; height = dimensions.height as f32;
} }
let padding = config.window.padding(self.window.dpr); let padding = config.window.padding(self.window.scale_factor);
self.size_info = SizeInfo::new( self.size_info = SizeInfo::new(
width, width,
@ -1021,9 +1011,9 @@ fn window_size(
dimensions: Dimensions, dimensions: Dimensions,
cell_width: f32, cell_width: f32,
cell_height: f32, cell_height: f32,
dpr: f64, scale_factor: f64,
) -> PhysicalSize<u32> { ) -> PhysicalSize<u32> {
let padding = config.window.padding(dpr); let padding = config.window.padding(scale_factor);
let grid_width = cell_width * dimensions.columns.0.max(MIN_COLUMNS) as f32; let grid_width = cell_width * dimensions.columns.0.max(MIN_COLUMNS) as f32;
let grid_height = cell_height * dimensions.lines.max(MIN_SCREEN_LINES) as f32; let grid_height = cell_height * dimensions.lines.max(MIN_SCREEN_LINES) as f32;

View File

@ -151,8 +151,8 @@ pub struct Window {
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
pub wayland_surface: Option<Attached<WlSurface>>, pub wayland_surface: Option<Attached<WlSurface>>,
/// Cached DPR for quickly scaling pixel sizes. /// Cached scale factor for quickly scaling pixel sizes.
pub dpr: f64, pub scale_factor: f64,
/// Current window title. /// Current window title.
title: String, title: String,
@ -219,7 +219,7 @@ impl Window {
None None
}; };
let dpr = windowed_context.window().scale_factor(); let scale_factor = windowed_context.window().scale_factor();
Ok(Self { Ok(Self {
current_mouse_cursor, current_mouse_cursor,
@ -230,7 +230,7 @@ impl Window {
should_draw: Arc::new(AtomicBool::new(true)), should_draw: Arc::new(AtomicBool::new(true)),
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_surface, wayland_surface,
dpr, scale_factor,
}) })
} }

View File

@ -83,7 +83,7 @@ impl From<Event> for GlutinEvent<'_, Event> {
/// Alacritty events. /// Alacritty events.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum EventType { pub enum EventType {
DprChanged(f64, (u32, u32)), ScaleFactorChanged(f64, (u32, u32)),
Terminal(TerminalEvent), Terminal(TerminalEvent),
ConfigReload(PathBuf), ConfigReload(PathBuf),
Message(Message), Message(Message),
@ -1026,17 +1026,17 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
pub fn handle_event(&mut self, event: GlutinEvent<'_, Event>) { pub fn handle_event(&mut self, event: GlutinEvent<'_, Event>) {
match event { match event {
GlutinEvent::UserEvent(Event { payload, .. }) => match payload { GlutinEvent::UserEvent(Event { payload, .. }) => match payload {
EventType::DprChanged(scale_factor, (width, height)) => { EventType::ScaleFactorChanged(scale_factor, (width, height)) => {
let display_update_pending = &mut self.ctx.display.pending_update; let display_update_pending = &mut self.ctx.display.pending_update;
// Push current font to update its DPR. // Push current font to update its scale factor.
let font = self.ctx.config.font.clone(); let font = self.ctx.config.font.clone();
display_update_pending.set_font(font.with_size(*self.ctx.font_size)); display_update_pending.set_font(font.with_size(*self.ctx.font_size));
// Resize to event's dimensions, since no resize event is emitted on Wayland. // Resize to event's dimensions, since no resize event is emitted on Wayland.
display_update_pending.set_dimensions(PhysicalSize::new(width, height)); display_update_pending.set_dimensions(PhysicalSize::new(width, height));
self.ctx.window().dpr = scale_factor; self.ctx.window().scale_factor = scale_factor;
*self.ctx.dirty = true; *self.ctx.dirty = true;
}, },
EventType::SearchNext => self.ctx.goto_match(None), EventType::SearchNext => self.ctx.goto_match(None),

View File

@ -923,14 +923,14 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
/// Handle automatic scrolling when selecting above/below the window. /// Handle automatic scrolling when selecting above/below the window.
fn update_selection_scrolling(&mut self, mouse_y: i32) { fn update_selection_scrolling(&mut self, mouse_y: i32) {
let dpr = self.ctx.window().dpr; let scale_factor = self.ctx.window().scale_factor;
let size = self.ctx.size_info(); let size = self.ctx.size_info();
let window_id = self.ctx.window().id(); let window_id = self.ctx.window().id();
let scheduler = self.ctx.scheduler_mut(); let scheduler = self.ctx.scheduler_mut();
// Scale constants by DPI. // Scale constants by DPI.
let min_height = (MIN_SELECTION_SCROLLING_HEIGHT * dpr) as i32; let min_height = (MIN_SELECTION_SCROLLING_HEIGHT * scale_factor) as i32;
let step = (SELECTION_SCROLLING_STEP * dpr) as i32; let step = (SELECTION_SCROLLING_STEP * scale_factor) as i32;
// Compute the height of the scrolling areas. // Compute the height of the scrolling areas.
let end_top = max(min_height, size.padding_y() as i32); let end_top = max(min_height, size.padding_y() as i32);

View File

@ -156,14 +156,7 @@ pub struct GlyphCache {
} }
impl GlyphCache { impl GlyphCache {
pub fn new<L>( pub fn new(mut rasterizer: Rasterizer, font: &Font) -> Result<GlyphCache, crossfont::Error> {
mut rasterizer: Rasterizer,
font: &Font,
loader: &mut L,
) -> Result<GlyphCache, crossfont::Error>
where
L: LoadGlyph,
{
let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?; let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?;
// Need to load at least one glyph for the face before calling metrics. // Need to load at least one glyph for the face before calling metrics.
@ -173,7 +166,7 @@ impl GlyphCache {
let metrics = rasterizer.metrics(regular, font.size())?; let metrics = rasterizer.metrics(regular, font.size())?;
let mut cache = Self { Ok(Self {
cache: HashMap::default(), cache: HashMap::default(),
rasterizer, rasterizer,
font_size: font.size(), font_size: font.size(),
@ -185,11 +178,7 @@ impl GlyphCache {
glyph_offset: font.glyph_offset, glyph_offset: font.glyph_offset,
metrics, metrics,
builtin_box_drawing: font.builtin_box_drawing, builtin_box_drawing: font.builtin_box_drawing,
}; })
cache.load_common_glyphs(loader);
Ok(cache)
} }
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) { fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
@ -358,11 +347,11 @@ impl GlyphCache {
pub fn update_font_size<L: LoadGlyph>( pub fn update_font_size<L: LoadGlyph>(
&mut self, &mut self,
font: &Font, font: &Font,
dpr: f64, scale_factor: f64,
loader: &mut L, loader: &mut L,
) -> Result<(), crossfont::Error> { ) -> Result<(), crossfont::Error> {
// Update dpi scaling. // Update dpi scaling.
self.rasterizer.update_dpr(dpr as f32); self.rasterizer.update_dpr(scale_factor as f32);
self.font_offset = font.offset; self.font_offset = font.offset;
// Recompute font keys. // Recompute font keys.
@ -376,7 +365,7 @@ impl GlyphCache {
})?; })?;
let metrics = self.rasterizer.metrics(regular, font.size())?; let metrics = self.rasterizer.metrics(regular, font.size())?;
info!("Font size changed to {:?} with DPR of {}", font.size(), dpr); info!("Font size changed to {:?} with scale factor of {}", font.size(), scale_factor);
self.font_size = font.size(); self.font_size = font.size();
self.font_key = regular; self.font_key = regular;
@ -396,24 +385,12 @@ impl GlyphCache {
} }
/// Prefetch glyphs that are almost guaranteed to be loaded anyways. /// Prefetch glyphs that are almost guaranteed to be loaded anyways.
fn load_common_glyphs<L: LoadGlyph>(&mut self, loader: &mut L) { pub fn load_common_glyphs<L: LoadGlyph>(&mut self, loader: &mut L) {
self.load_glyphs_for_font(self.font_key, loader); self.load_glyphs_for_font(self.font_key, loader);
self.load_glyphs_for_font(self.bold_key, loader); self.load_glyphs_for_font(self.bold_key, loader);
self.load_glyphs_for_font(self.italic_key, loader); self.load_glyphs_for_font(self.italic_key, loader);
self.load_glyphs_for_font(self.bold_italic_key, loader); self.load_glyphs_for_font(self.bold_italic_key, loader);
} }
/// Calculate font metrics without access to a glyph cache.
pub fn static_metrics(
rasterizer: &mut crossfont::Rasterizer,
font: Font,
) -> Result<crossfont::Metrics, crossfont::Error> {
let regular_desc = GlyphCache::make_desc(font.normal(), Slant::Normal, Weight::Normal);
let regular = Self::load_regular_font(rasterizer, &regular_desc, font.size())?;
rasterizer.get_glyph(GlyphKey { font_key: regular, character: 'm', size: font.size() })?;
rasterizer.metrics(regular, font.size())
}
} }
// NOTE: These flags must be in sync with their usage in the text.*.glsl shaders. // NOTE: These flags must be in sync with their usage in the text.*.glsl shaders.

View File

@ -242,13 +242,14 @@ impl WindowContext {
}, },
// Continue to process all pending events. // Continue to process all pending events.
GlutinEvent::RedrawEventsCleared => (), GlutinEvent::RedrawEventsCleared => (),
// Remap DPR change event to remove the lifetime. // Remap scale_factor change event to remove the lifetime.
GlutinEvent::WindowEvent { GlutinEvent::WindowEvent {
event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size }, event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size },
window_id, window_id,
} => { } => {
let size = (new_inner_size.width, new_inner_size.height); let size = (new_inner_size.width, new_inner_size.height);
let event = Event::new(EventType::DprChanged(scale_factor, size), window_id); let event =
Event::new(EventType::ScaleFactorChanged(scale_factor, size), window_id);
self.event_queue.push(event.into()); self.event_queue.push(event.into());
return; return;
}, },