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]
pub fn padding(&self, dpr: f64) -> (f32, f32) {
let padding_x = (f32::from(self.padding.x) * dpr as f32).floor();
let padding_y = (f32::from(self.padding.y) * dpr as f32).floor();
pub fn padding(&self, scale_factor: f64) -> (f32, f32) {
let padding_x = (f32::from(self.padding.x) * scale_factor as f32).floor();
let padding_y = (f32::from(self.padding.y) * scale_factor as f32).floor();
(padding_x, padding_y)
}

View File

@ -5,7 +5,6 @@ use std::convert::TryFrom;
use std::fmt::{self, Formatter};
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use std::sync::atomic::Ordering;
use std::time::Instant;
use std::{cmp, mem};
use glutin::dpi::PhysicalSize;
@ -223,26 +222,29 @@ impl Display {
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
let is_x11 = event_loop.is_x11();
// Guess DPR based on first monitor. On Wayland the initial frame always renders at a DPR
// of 1.
let estimated_dpr = if cfg!(any(target_os = "macos", windows)) || is_x11 {
// Guess scale_factor based on first monitor. On Wayland the initial frame always renders at
// a scale factor of 1.
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.)
} else {
1.
};
// Guess the target window dimensions.
let mut rasterizer = Rasterizer::new(estimated_dpr as f32, config.font.use_thin_strokes)?;
let metrics = GlyphCache::static_metrics(&mut rasterizer, config.font.clone())?;
debug!("Loading \"{}\" font", &config.font.normal().family);
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);
// Guess the target window size if the user has specified the number of lines/columns.
let dimensions = config.window.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 cell size: {} x {}", cell_width, cell_height);
@ -256,26 +258,34 @@ impl Display {
wayland_event_queue,
)?;
info!("Device pixel ratio: {}", window.dpr);
rasterizer.update_dpr(window.dpr as f32);
// Create renderer.
let mut renderer = QuadRenderer::new()?;
let (glyph_cache, cell_width, cell_height) =
Self::new_glyph_cache(rasterizer, &mut renderer, config)?;
let scale_factor = window.scale_factor;
info!("Display scale factor: {}", scale_factor);
if let Some(dimensions) = dimensions {
if (estimated_dpr - window.dpr).abs() < f64::EPSILON {
info!("Estimated DPR correctly, skipping resize");
} else {
// Resize the window again if the DPR was not estimated correctly.
let size = window_size(config, dimensions, cell_width, cell_height, window.dpr);
window.set_inner_size(size);
}
// 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)
} 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);
});
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();
// 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.
///
/// This will return a tuple of the cell width and height.
fn update_glyph_cache(&mut self, config: &UiConfig, font: &Font) -> (f32, f32) {
let cache = &mut self.glyph_cache;
let dpr = self.window.dpr;
self.renderer.with_loader(|mut api| {
let _ = cache.update_font_size(font, dpr, &mut api);
fn update_glyph_cache(
renderer: &mut QuadRenderer,
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);
});
// Compute new cell sizes.
compute_cell_size(config, &self.glyph_cache.font_metrics())
compute_cell_size(config, &glyph_cache.font_metrics())
}
/// Clear glyph cache.
@ -436,7 +419,14 @@ impl Display {
// Update font size and cell dimensions.
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_height = cell_dimensions.1;
@ -451,7 +441,7 @@ impl Display {
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(
width,
@ -1021,9 +1011,9 @@ fn window_size(
dimensions: Dimensions,
cell_width: f32,
cell_height: f32,
dpr: f64,
scale_factor: f64,
) -> 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_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))))]
pub wayland_surface: Option<Attached<WlSurface>>,
/// Cached DPR for quickly scaling pixel sizes.
pub dpr: f64,
/// Cached scale factor for quickly scaling pixel sizes.
pub scale_factor: f64,
/// Current window title.
title: String,
@ -219,7 +219,7 @@ impl Window {
None
};
let dpr = windowed_context.window().scale_factor();
let scale_factor = windowed_context.window().scale_factor();
Ok(Self {
current_mouse_cursor,
@ -230,7 +230,7 @@ impl Window {
should_draw: Arc::new(AtomicBool::new(true)),
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_surface,
dpr,
scale_factor,
})
}

View File

@ -83,7 +83,7 @@ impl From<Event> for GlutinEvent<'_, Event> {
/// Alacritty events.
#[derive(Debug, Clone)]
pub enum EventType {
DprChanged(f64, (u32, u32)),
ScaleFactorChanged(f64, (u32, u32)),
Terminal(TerminalEvent),
ConfigReload(PathBuf),
Message(Message),
@ -1026,17 +1026,17 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
pub fn handle_event(&mut self, event: GlutinEvent<'_, Event>) {
match event {
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;
// Push current font to update its DPR.
// Push current font to update its scale factor.
let font = self.ctx.config.font.clone();
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.
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;
},
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.
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 window_id = self.ctx.window().id();
let scheduler = self.ctx.scheduler_mut();
// Scale constants by DPI.
let min_height = (MIN_SELECTION_SCROLLING_HEIGHT * dpr) as i32;
let step = (SELECTION_SCROLLING_STEP * dpr) as i32;
let min_height = (MIN_SELECTION_SCROLLING_HEIGHT * scale_factor) as i32;
let step = (SELECTION_SCROLLING_STEP * scale_factor) as i32;
// Compute the height of the scrolling areas.
let end_top = max(min_height, size.padding_y() as i32);

View File

@ -156,14 +156,7 @@ pub struct GlyphCache {
}
impl GlyphCache {
pub fn new<L>(
mut rasterizer: Rasterizer,
font: &Font,
loader: &mut L,
) -> Result<GlyphCache, crossfont::Error>
where
L: LoadGlyph,
{
pub fn new(mut rasterizer: Rasterizer, font: &Font) -> Result<GlyphCache, crossfont::Error> {
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.
@ -173,7 +166,7 @@ impl GlyphCache {
let metrics = rasterizer.metrics(regular, font.size())?;
let mut cache = Self {
Ok(Self {
cache: HashMap::default(),
rasterizer,
font_size: font.size(),
@ -185,11 +178,7 @@ impl GlyphCache {
glyph_offset: font.glyph_offset,
metrics,
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) {
@ -358,11 +347,11 @@ impl GlyphCache {
pub fn update_font_size<L: LoadGlyph>(
&mut self,
font: &Font,
dpr: f64,
scale_factor: f64,
loader: &mut L,
) -> Result<(), crossfont::Error> {
// Update dpi scaling.
self.rasterizer.update_dpr(dpr as f32);
self.rasterizer.update_dpr(scale_factor as f32);
self.font_offset = font.offset;
// Recompute font keys.
@ -376,7 +365,7 @@ impl GlyphCache {
})?;
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_key = regular;
@ -396,24 +385,12 @@ impl GlyphCache {
}
/// 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.bold_key, loader);
self.load_glyphs_for_font(self.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.

View File

@ -242,13 +242,14 @@ impl WindowContext {
},
// Continue to process all pending events.
GlutinEvent::RedrawEventsCleared => (),
// Remap DPR change event to remove the lifetime.
// Remap scale_factor change event to remove the lifetime.
GlutinEvent::WindowEvent {
event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size },
window_id,
} => {
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());
return;
},