diff options
27 files changed, 444 insertions, 507 deletions
diff --git a/Cargo.lock b/Cargo.lock index 6b1530a7de0..20c65956206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1952,6 +1952,7 @@ name = "gfx" version = "0.0.1" dependencies = [ "app_units", + "atomic_refcell", "bitflags 2.5.0", "byteorder", "core-foundation", diff --git a/Cargo.toml b/Cargo.toml index 42123438215..f3809e5c641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,7 @@ to_shmem = { git = "https://github.com/servo/stylo", branch = "2024-04-16" } tokio = "1" tokio-rustls = "0.24" tungstenite = "0.20" +uluru = "3.0" unicode-bidi = "0.3.15" unicode-script = "0.5" unicode-segmentation = "1.1.0" diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 5bb4640dfd1..0175242521e 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -2,9 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::RefCell; use std::mem; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use canvas_traits::canvas::*; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; @@ -359,21 +358,6 @@ pub enum Filter { Nearest, } -pub(crate) type CanvasFontContext = FontContext<FontCacheThread>; - -thread_local!(static FONT_CONTEXT: RefCell<Option<CanvasFontContext>> = RefCell::new(None)); - -pub(crate) fn with_thread_local_font_context<F, R>(canvas_data: &CanvasData, f: F) -> R -where - F: FnOnce(&mut CanvasFontContext) -> R, -{ - FONT_CONTEXT.with(|font_context| { - f(font_context.borrow_mut().get_or_insert_with(|| { - FontContext::new(canvas_data.font_cache_thread.lock().unwrap().clone()) - })) - }) -} - pub struct CanvasData<'a> { backend: Box<dyn Backend>, drawtarget: Box<dyn GenericDrawTarget>, @@ -386,7 +370,7 @@ pub struct CanvasData<'a> { old_image_key: Option<ImageKey>, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option<ImageKey>, - font_cache_thread: Mutex<FontCacheThread>, + font_context: Arc<FontContext<FontCacheThread>>, } fn create_backend() -> Box<dyn Backend> { @@ -398,7 +382,7 @@ impl<'a> CanvasData<'a> { size: Size2D<u64>, webrender_api: Box<dyn WebrenderApi>, antialias: AntialiasMode, - font_cache_thread: FontCacheThread, + font_context: Arc<FontContext<FontCacheThread>>, ) -> CanvasData<'a> { let backend = create_backend(); let draw_target = backend.create_drawtarget(size); @@ -412,7 +396,7 @@ impl<'a> CanvasData<'a> { image_key: None, old_image_key: None, very_old_image_key: None, - font_cache_thread: Mutex::new(font_cache_thread), + font_context, } } @@ -494,17 +478,14 @@ impl<'a> CanvasData<'a> { let font = font_style.map_or_else( || load_system_font_from_style(None), |style| { - with_thread_local_font_context(self, |font_context| { - let font_group = font_context.font_group(ServoArc::new(style.clone())); - let font = font_group - .borrow_mut() - .first(font_context) - .expect("couldn't find font"); - let font = font.borrow_mut(); - Font::from_bytes(font.template.data(), 0) - .ok() - .or_else(|| load_system_font_from_style(Some(style))) - }) + let font_group = self.font_context.font_group(ServoArc::new(style.clone())); + let font = font_group + .write() + .first(&self.font_context) + .expect("couldn't find font"); + Font::from_bytes(font.template.data(), 0) + .ok() + .or_else(|| load_system_font_from_style(Some(style))) }, ); let font = match font { diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index ff9fcfaa313..2176ad8d636 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -4,6 +4,7 @@ use std::borrow::ToOwned; use std::collections::HashMap; +use std::sync::Arc; use std::thread; use canvas_traits::canvas::*; @@ -11,6 +12,7 @@ use canvas_traits::ConstellationCanvasMsg; use crossbeam_channel::{select, unbounded, Sender}; use euclid::default::Size2D; use gfx::font_cache_thread::FontCacheThread; +use gfx::font_context::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; @@ -40,7 +42,7 @@ pub struct CanvasPaintThread<'a> { canvases: HashMap<CanvasId, CanvasData<'a>>, next_canvas_id: CanvasId, webrender_api: Box<dyn WebrenderApi>, - font_cache_thread: FontCacheThread, + font_context: Arc<FontContext<FontCacheThread>>, } impl<'a> CanvasPaintThread<'a> { @@ -52,7 +54,7 @@ impl<'a> CanvasPaintThread<'a> { canvases: HashMap::new(), next_canvas_id: CanvasId(0), webrender_api, - font_cache_thread, + font_context: Arc::new(FontContext::new(font_cache_thread)), } } @@ -129,8 +131,6 @@ impl<'a> CanvasPaintThread<'a> { AntialiasMode::None }; - let font_cache_thread = self.font_cache_thread.clone(); - let canvas_id = self.next_canvas_id; self.next_canvas_id.0 += 1; @@ -138,7 +138,7 @@ impl<'a> CanvasPaintThread<'a> { size, self.webrender_api.clone(), antialias, - font_cache_thread, + self.font_context.clone(), ); self.canvases.insert(canvas_id, canvas_data); diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index c9c0d388fcb..bec2264c44c 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -15,6 +15,7 @@ doctest = false [dependencies] app_units = { workspace = true } +atomic_refcell = { workspace = true } bitflags = { workspace = true } cssparser = { workspace = true } euclid = { workspace = true } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 1921edc1d12..de42f6c321c 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -3,11 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; -use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::time::Instant; use std::{iter, str}; @@ -15,6 +13,7 @@ use app_units::Au; use bitflags::bitflags; use euclid::default::{Point2D, Rect, Size2D}; use log::debug; +use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use servo_atoms::{atom, Atom}; use smallvec::SmallVec; @@ -185,15 +184,21 @@ impl<'a> From<&'a FontStyleStruct> for FontDescriptor { } } +#[derive(Debug, Default)] +struct CachedShapeData { + glyph_advances: HashMap<GlyphId, FractionalPixel>, + glyph_indices: HashMap<char, Option<GlyphId>>, + shaped_text: HashMap<ShapeCacheEntry, Arc<GlyphStore>>, +} + #[derive(Debug)] pub struct Font { pub handle: PlatformFont, pub template: FontTemplateRef, pub metrics: FontMetrics, pub descriptor: FontDescriptor, - shaper: Option<Shaper>, - shape_cache: RefCell<HashMap<ShapeCacheEntry, Arc<GlyphStore>>>, - glyph_advance_cache: RefCell<HashMap<u32, FractionalPixel>>, + shaper: OnceLock<Shaper>, + cached_shape_data: RwLock<CachedShapeData>, pub font_key: FontInstanceKey, /// If this is a synthesized small caps font, then this font reference is for @@ -214,11 +219,10 @@ impl Font { Ok(Font { handle, template, - shaper: None, + shaper: OnceLock::new(), descriptor, metrics, - shape_cache: RefCell::new(HashMap::new()), - glyph_advance_cache: RefCell::new(HashMap::new()), + cached_shape_data: Default::default(), font_key: FontInstanceKey::default(), synthesized_small_caps, }) @@ -272,52 +276,49 @@ struct ShapeCacheEntry { } impl Font { - pub fn shape_text(&mut self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> { + pub fn shape_text(&self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> { let this = self as *const Font; - let mut shaper = self.shaper.take(); - let lookup_key = ShapeCacheEntry { text: text.to_owned(), options: *options, }; - let result = self - .shape_cache - .borrow_mut() - .entry(lookup_key) - .or_insert_with(|| { - let start_time = Instant::now(); - let mut glyphs = GlyphStore::new( - text.len(), - options - .flags - .contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG), - options.flags.contains(ShapingFlags::RTL_FLAG), - ); - - if self.can_do_fast_shaping(text, options) { - debug!("shape_text: Using ASCII fast path."); - self.shape_text_fast(text, options, &mut glyphs); - } else { - debug!("shape_text: Using Harfbuzz."); - if shaper.is_none() { - shaper = Some(Shaper::new(this)); - } - shaper - .as_ref() - .unwrap() - .shape_text(text, options, &mut glyphs); - } + { + let cache = self.cached_shape_data.read(); + if let Some(shaped_text) = cache.shaped_text.get(&lookup_key) { + return shaped_text.clone(); + } + } - let end_time = Instant::now(); - TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add( - (end_time.duration_since(start_time).as_nanos()) as usize, - Ordering::Relaxed, - ); - Arc::new(glyphs) - }) - .clone(); - self.shaper = shaper; - result + let start_time = Instant::now(); + let mut glyphs = GlyphStore::new( + text.len(), + options + .flags + .contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG), + options.flags.contains(ShapingFlags::RTL_FLAG), + ); + + if self.can_do_fast_shaping(text, options) { + debug!("shape_text: Using ASCII fast path."); + self.shape_text_fast(text, options, &mut glyphs); + } else { + debug!("shape_text: Using Harfbuzz."); + self.shaper + .get_or_init(|| Shaper::new(this)) + .shape_text(text, options, &mut glyphs); + } + + let shaped_text = Arc::new(glyphs); + let mut cache = self.cached_shape_data.write(); + cache.shaped_text.insert(lookup_key, shaped_text.clone()); + + let end_time = Instant::now(); + TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add( + (end_time.duration_since(start_time).as_nanos()) as usize, + Ordering::Relaxed, + ); + + shaped_text } fn can_do_fast_shaping(&self, text: &str, options: &ShapingOptions) -> bool { @@ -377,11 +378,21 @@ impl Font { #[inline] pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { + { + let cache = self.cached_shape_data.read(); + if let Some(glyph) = cache.glyph_indices.get(&codepoint) { + return *glyph; + } + } let codepoint = match self.descriptor.variant { font_variant_caps::T::SmallCaps => codepoint.to_ascii_uppercase(), font_variant_caps::T::Normal => codepoint, }; - self.handle.glyph_index(codepoint) + let glyph_index = self.handle.glyph_index(codepoint); + + let mut cache = self.cached_shape_data.write(); + cache.glyph_indices.insert(codepoint, glyph_index); + glyph_index } pub fn has_glyph_for(&self, codepoint: char) -> bool { @@ -392,21 +403,27 @@ impl Font { self.handle.glyph_h_kerning(first_glyph, second_glyph) } - pub fn glyph_h_advance(&self, glyph: GlyphId) -> FractionalPixel { - *self - .glyph_advance_cache - .borrow_mut() - .entry(glyph) - .or_insert_with(|| { - match self.handle.glyph_h_advance(glyph) { - Some(adv) => adv, - None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel, // FIXME: Need fallback strategy - } - }) + pub fn glyph_h_advance(&self, glyph_id: GlyphId) -> FractionalPixel { + { + let cache = self.cached_shape_data.read(); + if let Some(width) = cache.glyph_advances.get(&glyph_id) { + return *width; + } + } + + // TODO: Need a fallback strategy. + let new_width = match self.handle.glyph_h_advance(glyph_id) { + Some(adv) => adv, + None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel, + }; + + let mut cache = self.cached_shape_data.write(); + cache.glyph_advances.insert(glyph_id, new_width); + new_width } } -pub type FontRef = Rc<RefCell<Font>>; +pub type FontRef = Arc<Font>; /// A `FontGroup` is a prioritised list of fonts for a given set of font styles. It is used by /// `TextRun` to decide which font to render a character with. If none of the fonts listed in the @@ -442,14 +459,13 @@ impl FontGroup { /// found, returns None. pub fn find_by_codepoint<S: FontSource>( &mut self, - font_context: &mut FontContext<S>, + font_context: &FontContext<S>, codepoint: char, ) -> Option<FontRef> { let should_look_for_small_caps = self.descriptor.variant == font_variant_caps::T::SmallCaps && codepoint.is_ascii_lowercase(); let font_or_synthesized_small_caps = |font: FontRef| { if should_look_for_small_caps { - let font = font.borrow(); if font.synthesized_small_caps.is_some() { return font.synthesized_small_caps.clone(); } @@ -457,7 +473,7 @@ impl FontGroup { Some(font) }; - let glyph_in_font = |font: &FontRef| font.borrow().has_glyph_for(codepoint); + let glyph_in_font = |font: &FontRef| font.has_glyph_for(codepoint); let char_in_template = |template: FontTemplateRef| template.char_in_unicode_range(codepoint); @@ -466,7 +482,7 @@ impl FontGroup { } if let Some(ref last_matching_fallback) = self.last_matching_fallback { - if char_in_template(last_matching_fallback.borrow().template.clone()) && + if char_in_template(last_matching_fallback.template.clone()) && glyph_in_font(last_matching_fallback) { return font_or_synthesized_small_caps(last_matching_fallback.clone()); @@ -487,7 +503,7 @@ impl FontGroup { } /// Find the first available font in the group, or the first available fallback font. - pub fn first<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> { + pub fn first<S: FontSource>(&mut self, font_context: &FontContext<S>) -> Option<FontRef> { // From https://drafts.csswg.org/css-fonts/#first-available-font: // > The first available font, used for example in the definition of font-relative lengths // > such as ex or in the definition of the line-height property, is defined to be the first @@ -506,7 +522,7 @@ impl FontGroup { /// a suitable font. fn find<S, TemplatePredicate, FontPredicate>( &mut self, - font_context: &mut FontContext<S>, + font_context: &FontContext<S>, template_predicate: TemplatePredicate, font_predicate: FontPredicate, ) -> Option<FontRef> @@ -535,7 +551,7 @@ impl FontGroup { /// used to refine the list of family names which will be tried. fn find_fallback<S, TemplatePredicate, FontPredicate>( &mut self, - font_context: &mut FontContext<S>, + font_context: &FontContext<S>, codepoint: Option<char>, template_predicate: TemplatePredicate, font_predicate: FontPredicate, @@ -602,7 +618,7 @@ impl FontGroupFamily { fn find<S, TemplatePredicate, FontPredicate>( &mut self, font_descriptor: &FontDescriptor, - font_context: &mut FontContext<S>, + font_context: &FontContext<S>, template_predicate: &TemplatePredicate, font_predicate: &FontPredicate, ) -> Option<FontRef> @@ -633,7 +649,7 @@ impl FontGroupFamily { fn members<'a, S: FontSource>( &'a mut self, font_descriptor: &FontDescriptor, - font_context: &mut FontContext<S>, + font_context: &FontContext<S>, ) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a { let family_descriptor = &self.family_descriptor; let members = self.members.get_or_insert_with(|| { diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 5d7d160ca65..3c1790edb6f 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -3,14 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; -use std::cell::RefCell; use std::collections::HashMap; use std::ops::{Deref, RangeInclusive}; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{f32, fmt, mem, thread}; use app_units::Au; +use atomic_refcell::AtomicRefCell; use gfx_traits::WebrenderApi; use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender}; use log::{debug, trace}; @@ -125,7 +124,8 @@ impl FontTemplates { return; } } - self.templates.push(Rc::new(RefCell::new(new_template))); + self.templates + .push(Arc::new(AtomicRefCell::new(new_template))); } } @@ -730,7 +730,7 @@ impl FontSource for FontCacheThread { .into_iter() .map(|serialized_font_template| { let font_data = serialized_font_template.bytes_receiver.recv().ok(); - Rc::new(RefCell::new(FontTemplate { + Arc::new(AtomicRefCell::new(FontTemplate { identifier: serialized_font_template.identifier, descriptor: serialized_font_template.descriptor.clone(), data: font_data.map(Arc::new), diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index b831fc916cd..c08764f7a70 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -2,17 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::RefCell; use std::collections::HashMap; use std::default::Default; use std::hash::{BuildHasherDefault, Hash, Hasher}; -use std::rc::Rc; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; use app_units::Au; use fnv::FnvHasher; use log::debug; -use servo_arc::Arc; +use parking_lot::{Mutex, RwLock}; +use servo_arc::Arc as ServoArc; use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::{FontInstanceFlags, FontInstanceKey}; @@ -25,10 +24,6 @@ use crate::platform::core_text_font_cache::CoreTextFontCache; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) -/// An epoch for the font context cache. The cache is flushed if the current epoch does not match -/// this one. -static FONT_CACHE_EPOCH: AtomicUsize = AtomicUsize::new(0); - pub trait FontSource { fn get_font_instance( &mut self, @@ -49,48 +44,48 @@ pub trait FontSource { /// required. #[derive(Debug)] pub struct FontContext<S: FontSource> { - font_source: S, + font_source: Mutex<S>, // TODO: The font context holds a strong ref to the cached fonts // so they will never be released. Find out a good time to drop them. // See bug https://github.com/servo/servo/issues/3300 - font_cache: HashMap<FontCacheKey, Option<FontRef>>, - font_template_cache: HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>, - + font_cache: RwLock<HashMap<FontCacheKey, Option<FontRef>>>, + font_template_cache: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>, font_group_cache: - HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>, - - epoch: usize, + RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, } impl<S: FontSource> FontContext<S> { pub fn new(font_source: S) -> FontContext<S> { #[allow(clippy::default_constructed_unit_structs)] FontContext { - font_source, - font_cache: HashMap::new(), - font_template_cache: HashMap::new(), - font_group_cache: HashMap::with_hasher(Default::default()), - epoch: 0, + font_source: Mutex::new(font_source), + font_cache: RwLock::default(), + font_template_cache: RwLock::default(), + font_group_cache: RwLock::default(), } } - fn expire_font_caches_if_necessary(&mut self) { - let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst); - if current_epoch == self.epoch { - return; - } - - self.font_cache.clear(); - self.font_template_cache.clear(); - self.font_group_cache.clear(); - self.epoch = current_epoch + /// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific + /// caches. + /// + /// # Safety + /// + /// This should never be called when more than one thread is using the [`FontContext`] or it + /// may leave the context in an inconsistent state. + pub fn invalidate_caches(&self) { + #[cfg(target_os = "macos")] + CoreTextFontCache::clear_core_text_font_cache(); + + self.font_cache.write().clear(); + self.font_template_cache.write().clear(); + self.font_group_cache.write().clear(); } /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`. /// Font groups are cached, so subsequent calls with the same `style` will return a reference /// to an existing `FontGroup`. - pub fn font_group(&mut self, style: Arc<FontStyleStruct>) -> Rc<RefCell<FontGroup>> { + pub fn font_group(&self, style: ServoArc<FontStyleStruct>) -> Arc<RwLock<FontGroup>> { let font_size = style.font_size.computed_size().into(); self.font_group_with_size(style, font_size) } @@ -98,27 +93,27 @@ impl<S: FontSource> FontContext<S> { /// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size /// in pixels. pub fn font_group_with_size( - &mut self, - style: Arc<FontStyleStruct>, + &self, + style: ServoArc<FontStyleStruct>, size: Au, - ) -> Rc<RefCell<FontGroup>> { - self.expire_font_caches_if_necessary(); - + ) -> Arc<RwLock<FontGroup>> { let cache_key = FontGroupCacheKey { size, style }; - if let Some(font_group) = self.font_group_cache.get(&cache_key) { + if let Some(font_group) = self.font_group_cache.read().get(&cache_key) { return font_group.clone(); } - let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style))); - self.font_group_cache.insert(cache_key, font_group.clone()); + let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style))); + self.font_group_cache + .write() + .insert(cache_key, font_group.clone()); font_group } /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a /// reference to the same underlying `Font`. pub fn font( - &mut self, + &self, font_template: FontTemplateRef, font_descriptor: &FontDescriptor, ) -> Option<FontRef> { @@ -130,7 +125,7 @@ impl<S: FontSource> FontContext<S> { } fn get_font_maybe_synthesizing_small_caps( - &mut self, + &self, font_template: FontTemplateRef, font_descriptor: &FontDescriptor, synthesize_small_caps: bool, @@ -157,27 +152,28 @@ impl<S: FontSource> FontContext<S> { font_descriptor: font_descriptor.clone(), }; - self.font_cache.get(&cache_key).cloned().unwrap_or_else(|| { - debug!( - "FontContext::font cache miss for font_template={:?} font_descriptor={:?}", - font_template, font_descriptor - ); - - let font = self - .create_font( - font_template, - font_descriptor.to_owned(), - synthesized_small_caps_font, - ) - .ok(); - self.font_cache.insert(cache_key, font.clone()); + if let Some(font) = self.font_cache.read().get(&cache_key).cloned() { + return font; + } - font - }) + debug!( + "FontContext::font cache miss for font_template={:?} font_descriptor={:?}", + font_template, font_descriptor + ); + + let font = self + .create_font( + font_template, + font_descriptor.to_owned(), + synthesized_small_caps_font, + ) + .ok(); + self.font_cache.write().insert(cache_key, font.clone()); + font } pub fn matching_templates( - &mut self, + &self, descriptor_to_match: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, ) -> Vec<FontTemplateRef> { @@ -186,27 +182,31 @@ impl<S: FontSource> FontContext<S> { family_descriptor: family_descriptor.clone(), }; - self.font_template_cache.get(&cache_key).cloned().unwrap_or_else(|| { - debug!( - "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}", - descriptor_to_match, - family_descriptor - ); - - let template_info = self.font_source.find_matching_font_templates( - descriptor_to_match, - family_descriptor.clone(), - ); - - self.font_template_cache.insert(cache_key, template_info.clone()); - template_info - }) + if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() { + return templates; + } + + debug!( + "FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}", + descriptor_to_match, + family_descriptor + ); + + let templates = self + .font_source + .lock() + .find_matching_font_templates(descriptor_to_match, family_descriptor.clone()); + + self.font_template_cache + .write() + .insert(cache_key, templates.clone()); + templates } /// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the /// cache thread and a `FontDescriptor` which contains the styling parameters. fn create_font( - &mut self, + &self, font_template: FontTemplateRef, font_descriptor: FontDescriptor, synthesized_small_caps: Option<FontRef>, @@ -216,13 +216,13 @@ impl<S: FontSource> FontContext<S> { font_descriptor.clone(), synthesized_small_caps, )?; - font.font_key = self.font_source.get_font_instance( + font.font_key = self.font_source.lock().get_font_instance( font_template.identifier(), font_descriptor.pt_size, font.webrender_font_instance_flags(), ); - Ok(Rc::new(RefCell::new(font))) + Ok(Arc::new(font)) } } @@ -240,7 +240,7 @@ struct FontTemplateCacheKey { #[derive(Debug)] struct FontGroupCacheKey { - style: Arc<FontStyleStruct>, + style: ServoArc<FontStyleStruct>, size: Au, } @@ -260,11 +260,3 @@ impl Hash for FontGroupCacheKey { self.style.hash.hash(hasher) } } - -#[inline] -pub fn invalidate_font_caches() { - FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst); - - #[cfg(target_os = "macos")] - CoreTextFontCache::clear_core_text_font_cache(); -} diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 30a40608df5..0af8aa75443 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -2,12 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::RefCell; use std::fmt::{Debug, Error, Formatter}; use std::ops::RangeInclusive; -use std::rc::Rc; use std::sync::Arc; +use atomic_refcell::AtomicRefCell; use serde::{Deserialize, Serialize}; use servo_url::ServoUrl; use style::computed_values::font_stretch::T as FontStretch; @@ -22,7 +21,7 @@ use crate::platform::font::PlatformFont; use crate::platform::font_list::LocalFontIdentifier; /// A reference to a [`FontTemplate`] with shared ownership and mutability. -pub type FontTemplateRef = Rc<RefCell<FontTemplate>>; +pub type FontTemplateRef = Arc<AtomicRefCell<FontTemplate>>; /// Describes how to select a font from a given family. This is very basic at the moment and needs /// to be expanded or refactored when we support more of the font styling parameters. diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index f437dbed8c0..f68e370e6d3 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -9,14 +9,15 @@ use std::{mem, ptr}; use app_units::Au; use freetype::freetype::{ - FT_Done_Face, FT_F26Dot6, FT_Face, FT_FaceRec, FT_Get_Char_Index, FT_Get_Kerning, - FT_Get_Sfnt_Table, FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table, - FT_Long, FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics, - FT_UInt, FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC, + FT_Done_Face, FT_F26Dot6, FT_Face, FT_Get_Char_Index, FT_Get_Kerning, FT_Get_Sfnt_Table, + FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table, FT_Long, + FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics, FT_UInt, + FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC, }; use freetype::succeeded; use freetype::tt_os2::TT_OS2; use log::debug; +use parking_lot::ReentrantMutex; use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_weight::T as FontWeight; use style::values::computed::font::FontStyle; @@ -75,19 +76,26 @@ pub struct PlatformFont { /// The font data itself, which must stay valid for the lifetime of the /// platform [`FT_Face`]. font_data: Arc<Vec<u8>>, - face: FT_Face, + face: ReentrantMutex<FT_Face>, can_do_fast_shaping: bool, } +// FT_Face can be used in multiple threads, but from only one thread at a time. +// It's protected with a ReentrantMutex for PlatformFont. +// See https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face. +unsafe impl Sync for PlatformFont {} +unsafe impl Send for PlatformFont {} + impl Drop for PlatformFont { fn drop(&mut self) { - assert!(!self.face.is_null()); + let face = self.face.lock(); + assert!(!face.is_null()); unsafe { // The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face` // should be protected by a mutex. // See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html. let _guard = FreeTypeLibraryHandle::get().lock(); - if !succeeded(FT_Done_Face(self.face)) { + if !succeeded(FT_Done_Face(*face)) { panic!("FT_Done_Face failed"); } } @@ -134,7 +142,7 @@ impl PlatformFontMethods for PlatformFont { ) -> Result<PlatformFont, &'static str> { let face = create_face(data.clone(), face_index, pt_size)?; let mut handle = PlatformFont { - face, + face: ReentrantMutex::new(face), font_data: data, can_do_fast_shaping: false, }; @@ -147,7 +155,8 @@ impl PlatformFontMethods for PlatformFont { } fn descriptor(&self) -> FontTemplateDescriptor { - let style = if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } { + let face = self.face.lock(); + let style = if unsafe { (**face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } { FontStyle::ITALIC } else { FontStyle::NORMAL @@ -178,9 +187,11 @@ impl PlatformFontMethods for PlatformFont { } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { - assert!(!self.face.is_null()); + let face = self.face.lock(); + assert!(!face.is_null()); + unsafe { - let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong); + let idx = FT_Get_Char_Index(*face, codepoint as FT_ULong); if idx != 0 as FT_UInt { Some(idx as GlyphId) } else { @@ -194,11 +205,13 @@ impl PlatformFontMethods for PlatformFont { } fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel { - assert!(!self.face.is_null()); + let face = self.face.lock(); + assert!(!face.is_null()); + let mut delta = FT_Vector { x: 0, y: 0 }; unsafe { FT_Get_Kerning( - self.face, + *face, first_glyph, second_glyph, FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt, @@ -213,11 +226,13 @@ impl PlatformFontMethods for PlatformFont { } fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> { - assert!(!self.face.is_null()); + let face = self.face.lock(); + assert!(!face.is_null()); + unsafe { - let res = FT_Load_Glyph(self.face, glyph as FT_UInt, GLYPH_LOAD_FLAGS); + let res = FT_Load_Glyph(*face, glyph as FT_UInt, GLYPH_LOAD_FLAGS); if succeeded(res) { - let void_glyph = (*self.face).glyph; + let void_glyph = (**face).glyph; let slot: FT_GlyphSlot = void_glyph; assert!(!slot.is_null()); let advance = (*slot).metrics.horiAdvance; @@ -232,8 +247,8 @@ impl PlatformFontMethods for PlatformFont { } fn metrics(&self) -> FontMetrics { - /* TODO(Issue #76): complete me */ - let face = self.face_rec_mut(); + let face = self.face.lock(); + let face = unsafe { **face }; let underline_size = self.font_units_to_au(face.underline_thickness as f64); let underline_offset = self.font_units_to_au(face.underline_position as f64); @@ -294,24 +309,19 @@ impl PlatformFontMethods for PlatformFont { } fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> { + let face = self.face.lock(); let tag = tag as FT_ULong; unsafe { // Get the length let mut len = 0; - if !succeeded(FT_Load_Sfnt_Table( - self.face, - tag, - 0, - ptr::null_mut(), - &mut len, - )) { + if !succeeded(FT_Load_Sfnt_Table(*face, tag, 0, ptr::null_mut(), &mut len)) { return None; } // Get the bytes let mut buf = vec![0u8; len as usize]; if !succeeded(FT_Load_Sfnt_Table( - self.face, + *face, tag, 0, buf.as_mut_ptr(), @@ -343,9 +353,10 @@ impl<'a> PlatformFont { } fn has_table(&self, tag: FontTableTag) -> bool { + let face = self.face.lock(); unsafe { succeeded(FT_Load_Sfnt_Table( - self.face, + *face, tag as FT_ULong, 0, ptr::null_mut(), @@ -354,13 +365,9 @@ impl<'a> PlatformFont { } } - #[allow(clippy::mut_from_ref)] // Intended for this function - fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec { - unsafe { &mut (*self.face) } - } - fn font_units_to_au(&self, value: f64) -> Au { - let face = self.face_rec_mut(); + let face = self.face.lock(); + let face = unsafe { **face }; // face.size is a *c_void in the bindings, presumably to avoid // recursive structural types @@ -377,9 +384,10 @@ impl<'a> PlatformFont { } fn os2_table(&self) -> Option<OS2Table> { + let face = self.face.lock(); + unsafe { - let os2 = - FT_Get_Sfnt_Table(self.face_rec_mut(), FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2; + let os2 = FT_Get_Sfnt_Table(*face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2; let valid = !os2.is_null() && (*os2).version != 0xffff; if !valid { diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index c6e009ffae8..7ab00b51cd0 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -63,6 +63,17 @@ pub struct PlatformFont { can_do_fast_shaping: bool, } +// From https://developer.apple.com/documentation/coretext: +// > All individual functions in Core Text are thread-safe. Font objects (CTFont, +// > CTFontDescriptor, and associated objects) can be used simultaneously by multiple +// > operations, work queues, or threads. However, the layout objects (CTTypesetter, +// > CTFramesetter, CTRun, CTLine, CTFrame, and associated objects) should be used in a +// > single operation, work queue, or thread. +// +// The other element is a read-only CachedKernTable which is stored in a CFData. +unsafe impl Sync for PlatformFont {} +unsafe impl Send for PlatformFont {} + impl PlatformFont { /// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or /// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case. diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index 5debd0c64fa..5ec0a4c658a 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -82,6 +82,13 @@ pub struct PlatformFont { scaled_du_to_px: f32, } +// Based on information from the Skia codebase, it seems that DirectWrite APIs from +// Windows 10 and beyond are thread safe. If problems arise from this, we can protect the +// platform font with a Mutex. +// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/ports/SkScalerContext_win_dw.cpp;l=56;bpv=0;bpt=1. +unsafe impl Sync for PlatformFont {} +unsafe impl Send for PlatformFont {} + struct Nondebug<T>(T); impl<T> fmt::Debug for Nondebug<T> { diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index f502b7d97fe..54babf3ef83 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -150,22 +150,26 @@ fn font_family(names: Vec<&str>) -> FontFamily { #[test] fn test_font_group_is_cached_by_style() { let source = TestFontSource::new(); - let mut context = FontContext::new(source); + let context = FontContext::new(source); let style1 = style(); let mut style2 = style(); style2.set_font_style(FontStyle::ITALIC); - assert_eq!( - context.font_group(Arc::new(style1.clone())).as_ptr(), - context.font_group(Arc::new(style1.clone())).as_ptr(), + assert!( + std::ptr::eq( + &*context.font_group(Arc::new(style1.clone())).read(), + &*context.font_group(Arc::new(style1.clone())).read() + ), "the same font group should be returned for two styles with the same hash" ); - assert_ne!( - context.font_group(Arc::new(style1.clone())).as_ptr(), - context.font_group(Arc::new(style2.clone())).as_ptr(), + assert!( + !std::ptr::eq( + &*context.font_group(Arc::new(style1.clone())).read(), + &*context.font_group(Arc::new(style2.clone())).read() + ), "different font groups should be returned for two styles with different hashes" ) } @@ -181,12 +185,9 @@ fn test_font_group_find_by_codepoint() { let group = context.font_group(Arc::new(style)); - let font = group - .borrow_mut() - .find_by_codepoint(&mut context, 'a') - .unwrap(); + let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( - font.borrow().identifier(), + font.identifier(), TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -195,12 +196,9 @@ fn test_font_group_find_by_codepoint() { "only the first font in the list should have been loaded" ); - let font = group - .borrow_mut() - .find_by_codepoint(&mut context, 'a') - .unwrap(); + let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( - font.borrow().identifier(), + font.identifier(), TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -209,12 +207,9 @@ fn test_font_group_find_by_codepoint() { "we shouldn't load the same font a second time" ); - let font = group - .borrow_mut() - .find_by_codepoint(&mut context, 'á') - .unwrap(); + let font = group.write().find_by_codepoint(&mut context, 'á').unwrap(); assert_eq!( - font.borrow().identifier(), + font.identifier(), TestFontSource::identifier_for_font_name("csstest-basic-regular") ); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); @@ -230,22 +225,16 @@ fn test_font_fallback() { let group = context.font_group(Arc::new(style)); - let font = group - .borrow_mut() - .find_by_codepoint(&mut context, 'a') - .unwrap(); + let font = group.write().find_by_codepoint(&mut context, 'a').unwrap(); assert_eq!( - font.borrow().identifier(), + font.identifier(), TestFontSource::identifier_for_font_name("csstest-ascii"), "a family in the group should be used if there is a matching glyph" ); - let font = group - .borrow_mut() - .find_by_codepoint(&mut context, 'á') - .unwrap(); + let font = group.write().find_by_codepoint(&mut context, 'á').unwrap(); assert_eq!( - font.borrow().identifier(), + font.identifier(), TestFontSource::identifier_for_font_name("csstest-basic-regular"), "a fallback font should be used if there is no matching glyph in the group" ); @@ -255,7 +244,7 @@ fn test_font_fallback() { fn test_font_template_is_cached() { let source = TestFontSource::new(); let count = source.find_font_count.clone(); - let mut context = FontContext::new(source); + let context = FontContext::new(source); let mut font_descriptor = FontDescriptor { weight: FontWeight::normal(), @@ -280,8 +269,7 @@ fn test_font_template_is_cached() { .unwrap(); assert_ne!( - font1.borrow().descriptor.pt_size, - font2.borrow().descriptor.pt_size, + font1.descriptor.pt_size, font2.descriptor.pt_size, "the same font should not have been returned" ); diff --git a/components/gfx/text/shaping/harfbuzz.rs b/components/gfx/text/shaping/harfbuzz.rs index 5f2e801f40a..ce0ff44be8f 100644 --- a/components/gfx/text/shaping/harfbuzz.rs +++ b/components/gfx/text/shaping/harfbuzz.rs @@ -130,6 +130,12 @@ pub struct Shaper { font: *const Font, } +// The HarfBuzz API is thread safe as well as our `Font`, so we can make the data +// structures here as thread-safe as well. This doesn't seem to be documented, +// but was expressed as one of the original goals of the HarfBuzz API. +unsafe impl Sync for Shaper {} +unsafe impl Send for Shaper {} + impl Drop for Shaper { fn drop(&mut self) { unsafe { diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 3973702a026..d4bec79564a 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -16,7 +16,7 @@ use unicode_bidi as bidi; use webrender_api::FontInstanceKey; use xi_unicode::LineBreakLeafIter; -use crate::font::{Font, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions}; +use crate::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions}; use crate::text::glyph::{ByteIndex, GlyphStore}; thread_local! { @@ -180,13 +180,14 @@ impl<'a> Iterator for CharacterSliceIterator<'a> { impl<'a> TextRun { /// Constructs a new text run. Also returns if there is a line break at the beginning pub fn new( - font: &mut Font, + font: FontRef, text: String, options: &ShapingOptions, bidi_level: bidi::Level, breaker: &mut Option<LineBreakLeafIter>, ) -> (TextRun, bool) { - let (glyphs, break_at_zero) = TextRun::break_and_shape(font, &text, options, breaker); + let (glyphs, break_at_zero) = + TextRun::break_and_shape(font.clone(), &text, options, breaker); ( TextRun { text: Arc::new(text), @@ -202,7 +203,7 @@ impl<'a> TextRun { } pub fn break_and_shape( - font: &mut Font, + font: FontRef, text: &str, options: &ShapingOptions, breaker: &mut Option<LineBreakLeafIter>, diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 522635543fd..1c4eff55898 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -42,7 +42,7 @@ use style::values::generics::counters::ContentItem; use style::LocalName; use crate::block::BlockFlow; -use crate::context::{with_thread_local_font_context, LayoutContext}; +use crate::context::LayoutContext; use crate::data::{InnerLayoutData, LayoutDataFlags}; use crate::display_list::items::OpaqueNode; use crate::flex::FlexFlow; @@ -517,11 +517,10 @@ where // We must scan for runs before computing minimum ascent and descent because scanning // for runs might collapse so much whitespace away that only hypothetical fragments // remain. In that case the inline flow will compute its ascent and descent to be zero. - let scanned_fragments = - with_thread_local_font_context(self.layout_context, |font_context| { - TextRunScanner::new() - .scan_for_runs(font_context, mem::take(&mut fragments.fragments)) - }); + let scanned_fragments = TextRunScanner::new().scan_for_runs( + &self.layout_context.font_context, + mem::take(&mut fragments.fragments), + ); let mut inline_flow_ref = FlowRef::new(Arc::new(InlineFlow::from_fragments( scanned_fragments, node.style(self.style_context()).writing_mode, @@ -550,11 +549,10 @@ where { // FIXME(#6503): Use Arc::get_mut().unwrap() here. let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline(); - inline_flow.minimum_line_metrics = - with_thread_local_font_context(self.layout_context, |font_context| { - inline_flow - .minimum_line_metrics(font_context, &node.style(self.style_context())) - }); + inline_flow.minimum_line_metrics = inline_flow.minimum_line_metrics( + &self.layout_context.font_context, + &node.style(self.style_context()), + ); } inline_flow_ref.finish(); @@ -1545,11 +1543,10 @@ where )), self.layout_context, )); - let marker_fragments = - with_thread_local_font_context(self.layout_context, |font_context| { - TextRunScanner::new() - .scan_for_runs(font_context, unscanned_marker_fragments) - }); + let marker_fragments = TextRunScanner::new().scan_for_runs( + &self.layout_context.font_context, + unscanned_marker_fragments, + ); marker_fragments.fragments }, ListStyleTypeContent::GeneratedContent(info) => vec![Fragment::new( diff --git a/components/layout/context.rs b/components/layout/context.rs index d43210c141b..5cedf5d38aa 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -4,7 +4,6 @@ //! Data needed by layout. -use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::sync::{Arc, Mutex}; @@ -13,7 +12,6 @@ use std::thread; use fnv::FnvHasher; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context::FontContext; -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use msg::constellation_msg::PipelineId; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, @@ -29,32 +27,6 @@ use crate::display_list::items::{OpaqueNode, WebRenderImageInfo}; pub type LayoutFontContext = FontContext<FontCacheThread>; -thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<LayoutFontContext>> = RefCell::new(None)); - -pub fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R -where - F: FnOnce(&mut LayoutFontContext) -> R, -{ - FONT_CONTEXT_KEY.with(|k| { - let mut font_context = k.borrow_mut(); - if font_context.is_none() { - let font_cache_thread = layout_context.font_cache_thread.lock().unwrap().clone(); - *font_context = Some(FontContext::new(font_cache_thread)); - } - f(&mut RefMut::map(font_context, |x| x.as_mut().unwrap())) - }) -} - -pub fn malloc_size_of_persistent_local_context(ops: &mut MallocSizeOfOps) -> usize { - FONT_CONTEXT_KEY.with(|r| { - if let Some(ref context) = *r.borrow() { - context.size_of(ops) - } else { - 0 - } - }) -} - type WebrenderImageCache = HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>; @@ -72,8 +44,8 @@ pub struct LayoutContext<'a> { /// Reference to the script thread image cache. pub image_cache: Arc<dyn ImageCache>, - /// Interface to the font cache thread. - pub font_cache_thread: Mutex<FontCacheThread>, + /// A FontContext to be used during layout. + pub font_context: Arc<FontContext<FontCacheThread>>, /// A cache of WebRender image info. pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>, diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 8e50d1c499e..45843627493 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -57,7 +57,7 @@ use style::values::generics::transform; use webrender_api::units::LayoutTransform; use webrender_api::{self, ImageKey}; -use crate::context::{with_thread_local_font_context, LayoutContext}; +use crate::context::LayoutContext; use crate::display_list::items::{ClipScrollNodeIndex, OpaqueNode, BLUR_INFLATION_FACTOR}; use crate::display_list::ToLayout; use crate::floats::ClearType; @@ -852,9 +852,8 @@ impl Fragment { ))), ); unscanned_ellipsis_fragments.push_back(ellipsis_fragment); - let ellipsis_fragments = with_thread_local_font_context(layout_context, |font_context| { - TextRunScanner::new().scan_for_runs(font_context, unscanned_ellipsis_fragments) - }); + let ellipsis_fragments = TextRunScanner::new() + .scan_for_runs(&layout_context.font_context, unscanned_ellipsis_fragments); debug_assert_eq!(ellipsis_fragments.len(), 1); ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap(); ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS; @@ -2346,9 +2345,10 @@ impl Fragment { return InlineMetrics::new(Au(0), Au(0), Au(0)); } // See CSS 2.1 § 10.8.1. - let font_metrics = with_thread_local_font_context(layout_context, |font_context| { - text::font_metrics_for_style(font_context, self_.style.clone_font()) - }); + let font_metrics = text::font_metrics_for_style( + &layout_context.font_context, + self_.style.clone_font(), + ); let line_height = text::line_height_from_style(&self_.style, &font_metrics); InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height) } @@ -2426,10 +2426,10 @@ impl Fragment { VerticalAlign::Keyword(kw) => match kw { VerticalAlignKeyword::Baseline => {}, VerticalAlignKeyword::Middle => { - let font_metrics = - with_thread_local_font_context(layout_context, |font_context| { - text::font_metrics_for_style(font_context, self.style.clone_font()) - }); + let font_metrics = text::font_metrics_for_style( + &layout_context.font_context, + self.style.clone_font(), + ); offset += (content_inline_metrics.ascent - content_inline_metrics.space_below_baseline - font_metrics.x_height) diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs index 862e8c88191..3a50024d522 100644 --- a/components/layout/generated_content.rs +++ b/components/layout/generated_content.rs @@ -20,7 +20,7 @@ use style::servo::restyle_damage::ServoRestyleDamage; use style::values::generics::counters::ContentItem; use style::values::specified::list::{QuotePair, Quotes}; -use crate::context::{with_thread_local_font_context, LayoutContext}; +use crate::context::LayoutContext; use crate::display_list::items::OpaqueNode; use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils}; use crate::fragment::{ @@ -493,9 +493,7 @@ fn render_text( )); // FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen // due to text run splitting. - let fragments = with_thread_local_font_context(layout_context, |font_context| { - TextRunScanner::new().scan_for_runs(font_context, fragments) - }); + let fragments = TextRunScanner::new().scan_for_runs(&layout_context.font_context, fragments); if fragments.is_empty() { None } else { diff --git a/components/layout/inline.rs b/components/layout/inline.rs index fa2ebb01efb..5926e4b2e03 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -1239,7 +1239,7 @@ impl InlineFlow { /// `style` is the style of the block. pub fn minimum_line_metrics( &self, - font_context: &mut LayoutFontContext, + font_context: &LayoutFontContext, style: &ComputedValues, ) -> LineMetrics { InlineFlow::minimum_line_metrics_for_fragments( @@ -1255,7 +1255,7 @@ impl InlineFlow { /// `style` is the style of the block that these fragments belong to. pub fn minimum_line_metrics_for_fragments( fragments: &[Fragment], - font_context: &mut LayoutFontContext, + font_context: &LayoutFontContext, style: &ComputedValues, ) -> LineMetrics { // As a special case, if this flow contains only hypothetical fragments, then the entire diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 601046fc0c8..a7c44164ac1 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -14,7 +14,7 @@ use style::properties::ComputedValues; use style::servo::restyle_damage::ServoRestyleDamage; use crate::block::BlockFlow; -use crate::context::{with_thread_local_font_context, LayoutContext}; +use crate::context::LayoutContext; use crate::display_list::items::DisplayListSection; use crate::display_list::{ BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState, @@ -114,13 +114,11 @@ impl ListItemFlow { fn assign_marker_block_sizes(&mut self, layout_context: &LayoutContext) { // FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does? - let marker_line_metrics = with_thread_local_font_context(layout_context, |font_context| { - InlineFlow::minimum_line_metrics_for_fragments( - &self.marker_fragments, - font_context, - &self.block_flow.fragment.style, - ) - }); + let marker_line_metrics = InlineFlow::minimum_line_metrics_for_fragments( + &self.marker_fragments, + &layout_context.font_context, + &self.block_flow.fragment.style, + ); for marker in &mut self.marker_fragments { marker.assign_replaced_block_size_if_necessary(); diff --git a/components/layout/text.rs b/components/layout/text.rs index b11a25f8dee..2875ccd0d77 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -70,7 +70,7 @@ impl TextRunScanner { pub fn scan_for_runs( &mut self, - font_context: &mut LayoutFontContext, + font_context: &LayoutFontContext, mut fragments: LinkedList<Fragment>, ) -> InlineFragments { debug!( @@ -150,7 +150,7 @@ impl TextRunScanner { /// be adjusted. fn flush_clump_to_list( &mut self, - font_context: &mut LayoutFontContext, + font_context: &LayoutFontContext, out_fragments: &mut Vec<Fragment>, paragraph_bytes_processed: &mut usize, bidi_levels: Option<&[bidi::Level]>, @@ -203,10 +203,9 @@ impl TextRunScanner { .map(|l| l.into()) .unwrap_or_else(|| { let space_width = font_group - .borrow_mut() + .write() .find_by_codepoint(font_context, ' ') .and_then(|font| { - let font = font.borrow(); font.glyph_index(' ') .map(|glyph_id| font.glyph_h_advance(glyph_id)) }) @@ -248,7 +247,7 @@ impl TextRunScanner { for (byte_index, character) in text.char_indices() { if !character.is_control() { let font = font_group - .borrow_mut() + .write() .find_by_codepoint(font_context, character); let bidi_level = match bidi_levels { @@ -367,7 +366,7 @@ impl TextRunScanner { // If no font is found (including fallbacks), there's no way we can render. let font = match run_info .font - .or_else(|| font_group.borrow_mut().first(font_context)) + .or_else(|| font_group.write().first(font_context)) { Some(font) => font, None => { @@ -377,7 +376,7 @@ impl TextRunScanner { }; let (run, break_at_zero) = TextRun::new( - &mut font.borrow_mut(), + font, run_info.text, &options, run_info.bidi_level, @@ -535,14 +534,12 @@ fn bounding_box_for_run_metrics( /// Panics if no font can be found for the given font style. #[inline] pub fn font_metrics_for_style( - font_context: &mut LayoutFontContext, + font_context: &LayoutFontContext, style: crate::ServoArc<FontStyleStruct>, ) -> FontMetrics { let font_group = font_context.font_group(style); - let font = font_group.borrow_mut().first(font_context); - let font = font.as_ref().unwrap().borrow(); - - font.metrics.clone() + let font = font_group.write().first(font_context); + font.as_ref().unwrap().metrics.clone() } /// Returns the line block-size needed by the given computed style and font size. @@ -664,10 +661,8 @@ impl RunInfo { fn has_font(&self, font: &Option<FontRef>) -> bool { fn identifier_and_pt_size(font: &Option<FontRef>) -> Option<(FontIdentifier, Au)> { - font.as_ref().map(|font| { - let font = font.borrow(); - (font.identifier().clone(), font.descriptor.pt_size) - }) + font.as_ref() + .map(|font| (font.identifier().clone(), font.descriptor.pt_size)) } identifier_and_pt_size(&self.font) == identifier_and_pt_size(font) diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs index 8ffc3386e3e..500f16093b1 100644 --- a/components/layout_2020/context.rs +++ b/components/layout_2020/context.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::RefCell; use std::sync::{Arc, Mutex}; use fnv::FnvHashMap; @@ -12,7 +11,7 @@ use msg::constellation_msg::PipelineId; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; -use parking_lot::{ReentrantMutex, RwLock}; +use parking_lot::RwLock; use script_layout_interface::{PendingImage, PendingImageState}; use servo_url::{ImmutableOrigin, ServoUrl}; use style::context::SharedStyleContext; @@ -20,8 +19,6 @@ use style::dom::OpaqueNode; use crate::display_list::WebRenderImageInfo; -thread_local!(static FONT_CONTEXT: RefCell<Option<FontContext<FontCacheThread>>> = RefCell::new(None)); - pub struct LayoutContext<'a> { pub id: PipelineId, pub use_rayon: bool, @@ -31,7 +28,7 @@ pub struct LayoutContext<'a> { pub style_context: SharedStyleContext<'a>, /// A FontContext to be used during layout. - pub font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>, + pub font_context: Arc<FontContext<FontCacheThread>>, /// Reference to the script thread image cache. pub image_cache: Arc<dyn ImageCache>, @@ -133,27 +130,4 @@ impl<'a> LayoutContext<'a> { None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None, } } - - pub fn with_font_context<F, R>(&self, callback: F) -> R - where - F: FnOnce(&mut FontContext<FontCacheThread>) -> R, - { - with_thread_local_font_context(&self.font_cache_thread, callback) - } -} - -pub fn with_thread_local_font_context<F, R>( - font_cache_thread: &ReentrantMutex<FontCacheThread>, - callback: F, -) -> R -where - F: FnOnce(&mut FontContext<FontCacheThread>) -> R, -{ - FONT_CONTEXT.with(|font_context| { - callback( - font_context - .borrow_mut() - .get_or_insert_with(|| FontContext::new(font_cache_thread.lock().clone())), - ) - }) } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index cadf5840e32..047cfc7087d 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -1638,10 +1638,9 @@ impl InlineFormattingContext { // It's unfortunate that it isn't possible to get this during IFC text processing, but in // that situation the style of the containing block is unknown. - let default_font_metrics = layout_context.with_font_context(|font_context| { - get_font_for_first_font_for_style(style, font_context) - .map(|font| font.borrow().metrics.clone()) - }); + let default_font_metrics = + get_font_for_first_font_for_style(style, &layout_context.font_context) + .map(|font| font.metrics.clone()); let style_text = containing_block.style.get_inherited_text(); let mut ifc = InlineFormattingContextState { @@ -1768,34 +1767,30 @@ impl InlineFormattingContext { // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary. let mut on_word_boundary = true; - layout_context.with_font_context(|font_context| { - let mut linebreaker = None; - self.foreach(|iter_item| match iter_item { - InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun( - ref mut text_run, - )) => { - text_run.break_and_shape( - font_context, - &mut linebreaker, - &mut ifc_fonts, - &mut last_inline_box_ended_with_white_space, - &mut on_word_boundary, - ); - }, - InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => { - if let Some(font) = - get_font_for_first_font_for_style(&inline_box.style, font_context) - { - inline_box.default_font_index = - Some(add_or_get_font(&font, &mut ifc_fonts)); - } - }, - InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => { - last_inline_box_ended_with_white_space = false; - on_word_boundary = true; - }, - _ => {}, - }); + let mut linebreaker = None; + self.foreach(|iter_item| match iter_item { + InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(ref mut text_run)) => { + text_run.break_and_shape( + &layout_context.font_context, + &mut linebreaker, + &mut ifc_fonts, + &mut last_inline_box_ended_with_white_space, + &mut on_word_boundary, + ); + }, + InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => { + if let Some(font) = get_font_for_first_font_for_style( + &inline_box.style, + &layout_context.font_context, + ) { + inline_box.default_font_index = Some(add_or_get_font(&font, &mut ifc_fonts)); + } + }, + InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => { + last_inline_box_ended_with_white_space = false; + on_word_boundary = true; + }, + _ => {}, }); self.font_metrics = ifc_fonts; diff --git a/components/layout_2020/flow/text_run.rs b/components/layout_2020/flow/text_run.rs index 86884565d57..e82d1a6159b 100644 --- a/components/layout_2020/flow/text_run.rs +++ b/components/layout_2020/flow/text_run.rs @@ -111,7 +111,7 @@ impl TextRunSegment { /// compatible with this segment or false otherwise. fn update_if_compatible( &mut self, - font: &FontRef, + new_font: &FontRef, script: Script, fonts: &[FontKeyAndMetrics], ) -> bool { @@ -120,7 +120,6 @@ impl TextRunSegment { } let current_font_key_and_metrics = &fonts[self.font_index]; - let new_font = font.borrow(); if new_font.font_key != current_font_key_and_metrics.key || new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size { @@ -208,7 +207,7 @@ impl TextRun { pub(super) fn break_and_shape( &mut self, - font_context: &mut FontContext<FontCacheThread>, + font_context: &FontContext<FontCacheThread>, linebreaker: &mut Option<LineBreakLeafIter>, font_cache: &mut Vec<FontKeyAndMetrics>, last_inline_box_ended_with_collapsible_white_space: &mut bool, @@ -244,7 +243,6 @@ impl TextRun { let segments = segment_results .into_iter() .map(|(mut segment, font)| { - let mut font = font.borrow_mut(); let word_spacing = style_word_spacing.unwrap_or_else(|| { let space_width = font .glyph_index(' ') @@ -260,7 +258,7 @@ impl TextRun { }; (segment.runs, segment.break_at_start) = gfx::text::text_run::TextRun::break_and_shape( - &mut font, + font, &self.text [segment.range.begin().0 as usize..segment.range.end().0 as usize], &shaping_options, @@ -280,7 +278,7 @@ impl TextRun { /// [`super::InlineFormattingContext`]. fn segment_text( &mut self, - font_context: &mut FontContext<FontCacheThread>, + font_context: &FontContext<FontCacheThread>, font_cache: &mut Vec<FontKeyAndMetrics>, last_inline_box_ended_with_collapsible_white_space: &mut bool, on_word_boundary: &mut bool, @@ -341,7 +339,7 @@ impl TextRun { } let font = match font_group - .borrow_mut() + .write() .find_by_codepoint(font_context, character) { Some(font) => font, @@ -383,7 +381,7 @@ impl TextRun { // Either we have a current segment or we only had control character and whitespace. In both // of those cases, just use the first font. if current.is_none() { - current = font_group.borrow_mut().first(font_context).map(|font| { + current = font_group.write().first(font_context).map(|font| { let font_index = add_or_get_font(&font, font_cache); ( TextRunSegment::new(font_index, Script::Common, ByteIndex(0)), @@ -489,7 +487,6 @@ fn char_does_not_change_font(character: char) -> bool { } pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize { - let font = font.borrow(); for (index, ifc_font_info) in ifc_fonts.iter().enumerate() { if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size { return index; @@ -505,11 +502,11 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetr pub(super) fn get_font_for_first_font_for_style( style: &ComputedValues, - font_context: &mut FontContext<FontCacheThread>, + font_context: &FontContext<FontCacheThread>, ) -> Option<FontRef> { let font = font_context .font_group(style.clone_font()) - .borrow_mut() + .write() .first(font_context); if font.is_none() { warn!("Could not find font for style: {:?}", style.clone_font()); diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index ccd0f37d829..589a2744364 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -22,16 +22,15 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as use euclid::{Point2D, Rect, Scale, Size2D}; use fnv::FnvHashMap; use fxhash::{FxHashMap, FxHashSet}; +use gfx::font; use gfx::font_cache_thread::FontCacheThread; -use gfx::{font, font_context}; +use gfx::font_context::FontContext; use gfx_traits::{node_id_from_scroll_id, Epoch}; use histogram::Histogram; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use layout::construct::ConstructionResult; -use layout::context::{ - malloc_size_of_persistent_local_context, LayoutContext, RegisteredPainter, RegisteredPainters, -}; +use layout::context::{LayoutContext, RegisteredPainter, RegisteredPainters}; use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo}; use layout::display_list::{IndexableText, ToLayout}; use layout::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils}; @@ -136,6 +135,9 @@ pub struct LayoutThread { /// Public interface to the font cache thread. font_cache_thread: FontCacheThread, + /// A FontContext to be used during layout. + font_context: Arc<FontContext<FontCacheThread>>, + /// Is this the first reflow in this LayoutThread? first_reflow: Cell<bool>, @@ -513,7 +515,7 @@ impl Layout for LayoutThread { // malloc_enclosing_size_of function. let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None); - // FIXME(njn): Just measuring the display tree for now. + // TODO: Measure more than just display list, stylist, and font context. let display_list = self.display_list.borrow(); let display_list_ref = display_list.as_ref(); let formatted_url = &format!("url({})", self.url); @@ -528,13 +530,6 @@ impl Layout for LayoutThread { kind: ReportKind::ExplicitJemallocHeapSize, size: self.stylist.size_of(&mut ops), }); - - // The LayoutThread has data in Persistent TLS... - reports.push(Report { - path: path![formatted_url, "layout-thread", "local-context"], - kind: ReportKind::ExplicitJemallocHeapSize, - size: malloc_size_of_persistent_local_context(&mut ops), - }); } fn reflow(&mut self, script_reflow: script_layout_interface::ScriptReflow) { @@ -573,6 +568,7 @@ impl LayoutThread { // Let webrender know about this pipeline by sending an empty display list. webrender_api.send_initial_transaction(id.into()); + let font_context = Arc::new(FontContext::new(font_cache_thread.clone())); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, @@ -602,6 +598,7 @@ impl LayoutThread { registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, font_cache_thread, + font_context, first_reflow: Cell::new(true), font_cache_sender: ipc_font_cache_sender, parallel_flag: true, @@ -675,7 +672,7 @@ impl LayoutThread { traversal_flags, ), image_cache: self.image_cache.clone(), - font_cache_thread: Mutex::new(self.font_cache_thread.clone()), + font_context: self.font_context.clone(), webrender_image_cache: self.webrender_image_cache.clone(), pending_images: Mutex::new(vec![]), registered_painters: &self.registered_painters, @@ -726,7 +723,7 @@ impl LayoutThread { } fn handle_web_font_loaded(&self) { - font_context::invalidate_font_caches(); + self.font_context.invalidate_caches(); self.script_chan .send(ConstellationControlMsg::WebFontLoaded(self.id)) .unwrap(); diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index d0a9eccfef9..c23e6f55fd4 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -22,7 +22,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D}; use fnv::FnvHashMap; use fxhash::FxHashMap; use gfx::font_cache_thread::FontCacheThread; -use gfx::font_context; +use gfx::font_context::FontContext; use gfx_traits::{node_id_from_scroll_id, Epoch}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; @@ -41,7 +41,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; -use parking_lot::{ReentrantMutex, RwLock}; +use parking_lot::RwLock; use profile_traits::mem::{Report, ReportKind}; use profile_traits::path; use profile_traits::time::{ @@ -121,7 +121,10 @@ pub struct LayoutThread { /// Public interface to the font cache thread. This needs to be behind a [`ReentrantMutex`], /// because some font cache operations can trigger others. - font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>, + font_cache_thread: FontCacheThread, + + /// A FontContext to be used during layout. + font_context: Arc<FontContext<FontCacheThread>>, /// Is this the first reflow in this LayoutThread? first_reflow: Cell<bool>, @@ -484,13 +487,13 @@ impl LayoutThread { // The device pixel ratio is incorrect (it does not have the hidpi value), // but it will be set correctly when the initial reflow takes place. - let font_cache_thread = Arc::new(ReentrantMutex::new(font_cache_thread)); + let font_context = Arc::new(FontContext::new(font_cache_thread.clone())); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, window_size.initial_viewport, window_size.device_pixel_ratio, - Box::new(LayoutFontMetricsProvider(font_cache_thread.clone())), + Box::new(LayoutFontMetricsProvider(font_context.clone())), ); // Ask the router to proxy IPC messages from the font cache thread to layout. @@ -514,6 +517,7 @@ impl LayoutThread { registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, font_cache_thread, + font_context, first_reflow: Cell::new(true), font_cache_sender: ipc_font_cache_sender, generation: Cell::new(0), @@ -584,7 +588,7 @@ impl LayoutThread { traversal_flags, ), image_cache: self.image_cache.clone(), - font_cache_thread: self.font_cache_thread.clone(), + font_context: self.font_context.clone(), webrender_image_cache: self.webrender_image_cache.clone(), pending_images: Mutex::new(vec![]), use_rayon, @@ -616,10 +620,8 @@ impl LayoutThread { // Find all font-face rules and notify the font cache of them. // GWTODO: Need to handle unloading web fonts. if stylesheet.is_effective_for_device(self.stylist.device(), guard) { - let newly_loading_font_count = self - .font_cache_thread - .lock() - .add_all_web_fonts_from_stylesheet( + let newly_loading_font_count = + self.font_cache_thread.add_all_web_fonts_from_stylesheet( stylesheet, guard, self.stylist.device(), @@ -637,7 +639,7 @@ impl LayoutThread { } fn handle_web_font_loaded(&self) { - font_context::invalidate_font_caches(); + self.font_context.invalidate_caches(); self.script_chan .send(ConstellationControlMsg::WebFontLoaded(self.id)) .unwrap(); @@ -1076,7 +1078,7 @@ impl LayoutThread { self.stylist.quirks_mode(), window_size_data.initial_viewport, window_size_data.device_pixel_ratio, - Box::new(LayoutFontMetricsProvider(self.font_cache_thread.clone())), + Box::new(LayoutFontMetricsProvider(self.font_context.clone())), ); // Preserve any previously computed root font size. @@ -1235,7 +1237,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl { } #[derive(Debug)] -struct LayoutFontMetricsProvider(Arc<ReentrantMutex<FontCacheThread>>); +struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>); impl FontMetricsProvider for LayoutFontMetricsProvider { fn query_font_metrics( @@ -1246,55 +1248,55 @@ impl FontMetricsProvider for LayoutFontMetricsProvider { _in_media_query: bool, _retrieve_math_scales: bool, ) -> FontMetrics { - layout::context::with_thread_local_font_context(&self.0, move |font_context| { - let font_group = - font_context.font_group_with_size(ServoArc::new(font.clone()), base_size.into()); - let Some(first_font_metrics) = font_group - .borrow_mut() - .first(font_context) - .map(|font| font.borrow().metrics.clone()) - else { - return Default::default(); - }; + let font_context = &self.0; + let font_group = self + .0 + .font_group_with_size(ServoArc::new(font.clone()), base_size.into()); + + let Some(first_font_metrics) = font_group + .write() + .first(font_context) + .map(|font| font.metrics.clone()) + else { + return Default::default(); + }; - // Only use the x-height of this font if it is non-zero. Some fonts return - // inaccurate metrics, which shouldn't be used. - let x_height = Some(first_font_metrics.x_height) - .filter(|x_height| !x_height.is_zero()) - .map(CSSPixelLength::from); - - let zero_advance_measure = first_font_metrics - .zero_horizontal_advance - .or_else(|| { - font_group - .borrow_mut() - .find_by_codepoint(font_context, '0')? - .borrow() - .metrics - .zero_horizontal_advance - }) - .map(CSSPixelLength::from); - let ic_width = first_font_metrics - .ic_horizontal_advance - .or_else(|| { - font_group - .borrow_mut() - .find_by_codepoint(font_context, '\u{6C34}')? - .borrow() - .metrics - .ic_horizontal_advance - }) - .map(CSSPixelLength::from); - - FontMetrics { - x_height, - zero_advance_measure, - cap_height: None, - ic_width, - ascent: first_font_metrics.ascent.into(), - script_percent_scale_down: None, - script_script_percent_scale_down: None, - } - }) + // Only use the x-height of this font if it is non-zero. Some fonts return + // inaccurate metrics, which shouldn't be used. + let x_height = Some(first_font_metrics.x_height) + .filter(|x_height| !x_height.is_zero()) + .map(CSSPixelLength::from); + + let zero_advance_measure = first_font_metrics + .zero_horizontal_advance + .or_else(|| { + font_group + .write() + .find_by_codepoint(font_context, '0')? + .metrics + .zero_horizontal_advance + }) + .map(CSSPixelLength::from); + + let ic_width = first_font_metrics + .ic_horizontal_advance + .or_else(|| { + font_group + .write() + .find_by_codepoint(font_context, '\u{6C34}')? + .metrics + .ic_horizontal_advance + }) + .map(CSSPixelLength::from); + + FontMetrics { + x_height, + zero_advance_measure, + cap_height: None, + ic_width, + ascent: first_font_metrics.ascent.into(), + script_percent_scale_down: None, + script_script_percent_scale_down: None, + } } } |