diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 3aa36ac2023..33254113e56 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -10,8 +10,8 @@ use canvas_traits::canvas::*; use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::point2; use fonts::{ - ByteIndex, FontBaseline, FontCacheThread, FontContext, FontGroup, FontMetrics, FontRef, - GlyphInfo, GlyphStore, ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE, + ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore, + ShapingFlags, ShapingOptions, SystemFontServiceProxy, LAST_RESORT_GLYPH_ADVANCE, }; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::{debug, warn}; @@ -434,7 +434,7 @@ pub struct CanvasData<'a> { old_image_key: Option, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option, - font_context: Arc>, + font_context: Arc>, } fn create_backend() -> Box { @@ -446,7 +446,7 @@ impl<'a> CanvasData<'a> { size: Size2D, webrender_api: Box, antialias: AntialiasMode, - font_context: Arc>, + font_context: Arc>, ) -> CanvasData<'a> { let backend = create_backend(); let draw_target = backend.create_drawtarget(size); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 37ccace4592..4f9007c9a52 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -11,7 +11,7 @@ use canvas_traits::canvas::*; use canvas_traits::ConstellationCanvasMsg; use crossbeam_channel::{select, unbounded, Sender}; use euclid::default::Size2D; -use fonts::{FontCacheThread, FontContext}; +use fonts::{FontContext, SystemFontServiceProxy}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; @@ -37,20 +37,20 @@ pub struct CanvasPaintThread<'a> { canvases: HashMap>, next_canvas_id: CanvasId, webrender_api: Box, - font_context: Arc>, + font_context: Arc>, } impl<'a> CanvasPaintThread<'a> { fn new( webrender_api: Box, - font_cache_thread: FontCacheThread, + system_font_service: Arc, resource_threads: ResourceThreads, ) -> CanvasPaintThread<'a> { CanvasPaintThread { canvases: HashMap::new(), next_canvas_id: CanvasId(0), webrender_api, - font_context: Arc::new(FontContext::new(font_cache_thread, resource_threads)), + font_context: Arc::new(FontContext::new(system_font_service, resource_threads)), } } @@ -58,7 +58,7 @@ impl<'a> CanvasPaintThread<'a> { /// communicate with it. pub fn start( webrender_api: Box, - font_cache_thread: FontCacheThread, + system_font_service: Arc, resource_threads: ResourceThreads, ) -> (Sender, IpcSender) { let (ipc_sender, ipc_receiver) = ipc::channel::().unwrap(); @@ -68,7 +68,7 @@ impl<'a> CanvasPaintThread<'a> { .name("Canvas".to_owned()) .spawn(move || { let mut canvas_paint_thread = CanvasPaintThread::new( - webrender_api, font_cache_thread, resource_threads); + webrender_api, system_font_service, resource_threads); loop { select! { recv(msg_receiver) -> msg => { diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 1ddf7906845..cccf2bf70d1 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -555,7 +555,9 @@ impl GenericDrawTarget for raqote::DrawTarget { SHARED_FONT_CACHE.with(|font_cache| { let identifier = template.identifier(); if !font_cache.borrow().contains_key(&identifier) { - let Ok(font) = Font::from_bytes(template.data(), identifier.index()) else { + let Ok(font) = + Font::from_bytes(run.font.data.as_arc().clone(), identifier.index()) + else { return; }; font_cache.borrow_mut().insert(identifier.clone(), font); diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 02ed570ae2c..ab3f4ae0bfb 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -895,12 +895,11 @@ impl IOCompositor { ForwardedToCompositorMsg::Font(FontToCompositorMsg::AddFont( key_sender, index, - bytes_receiver, + data, )) => { let font_key = self.webrender_api.generate_font_key(); let mut transaction = Transaction::new(); - let bytes = bytes_receiver.recv().unwrap_or_default(); - transaction.add_raw_font(font_key, bytes, index); + transaction.add_raw_font(font_key, (**data).into(), index); self.webrender_api .send_transaction(self.webrender_document, transaction); let _ = key_sender.send(font_key); diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index eb5876fb802..4765f651425 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -121,7 +121,7 @@ use embedder_traits::{ }; use euclid::default::Size2D as UntypedSize2D; use euclid::Size2D; -use fonts::FontCacheThread; +use fonts::SystemFontServiceProxy; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error as IpcError; @@ -346,7 +346,7 @@ pub struct Constellation { /// A channel for the constellation to send messages to the font /// cache thread. - font_cache_thread: FontCacheThread, + system_font_service: Arc, /// A channel for the constellation to send messages to the /// devtools thread. @@ -515,8 +515,8 @@ pub struct InitialConstellationState { /// A channel to the bluetooth thread. pub bluetooth_thread: IpcSender, - /// A channel to the font cache thread. - pub font_cache_thread: FontCacheThread, + /// A proxy to the `SystemFontService` which manages the list of system fonts. + pub system_font_service: Arc, /// A channel to the resource thread. pub public_resource_threads: ResourceThreads, @@ -762,7 +762,7 @@ where bluetooth_ipc_sender: state.bluetooth_thread, public_resource_threads: state.public_resource_threads, private_resource_threads: state.private_resource_threads, - font_cache_thread: state.font_cache_thread, + system_font_service: state.system_font_service, sw_managers: Default::default(), swmanager_receiver, swmanager_ipc_sender, @@ -1042,7 +1042,7 @@ where devtools_sender: self.devtools_sender.clone(), bluetooth_thread: self.bluetooth_ipc_sender.clone(), swmanager_thread: self.swmanager_ipc_sender.clone(), - font_cache_thread: self.font_cache_thread.clone(), + system_font_service: self.system_font_service.clone(), resource_threads, time_profiler_chan: self.time_profiler_chan.clone(), mem_profiler_chan: self.mem_profiler_chan.clone(), @@ -2760,8 +2760,8 @@ where } } - debug!("Exiting font cache thread."); - self.font_cache_thread.exit(); + debug!("Exiting the system font service thread."); + self.system_font_service.exit(); // Receive exit signals from threads. if let Err(e) = core_ipc_receiver.recv() { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 71ed997e11c..b646e581200 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -21,7 +21,7 @@ use canvas_traits::webgl::WebGLPipeline; use compositing_traits::{CompositionPipeline, CompositorMsg, CompositorProxy}; use crossbeam_channel::{unbounded, Sender}; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; -use fonts::FontCacheThread; +use fonts::{SystemFontServiceProxy, SystemFontServiceProxySender}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error; @@ -153,8 +153,8 @@ pub struct InitialPipelineState { /// A channel to the service worker manager thread pub swmanager_thread: IpcSender, - /// A channel to the font cache thread. - pub font_cache_thread: FontCacheThread, + /// A proxy to the system font service, responsible for managing the list of system fonts. + pub system_font_service: Arc, /// Channels to the resource-related threads. pub resource_threads: ResourceThreads, @@ -281,7 +281,7 @@ impl Pipeline { devtools_ipc_sender: script_to_devtools_ipc_sender, bluetooth_thread: state.bluetooth_thread, swmanager_thread: state.swmanager_thread, - font_cache_thread: state.font_cache_thread, + system_font_service: state.system_font_service.to_sender(), resource_threads: state.resource_threads, time_profiler_chan: state.time_profiler_chan, mem_profiler_chan: state.mem_profiler_chan, @@ -487,7 +487,7 @@ pub struct UnprivilegedPipelineContent { devtools_ipc_sender: Option>, bluetooth_thread: IpcSender, swmanager_thread: IpcSender, - font_cache_thread: FontCacheThread, + system_font_service: SystemFontServiceProxySender, resource_threads: ResourceThreads, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan, @@ -550,7 +550,7 @@ impl UnprivilegedPipelineContent { inherited_secure_context: self.load_data.inherited_secure_context, }, layout_factory, - self.font_cache_thread.clone(), + Arc::new(self.system_font_service.to_proxy()), self.load_data.clone(), self.user_agent, ); diff --git a/components/fonts/font.rs b/components/fonts/font.rs index e423c546dae..3af077b4e06 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -27,14 +27,14 @@ use style::values::computed::{FontStretch, FontStyle, FontWeight}; use unicode_script::Script; use webrender_api::{FontInstanceFlags, FontInstanceKey}; -use crate::font_cache_thread::{FontIdentifier, FontSource}; use crate::font_context::FontContext; use crate::font_template::{FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods}; use crate::platform::font::{FontTable, PlatformFont}; pub use crate::platform::font_list::fallback_font_families; +use crate::system_font_service::{FontIdentifier, SystemFontServiceProxyTrait}; use crate::{ - ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, GlyphData, GlyphId, - GlyphStore, Shaper, + ByteIndex, EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, GlyphData, + GlyphId, GlyphStore, Shaper, }; #[macro_export] @@ -66,11 +66,13 @@ pub trait PlatformFontMethods: Sized { fn new_from_template( template: FontTemplateRef, pt_size: Option, + data: &Arc>, ) -> Result { - let data = template.data(); + let template = template.borrow(); + let face_index = template.identifier().index(); - let font_identifier = template.borrow().identifier.clone(); - Self::new_from_data(font_identifier, data, face_index, pt_size) + let font_identifier = template.identifier.clone(); + Self::new_from_data(font_identifier, data.clone(), face_index, pt_size) } fn new_from_data( @@ -218,6 +220,7 @@ impl malloc_size_of::MallocSizeOf for CachedShapeData { #[derive(Debug)] pub struct Font { pub handle: PlatformFont, + pub data: Arc, pub template: FontTemplateRef, pub metrics: FontMetrics, pub descriptor: FontDescriptor, @@ -251,13 +254,19 @@ impl Font { pub fn new( template: FontTemplateRef, descriptor: FontDescriptor, + data: Arc, synthesized_small_caps: Option, ) -> Result { - let handle = PlatformFont::new_from_template(template.clone(), Some(descriptor.pt_size))?; + let handle = PlatformFont::new_from_template( + template.clone(), + Some(descriptor.pt_size), + data.as_arc(), + )?; let metrics = handle.metrics(); Ok(Font { handle, + data, template, shaper: OnceLock::new(), descriptor, @@ -526,7 +535,7 @@ impl FontGroup { /// `codepoint`. If no such font is found, returns the first available font or fallback font /// (which will cause a "glyph not found" character to be rendered). If no font at all can be /// found, returns None. - pub fn find_by_codepoint( + pub fn find_by_codepoint( &mut self, font_context: &FontContext, codepoint: char, @@ -598,7 +607,10 @@ impl FontGroup { } /// Find the first available font in the group, or the first available fallback font. - pub fn first(&mut self, font_context: &FontContext) -> Option { + pub fn first( + &mut self, + font_context: &FontContext, + ) -> Option { // 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 @@ -629,7 +641,7 @@ impl FontGroup { font_predicate: FontPredicate, ) -> Option where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -659,7 +671,7 @@ impl FontGroup { font_predicate: FontPredicate, ) -> Option where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -731,7 +743,7 @@ impl FontGroupFamily { font_predicate: &FontPredicate, ) -> Option where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -754,7 +766,7 @@ impl FontGroupFamily { .next() } - fn members( + fn members( &mut self, font_descriptor: &FontDescriptor, font_context: &FontContext, @@ -883,6 +895,8 @@ pub(crate) fn map_platform_values_to_style_values(mapping: &[(f64, f64)], value: #[cfg(test)] mod test { + use crate::FontData; + #[cfg(target_os = "windows")] #[test] fn test_shape_text_fast() { @@ -917,14 +931,16 @@ mod test { let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path.clone()).unwrap()); let file = File::open(path).unwrap(); - let data: Arc> = Arc::new(file.bytes().map(|b| b.unwrap()).collect()); + let data = Arc::new(FontData::from_bytes( + file.bytes().map(|b| b.unwrap()).collect(), + )); let platform_font = - PlatformFont::new_from_data(identifier.clone(), data.clone(), 0, None).unwrap(); + PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None) + .unwrap(); let template = FontTemplate { identifier, descriptor: platform_font.descriptor(), - data: Some(data), stylesheet: None, }; let descriptor = FontDescriptor { @@ -937,6 +953,7 @@ mod test { let font = Font::new( Arc::new(atomic_refcell::AtomicRefCell::new(template)), descriptor, + data, None, ) .unwrap(); diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index 140fa56f14d..cdd97466341 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -19,6 +19,7 @@ use net_traits::request::{Destination, Referrer, RequestBuilder}; use net_traits::{fetch_async, CoreResourceThread, FetchResponseMsg, ResourceThreads}; use parking_lot::{Mutex, ReentrantMutex, RwLock}; use servo_arc::Arc as ServoArc; +use servo_url::ServoUrl; use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, UrlSource}; use style::media_queries::Device; @@ -33,39 +34,67 @@ use webrender_api::{FontInstanceKey, FontKey}; use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, }; -use crate::font_cache_thread::{CSSFontFaceDescriptors, FontIdentifier, FontSource}; use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore}; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; -use crate::LowercaseFontFamilyName; +use crate::platform::font::PlatformFont; +use crate::system_font_service::{ + CSSFontFaceDescriptors, FontIdentifier, SystemFontServiceProxyTrait, +}; +use crate::{FontData, LowercaseFontFamilyName, PlatformFontMethods}; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) /// The FontContext represents the per-thread/thread state necessary for /// working with fonts. It is the public API used by the layout and -/// paint code. It talks directly to the font cache thread where +/// paint code. It talks directly to the system font service where /// required. -pub struct FontContext { - font_source: ReentrantMutex, +pub struct FontContext { + pub(crate) system_font_service_proxy: Arc, resource_threads: ReentrantMutex, - cache: CachingFontSource, + + /// The actual instances of fonts ie a [`FontTemplate`] combined with a size and + /// other font properties, along with the font data and a platform font instance. + fonts: RwLock>>, + + /// A caching map between the specification of a font in CSS style and + /// resolved [`FontGroup`] which contains information about all fonts that + /// can be selected with that style. + resolved_font_groups: + RwLock>, BuildHasherDefault>>, + web_fonts: CrossThreadFontStore, webrender_font_store: CrossThreadWebRenderFontStore, have_removed_web_fonts: AtomicBool, } -impl MallocSizeOf for FontContext { +impl MallocSizeOf for FontContext { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.cache.size_of(ops) + let font_cache_size = self + .fonts + .read() + .iter() + .map(|(key, font)| { + key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops)) + }) + .sum::(); + let font_group_cache_size = self + .resolved_font_groups + .read() + .iter() + .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops)) + .sum::(); + font_cache_size + font_group_cache_size } } -impl FontContext { - pub fn new(font_source: S, resource_threads: ResourceThreads) -> FontContext { +impl FontContext { + pub fn new(system_font_service_proxy: Arc, resource_threads: ResourceThreads) -> Self { #[allow(clippy::default_constructed_unit_structs)] - FontContext { - font_source: ReentrantMutex::new(font_source.clone()), + Self { + system_font_service_proxy, resource_threads: ReentrantMutex::new(resource_threads.core_thread), - cache: CachingFontSource::new(font_source), + fonts: Default::default(), + resolved_font_groups: Default::default(), web_fonts: Arc::new(RwLock::default()), webrender_font_store: Arc::new(RwLock::default()), have_removed_web_fonts: AtomicBool::new(false), @@ -76,6 +105,14 @@ impl FontContext { self.web_fonts.read().number_of_fonts_still_loading() } + pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Arc { + self.web_fonts + .read() + .get_font_data(identifier) + .or_else(|| self.system_font_service_proxy.get_font_data(identifier)) + .expect("Could not find font data") + } + /// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed. fn handle_web_font_load_finished( &self, @@ -83,7 +120,7 @@ impl FontContext { succeeded: bool, ) { if succeeded { - self.cache.invalidate_after_web_font_load(); + self.invalidate_font_groups_after_web_font_load(); } finished_callback(succeeded); } @@ -103,7 +140,19 @@ impl FontContext { style: ServoArc, size: Au, ) -> Arc> { - self.cache.font_group_with_size(style, size) + let cache_key = FontGroupCacheKey { size, style }; + if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) { + return font_group.clone(); + } + + let mut descriptor = FontDescriptor::from(&*cache_key.style); + descriptor.pt_size = size; + + let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor))); + self.resolved_font_groups + .write() + .insert(cache_key, font_group.clone()); + font_group } /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a @@ -148,7 +197,7 @@ impl FontContext { font_descriptor: font_descriptor.clone(), }; - if let Some(font) = self.cache.fonts.read().get(&cache_key).cloned() { + if let Some(font) = self.fonts.read().get(&cache_key).cloned() { return font; } @@ -166,7 +215,7 @@ impl FontContext { synthesized_small_caps_font, ) .ok(); - self.cache.fonts.write().insert(cache_key, font.clone()); + self.fonts.write().insert(cache_key, font.clone()); font } @@ -192,7 +241,7 @@ impl FontContext { } /// Try to find matching templates in this [`FontContext`], first looking in the list of web fonts and - /// falling back to asking the [`super::FontCacheThread`] for a matching system font. + /// falling back to asking the [`super::SystemFontService`] for a matching system font. pub fn matching_templates( &self, descriptor_to_match: &FontDescriptor, @@ -200,8 +249,10 @@ impl FontContext { ) -> Vec { self.matching_web_font_templates(descriptor_to_match, family_descriptor) .unwrap_or_else(|| { - self.cache - .matching_templates(descriptor_to_match, family_descriptor) + self.system_font_service_proxy.find_matching_font_templates( + Some(descriptor_to_match), + &family_descriptor.family, + ) }) } @@ -216,18 +267,18 @@ impl FontContext { let mut font = Font::new( font_template.clone(), font_descriptor.clone(), + self.get_font_data(&font_template.identifier()), synthesized_small_caps, )?; - let font_source = self.font_source.lock(); font.font_key = match font_template.identifier() { - FontIdentifier::Local(_) => font_source.get_system_font_instance( + FontIdentifier::Local(_) => self.system_font_service_proxy.get_system_font_instance( font_template.identifier(), font_descriptor.pt_size, font.webrender_font_instance_flags(), ), FontIdentifier::Web(_) => self.webrender_font_store.write().get_font_instance( - &*font_source, + self, font_template.clone(), font_descriptor.pt_size, font.webrender_font_instance_flags(), @@ -236,6 +287,10 @@ impl FontContext { Ok(Arc::new(font)) } + + fn invalidate_font_groups_after_web_font_load(&self) { + self.resolved_font_groups.write().clear(); + } } #[derive(Clone)] @@ -263,7 +318,7 @@ pub trait FontContextWebFontMethods { -> (Vec, Vec); } -impl FontContextWebFontMethods for Arc> { +impl FontContextWebFontMethods for Arc> { fn add_all_web_fonts_from_stylesheet( &self, stylesheet: &DocumentStyleSheet, @@ -325,8 +380,7 @@ impl FontContextWebFontMethods for Arc FontContextWebFontMethods for Arc { - if let Some(new_template) = state + if let Some((new_template, font_data)) = state .local_fonts .get(&local_family_name.name) .cloned() .flatten() .and_then(|local_template| { - FontTemplate::new_for_local_web_font( - local_template, + let template = FontTemplate::new_for_local_web_font( + local_template.clone(), &state.css_font_face_descriptors, state.stylesheet.clone(), ) - .ok() + .ok()?; + let font_data = self.get_font_data(&local_template.identifier()); + Some((template, font_data)) }) { - let not_cancelled = self - .web_fonts - .write() - .handle_web_font_loaded(&state, new_template); + let not_cancelled = self.web_fonts.write().handle_web_font_loaded( + &state, + new_template, + font_data, + ); self.handle_web_font_load_finished(&state.finished_callback, not_cancelled); } else { this.process_next_web_font_source(state); @@ -401,8 +458,8 @@ impl FontContextWebFontMethods for Arc FontContextWebFontMethods for Arc = webrender_font_store @@ -459,13 +516,15 @@ impl FontContextWebFontMethods for Arc { - font_context: Arc>, +struct RemoteWebFontDownloader { + font_context: Arc>, url: ServoArc, web_font_family_name: LowercaseFontFamilyName, response_valid: Mutex, @@ -478,10 +537,10 @@ enum DownloaderResponseResult { Failure, } -impl RemoteWebFontDownloader { +impl RemoteWebFontDownloader { fn download( url_source: UrlSource, - font_context: Arc>, + font_context: Arc>, web_font_family_name: LowercaseFontFamilyName, state: WebFontDownloadState, ) { @@ -548,7 +607,7 @@ impl RemoteWebFontDownloader { ); let font_data = match fontsan::process(&font_data) { - Ok(bytes) => bytes, + Ok(bytes) => Arc::new(FontData::from_bytes(bytes)), Err(error) => { debug!( "Sanitiser rejected web font: family={} url={:?} with {error:?}", @@ -558,20 +617,28 @@ impl RemoteWebFontDownloader { }, }; - let Ok(new_template) = FontTemplate::new_for_remote_web_font( - self.url.clone().into(), - Arc::new(font_data), - &state.css_font_face_descriptors, - Some(state.stylesheet.clone()), - ) else { + let url: ServoUrl = self.url.clone().into(); + let identifier = FontIdentifier::Web(url.clone()); + let Ok(handle) = + PlatformFont::new_from_data(identifier, font_data.as_arc().clone(), 0, None) + else { + return false; + }; + let mut descriptor = handle.descriptor(); + descriptor + .override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors); + + let Ok(new_template) = + FontTemplate::new_for_remote_web_font(url, descriptor, Some(state.stylesheet.clone())) + else { return false; }; - let not_cancelled = self - .font_context - .web_fonts - .write() - .handle_web_font_loaded(state, new_template); + let not_cancelled = self.font_context.web_fonts.write().handle_web_font_loaded( + state, + new_template, + font_data, + ); self.font_context .handle_web_font_load_finished(&state.finished_callback, not_cancelled); @@ -623,121 +690,12 @@ impl RemoteWebFontDownloader { } } -#[derive(Default)] -pub struct CachingFontSource { - font_cache_thread: ReentrantMutex, - fonts: RwLock>>, - templates: RwLock>>, - resolved_font_groups: - RwLock>, BuildHasherDefault>>, -} - -impl CachingFontSource { - fn new(font_cache_thread: FCT) -> Self { - Self { - font_cache_thread: ReentrantMutex::new(font_cache_thread), - fonts: Default::default(), - templates: Default::default(), - resolved_font_groups: Default::default(), - } - } - - fn invalidate_after_web_font_load(&self) { - self.resolved_font_groups.write().clear(); - } - - pub fn matching_templates( - &self, - descriptor_to_match: &FontDescriptor, - family_descriptor: &FontFamilyDescriptor, - ) -> Vec { - let cache_key = FontTemplateCacheKey { - font_descriptor: descriptor_to_match.clone(), - family_descriptor: family_descriptor.clone(), - }; - if let Some(templates) = self.templates.read().get(&cache_key).cloned() { - return templates; - } - - debug!( - "CachingFontSource: cache miss for template_descriptor={:?} family_descriptor={:?}", - descriptor_to_match, family_descriptor - ); - let templates = self - .font_cache_thread - .lock() - .find_matching_font_templates(Some(descriptor_to_match), &family_descriptor.family); - self.templates.write().insert(cache_key, templates.clone()); - - templates - } - - pub fn font_group_with_size( - &self, - style: ServoArc, - size: Au, - ) -> Arc> { - let cache_key = FontGroupCacheKey { size, style }; - if let Some(font_group) = self.resolved_font_groups.read().get(&cache_key) { - return font_group.clone(); - } - - let mut descriptor = FontDescriptor::from(&*cache_key.style); - descriptor.pt_size = size; - - let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style, descriptor))); - self.resolved_font_groups - .write() - .insert(cache_key, font_group.clone()); - font_group - } -} - -impl MallocSizeOf for CachingFontSource { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let font_cache_size = self - .fonts - .read() - .iter() - .map(|(key, font)| { - key.size_of(ops) + font.as_ref().map_or(0, |font| (*font).size_of(ops)) - }) - .sum::(); - let font_template_cache_size = self - .templates - .read() - .iter() - .map(|(key, templates)| { - let templates_size = templates - .iter() - .map(|template| template.borrow().size_of(ops)) - .sum::(); - key.size_of(ops) + templates_size - }) - .sum::(); - let font_group_cache_size = self - .resolved_font_groups - .read() - .iter() - .map(|(key, font_group)| key.size_of(ops) + (*font_group.read()).size_of(ops)) - .sum::(); - - font_cache_size + font_template_cache_size + font_group_cache_size - } -} - #[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] struct FontCacheKey { font_identifier: FontIdentifier, font_descriptor: FontDescriptor, } -#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] -struct FontTemplateCacheKey { - font_descriptor: FontDescriptor, - family_descriptor: FontFamilyDescriptor, -} - #[derive(Debug, MallocSizeOf)] struct FontGroupCacheKey { #[ignore_malloc_size_of = "This is also stored as part of styling."] diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs index 19e710120d3..e6e5b08ef84 100644 --- a/components/fonts/font_store.rs +++ b/components/fonts/font_store.rs @@ -3,25 +3,72 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::collections::{HashMap, HashSet}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use app_units::Au; use atomic_refcell::AtomicRefCell; +use ipc_channel::ipc::IpcSharedMemory; use log::warn; use parking_lot::RwLock; +use serde::{Deserialize, Serialize}; use style::stylesheets::DocumentStyleSheet; use style::values::computed::{FontStyle, FontWeight}; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; use crate::font::FontDescriptor; -use crate::font_cache_thread::{FontIdentifier, FontSource, LowercaseFontFamilyName}; use crate::font_context::WebFontDownloadState; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique}; +use crate::system_font_service::{ + FontIdentifier, LowercaseFontFamilyName, SystemFontServiceProxyTrait, +}; +use crate::FontContext; + +/// A data structure to store data for fonts. If sent across IPC channels and only a +/// [`IpcSharedMemory`] handle is sent, avoiding the overhead of serialization and +/// deserialization. In addition, if a shared handle to data is requested +/// (`Arc>`), the data is lazily copied out of shared memory once per +/// [`FontData`]. +#[derive(Debug, Deserialize, Serialize)] +pub struct FontData { + /// The data of this font in shared memory. Suitable for sending across IPC channels. + shared_memory: Arc, + /// A lazily-initialized copy of the data behind an [`Arc`] which can be used when + /// passing it to various APIs. + #[serde(skip)] + arc: OnceLock>>, +} + +impl FontData { + pub fn from_bytes(data: Vec) -> FontData { + FontData { + shared_memory: Arc::new(IpcSharedMemory::from_bytes(&data)), + arc: Arc::new(data).into(), + } + } + + /// Return a non-shared memory `Arc` view of this data. This may copy the data once + /// per [`FontData`], but subsequent calls will return the same shared view. + pub fn as_arc(&self) -> &Arc> { + self.arc + .get_or_init(|| Arc::new((**self.shared_memory).into())) + } + + /// Return a the [`IpcSharedMemory`] view of this data suitable for sending directly across + /// an IPC channel if necessary. An `Arc` is returned to avoid the overhead of copying the + /// platform-specific shared memory handle. + pub(crate) fn as_ipc_shared_memory(&self) -> Arc { + self.shared_memory.clone() + } +} #[derive(Default)] pub struct FontStore { pub(crate) families: HashMap, web_fonts_loading: Vec<(DocumentStyleSheet, usize)>, + /// The data for each [`FontIdentifier`]. This data might be used by + /// more than one [`FontTemplate`] as each identifier refers to a URL + /// or font that can contain more than a single font. + font_data: HashMap>, } pub(crate) type CrossThreadFontStore = Arc>; @@ -78,28 +125,67 @@ impl FontStore { /// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font /// load was canceled (for instance, if the stylesheet was removed), then do nothing and return /// false. + /// + /// In addition pass newly loaded data for this font. Add this data the cached [`FontData`] store + /// inside this [`FontStore`]. pub(crate) fn handle_web_font_loaded( &mut self, state: &WebFontDownloadState, new_template: FontTemplate, + data: Arc, ) -> bool { // Abort processing this web font if the originating stylesheet was removed. if self.font_load_cancelled_for_stylesheet(&state.stylesheet) { return false; } - let family_name = state.css_font_face_descriptors.family_name.clone(); - self.families - .entry(family_name) - .or_default() - .add_template(new_template); + self.add_template_and_data(family_name, new_template, data); self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet); true } + pub(crate) fn add_template_and_data( + &mut self, + family_name: LowercaseFontFamilyName, + new_template: FontTemplate, + data: Arc, + ) { + self.font_data.insert(new_template.identifier.clone(), data); + self.families + .entry(family_name) + .or_default() + .add_template(new_template); + } + pub(crate) fn number_of_fonts_still_loading(&self) -> usize { self.web_fonts_loading.iter().map(|(_, count)| count).sum() } + + pub(crate) fn get_or_initialize_font_data( + &mut self, + identifier: &FontIdentifier, + ) -> &Arc { + self.font_data + .entry(identifier.clone()) + .or_insert_with(|| match identifier { + FontIdentifier::Local(local_identifier) => { + Arc::new(FontData::from_bytes(local_identifier.read_data_from_file())) + }, + FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), + }) + } + + pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { + self.font_data.get(identifier).cloned() + } + + pub(crate) fn remove_all_font_data_for_identifiers( + &mut self, + identifiers: &HashSet, + ) { + self.font_data + .retain(|font_identifier, _| identifiers.contains(font_identifier)); + } } #[derive(Default)] @@ -110,26 +196,32 @@ pub struct WebRenderFontStore { pub(crate) type CrossThreadWebRenderFontStore = Arc>; impl WebRenderFontStore { - pub(crate) fn get_font_instance( + pub(crate) fn get_font_instance( &mut self, - font_cache_thread: &FCT, + font_context: &FontContext, font_template: FontTemplateRef, pt_size: Au, flags: FontInstanceFlags, ) -> FontInstanceKey { let webrender_font_key_map = &mut self.webrender_font_key_map; let identifier = font_template.identifier().clone(); + let font_key = *webrender_font_key_map .entry(identifier.clone()) .or_insert_with(|| { - font_cache_thread.get_web_font(font_template.data(), identifier.index()) + let data = font_context.get_font_data(&identifier); + font_context + .system_font_service_proxy + .get_web_font(data, identifier.index()) }); *self .webrender_font_instance_map .entry((font_key, pt_size)) .or_insert_with(|| { - font_cache_thread.get_web_font_instance(font_key, pt_size.to_f32_px(), flags) + font_context + .system_font_service_proxy + .get_web_font_instance(font_key, pt_size.to_f32_px(), flags) }) } @@ -148,7 +240,7 @@ impl WebRenderFontStore { pub(crate) fn remove_all_fonts_for_identifiers( &mut self, - identifiers: HashSet, + identifiers: &HashSet, ) -> (Vec, Vec) { let mut removed_keys: HashSet = HashSet::new(); self.webrender_font_key_map.retain(|identifier, font_key| { diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs index 19c71ed83fa..d053fe9f2d4 100644 --- a/components/fonts/font_template.rs +++ b/components/fonts/font_template.rs @@ -15,12 +15,11 @@ use style::computed_values::font_style::T as FontStyle; use style::stylesheets::DocumentStyleSheet; use style::values::computed::font::FontWeight; -use crate::font::{FontDescriptor, PlatformFontMethods}; -use crate::font_cache_thread::{ +use crate::font::FontDescriptor; +use crate::platform::font_list::LocalFontIdentifier; +use crate::system_font_service::{ CSSFontFaceDescriptors, ComputedFontStyleDescriptor, FontIdentifier, }; -use crate::platform::font::PlatformFont; -use crate::platform::font_list::LocalFontIdentifier; /// A reference to a [`FontTemplate`] with shared ownership and mutability. pub type FontTemplateRef = Arc>; @@ -109,7 +108,7 @@ impl FontTemplateDescriptor { self.stretch.1 >= descriptor_to_match.stretch } - fn override_values_with_css_font_template_descriptors( + pub(crate) fn override_values_with_css_font_template_descriptors( &mut self, css_font_template_descriptors: &CSSFontFaceDescriptors, ) { @@ -137,27 +136,20 @@ impl FontTemplateDescriptor { /// This describes all the information needed to create /// font instance handles. It contains a unique /// FontTemplateData structure that is platform specific. -#[derive(Clone)] +#[derive(Clone, Deserialize, MallocSizeOf, Serialize)] pub struct FontTemplate { pub identifier: FontIdentifier, pub descriptor: FontTemplateDescriptor, - /// The data to use for this [`FontTemplate`]. For web fonts, this is always filled, but - /// for local fonts, this is loaded only lazily in layout. - pub data: Option>>, /// If this font is a web font, this is a reference to the stylesheet that /// created it. This will be used to remove this font from caches, when the /// stylesheet is removed. + /// + /// This is not serialized, as it's only useful in the [`super::FontContext`] + /// that it is created in. + #[serde(skip)] pub stylesheet: Option, } -impl malloc_size_of::MallocSizeOf for FontTemplate { - fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize { - self.identifier.size_of(ops) + - self.descriptor.size_of(ops) + - self.data.as_ref().map_or(0, |data| (*data).size_of(ops)) - } -} - impl Debug for FontTemplate { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { self.identifier.fmt(f) @@ -176,7 +168,6 @@ impl FontTemplate { FontTemplate { identifier: FontIdentifier::Local(identifier), descriptor, - data: None, stylesheet: None, } } @@ -184,22 +175,12 @@ impl FontTemplate { /// Create a new [`FontTemplate`] for a `@font-family` with a `url(...)` `src` font. pub fn new_for_remote_web_font( url: ServoUrl, - data: Arc>, - css_font_template_descriptors: &CSSFontFaceDescriptors, + descriptor: FontTemplateDescriptor, stylesheet: Option, ) -> Result { - let identifier = FontIdentifier::Web(url.clone()); - let Ok(handle) = PlatformFont::new_from_data(identifier, data.clone(), 0, None) else { - return Err("Could not initialize platform font data for: {url:?}"); - }; - - let mut descriptor = handle.descriptor(); - descriptor - .override_values_with_css_font_template_descriptors(css_font_template_descriptors); Ok(FontTemplate { identifier: FontIdentifier::Web(url), descriptor, - data: Some(data), stylesheet, }) } @@ -223,19 +204,9 @@ impl FontTemplate { pub fn identifier(&self) -> &FontIdentifier { &self.identifier } - - /// Returns a reference to the bytes in this font if they are in memory. - /// This function never performs disk I/O. - pub fn data_if_in_memory(&self) -> Option>> { - self.data.clone() - } } pub trait FontTemplateRefMethods { - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - fn data(&self) -> Arc>; /// Get the descriptor. fn descriptor(&self) -> FontTemplateDescriptor; /// Get the [`FontIdentifier`] for this template. @@ -267,24 +238,6 @@ impl FontTemplateRefMethods for FontTemplateRef { self.descriptor().distance_from(descriptor_to_match) } - fn data(&self) -> Arc> { - if let Some(data) = self.borrow().data.clone() { - return data; - } - - let mut template = self.borrow_mut(); - let identifier = template.identifier.clone(); - template - .data - .get_or_insert_with(|| match identifier { - FontIdentifier::Local(local_identifier) => { - Arc::new(local_identifier.read_data_from_file()) - }, - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), - }) - .clone() - } - fn char_in_unicode_range(&self, character: char) -> bool { let character = character as u32; self.borrow() diff --git a/components/fonts/lib.rs b/components/fonts/lib.rs index 305db5e53ef..429672f3616 100644 --- a/components/fonts/lib.rs +++ b/components/fonts/lib.rs @@ -5,7 +5,6 @@ #![deny(unsafe_code)] mod font; -mod font_cache_thread; mod font_context; mod font_store; mod font_template; @@ -13,14 +12,15 @@ mod glyph; #[allow(unsafe_code)] pub mod platform; mod shaper; +mod system_font_service; pub use font::*; -pub use font_cache_thread::*; pub use font_context::*; pub use font_store::*; pub use font_template::*; pub use glyph::*; pub use shaper::*; +pub use system_font_service::*; use unicode_properties::{emoji, EmojiStatus, UnicodeEmoji}; /// Whether or not font fallback selection prefers the emoji or text representation diff --git a/components/fonts/platform/freetype/android/font_list.rs b/components/fonts/platform/freetype/android/font_list.rs index 7f599041ce3..c52b06d8b03 100644 --- a/components/fonts/platform/freetype/android/font_list.rs +++ b/components/fonts/platform/freetype/android/font_list.rs @@ -452,7 +452,7 @@ impl FontList { } } -// Functions used by FontCacheThread +// Functions used by SystemFontSerivce pub fn for_each_available_family(mut callback: F) where F: FnMut(String), diff --git a/components/fonts/platform/freetype/font.rs b/components/fonts/platform/freetype/font.rs index 3a77ab2d8d9..43885556942 100644 --- a/components/fonts/platform/freetype/font.rs +++ b/components/fonts/platform/freetype/font.rs @@ -29,9 +29,9 @@ use crate::font::{ FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, PlatformFontMethods, GPOS, GSUB, KERN, }; -use crate::font_cache_thread::FontIdentifier; use crate::font_template::FontTemplateDescriptor; use crate::glyph::GlyphId; +use crate::system_font_service::FontIdentifier; // This constant is not present in the freetype // bindings due to bindgen not handling the way diff --git a/components/fonts/platform/freetype/ohos/font_list.rs b/components/fonts/platform/freetype/ohos/font_list.rs index bc8303b78aa..82a5740f124 100644 --- a/components/fonts/platform/freetype/ohos/font_list.rs +++ b/components/fonts/platform/freetype/ohos/font_list.rs @@ -453,7 +453,7 @@ impl FontList { } } -// Functions used by FontCacheThread +// Functions used by SystemFontService pub fn for_each_available_family(mut callback: F) where F: FnMut(String), diff --git a/components/fonts/platform/macos/core_text_font_cache.rs b/components/fonts/platform/macos/core_text_font_cache.rs index 6f41a25ce70..0574ff78325 100644 --- a/components/fonts/platform/macos/core_text_font_cache.rs +++ b/components/fonts/platform/macos/core_text_font_cache.rs @@ -16,7 +16,7 @@ use core_text::font::CTFont; use core_text::font_descriptor::kCTFontURLAttribute; use parking_lot::RwLock; -use crate::font_cache_thread::FontIdentifier; +use crate::system_font_service::FontIdentifier; /// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is /// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this diff --git a/components/fonts/font_cache_thread.rs b/components/fonts/system_font_service.rs similarity index 70% rename from components/fonts/font_cache_thread.rs rename to components/fonts/system_font_service.rs index 5d5f3cff4b7..8bf15eaa2eb 100644 --- a/components/fonts/font_cache_thread.rs +++ b/components/fonts/system_font_service.rs @@ -11,9 +11,10 @@ use std::{fmt, thread}; use app_units::Au; use atomic_refcell::AtomicRefCell; -use ipc_channel::ipc::{self, IpcBytesReceiver, IpcBytesSender, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::debug; use malloc_size_of_derive::MallocSizeOf; +use parking_lot::{ReentrantMutex, RwLock}; use serde::{Deserialize, Serialize}; use servo_config::pref; use servo_url::ServoUrl; @@ -29,13 +30,12 @@ use webrender_traits::WebRenderFontApi; use crate::font::FontDescriptor; use crate::font_store::FontStore; -use crate::font_template::{ - FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, -}; +use crate::font_template::{FontTemplate, FontTemplateRef}; use crate::platform::font_list::{ default_system_generic_font_family, for_each_available_family, for_each_variation, LocalFontIdentifier, }; +use crate::FontData; #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] pub enum FontIdentifier { @@ -53,19 +53,18 @@ impl FontIdentifier { } #[derive(Debug, Deserialize, Serialize)] -pub struct SerializedFontTemplate { - identifier: FontIdentifier, - descriptor: FontTemplateDescriptor, - bytes_receiver: ipc_channel::ipc::IpcBytesReceiver, +pub struct FontTemplateRequestResult { + templates: Vec, + template_data: Vec<(FontIdentifier, Arc)>, } -/// Commands that the FontContext sends to the font cache thread. +/// Commands that the `FontContext` sends to the `SystemFontService`. #[derive(Debug, Deserialize, Serialize)] pub enum Command { GetFontTemplates( Option, SingleFontFamily, - IpcSender>, + IpcSender, ), GetFontInstance( FontIdentifier, @@ -73,7 +72,7 @@ pub enum Command { FontInstanceFlags, IpcSender, ), - GetWebFont(IpcBytesReceiver, u32, IpcSender), + GetWebFont(Arc, u32, IpcSender), GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender), Exit(IpcSender<()>), Ping, @@ -90,11 +89,11 @@ struct ResolvedGenericFontFamilies { system_ui: OnceCell, } -/// The font cache thread itself. It maintains a list of reference counted -/// font templates that are currently in use. -struct FontCache { +/// The system font service. There is one of these for every Servo instance. This is a thread, +/// responsible for reading the list of system fonts, handling requests to match against +/// them, and ensuring that only one copy of system font data is loaded at a time. +pub struct SystemFontService { port: IpcReceiver, - font_data: HashMap>>, local_families: FontStore, webrender_api: Box, webrender_fonts: HashMap, @@ -102,68 +101,62 @@ struct FontCache { generic_fonts: ResolvedGenericFontFamilies, } -impl FontCache { +#[derive(Clone, Deserialize, Serialize)] +pub struct SystemFontServiceProxySender(IpcSender); + +impl SystemFontServiceProxySender { + pub fn to_proxy(&self) -> SystemFontServiceProxy { + SystemFontServiceProxy { + sender: ReentrantMutex::new(self.0.clone()), + templates: Default::default(), + data_cache: Default::default(), + } + } +} + +impl SystemFontService { + pub fn spawn(webrender_api: Box) -> SystemFontServiceProxySender { + let (sender, receiver) = ipc::channel().unwrap(); + + thread::Builder::new() + .name("SystemFontService".to_owned()) + .spawn(move || { + #[allow(clippy::default_constructed_unit_structs)] + let mut cache = SystemFontService { + port: receiver, + local_families: Default::default(), + webrender_api, + webrender_fonts: HashMap::new(), + font_instances: HashMap::new(), + generic_fonts: Default::default(), + }; + + cache.refresh_local_families(); + cache.run(); + }) + .expect("Thread spawning failed"); + + SystemFontServiceProxySender(sender) + } + #[tracing::instrument(skip(self), fields(servo_profiling = true))] fn run(&mut self) { loop { let msg = self.port.recv().unwrap(); match msg { - Command::GetFontTemplates(descriptor_to_match, font_family, result) => { - let span = span!( - Level::TRACE, - "Command::GetFontTemplates", - servo_profiling = true - ); + Command::GetFontTemplates(font_descriptor, font_family, result_sender) => { + let span = span!(Level::TRACE, "GetFontTemplates", servo_profiling = true); let _span = span.enter(); - let templates = - self.find_font_templates(descriptor_to_match.as_ref(), &font_family); - debug!("Found templates for descriptor {descriptor_to_match:?}: "); - debug!(" {templates:?}"); - - let (serialized_templates, senders): ( - Vec, - Vec<(FontTemplateRef, IpcBytesSender)>, - ) = templates - .into_iter() - .map(|template| { - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); - ( - SerializedFontTemplate { - identifier: template.identifier().clone(), - descriptor: template.descriptor().clone(), - bytes_receiver, - }, - (template.clone(), bytes_sender), - ) - }) - .unzip(); - - let _ = result.send(serialized_templates); - - // NB: This will load the font into memory if it hasn't been loaded already. - for (font_template, bytes_sender) in senders.iter() { - let identifier = font_template.identifier(); - let data = self - .font_data - .entry(identifier) - .or_insert_with(|| font_template.data()); - let span = span!( - Level::TRACE, - "GetFontTemplates send", - servo_profiling = true - ); - let _span = span.enter(); - let _ = bytes_sender.send(data); - } + let _ = + result_sender.send(self.get_font_templates(font_descriptor, font_family)); }, Command::GetFontInstance(identifier, pt_size, flags, result) => { let _ = result.send(self.get_font_instance(identifier, pt_size, flags)); }, - Command::GetWebFont(bytes_receiver, font_index, result_sender) => { + Command::GetWebFont(data, font_index, result_sender) => { self.webrender_api.forward_add_font_message( - bytes_receiver, + data.as_ipc_shared_memory(), font_index, result_sender, ); @@ -190,6 +183,38 @@ impl FontCache { } } + fn get_font_templates( + &mut self, + font_descriptor: Option, + font_family: SingleFontFamily, + ) -> FontTemplateRequestResult { + let templates = self.find_font_templates(font_descriptor.as_ref(), &font_family); + let templates: Vec<_> = templates + .into_iter() + .map(|template| template.borrow().clone()) + .collect(); + + // The `FontData` for all templates is also sent along with the `FontTemplate`s. This is to ensure that + // the data is not read from disk in each content process. The data is loaded once here in the system + // font service and each process gets another handle to the `IpcSharedMemory` view of that data. + let template_data = templates + .iter() + .map(|template| { + let identifier = template.identifier.clone(); + let data = self + .local_families + .get_or_initialize_font_data(&identifier) + .clone(); + (identifier, data) + }) + .collect(); + + FontTemplateRequestResult { + templates, + template_data, + } + } + fn refresh_local_families(&mut self) { self.local_families.clear(); for_each_available_family(|family_name| { @@ -232,11 +257,7 @@ impl FontCache { ) -> FontInstanceKey { let webrender_font_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; - let font_data = self - .font_data - .get(&identifier) - .expect("Got unexpected FontIdentifier") - .clone(); + let font_data = self.local_families.get_or_initialize_font_data(&identifier); let font_key = *webrender_fonts .entry(identifier.clone()) @@ -252,7 +273,7 @@ impl FontCache { .add_system_font(local_font_identifier.native_font_handle()); } - webrender_font_api.add_font(font_data, identifier.index()) + webrender_font_api.add_font(font_data.as_ipc_shared_memory(), identifier.index()) }); *self @@ -304,7 +325,8 @@ impl FontCache { } } -pub trait FontSource: Clone { +/// A trait for accessing the [`SystemFontServiceProxy`] necessary for unit testing. +pub trait SystemFontServiceProxyTrait: Send + Sync { fn find_matching_font_templates( &self, descriptor_to_match: Option<&FontDescriptor>, @@ -316,20 +338,29 @@ pub trait FontSource: Clone { size: Au, flags: FontInstanceFlags, ) -> FontInstanceKey; - fn get_web_font(&self, data: Arc>, index: u32) -> FontKey; + fn get_web_font(&self, data: Arc, index: u32) -> FontKey; fn get_web_font_instance( &self, font_key: FontKey, size: f32, flags: FontInstanceFlags, ) -> FontInstanceKey; + fn get_font_data(&self, identifier: &FontIdentifier) -> Option>; } -/// The public interface to the font cache thread, used by per-thread `FontContext` instances (via -/// the `FontSource` trait), and also by layout. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct FontCacheThread { - chan: IpcSender, +#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] +struct FontTemplateCacheKey { + font_descriptor: Option, + family_descriptor: SingleFontFamily, +} + +/// The public interface to the [`SystemFontService`], used by per-Document `FontContext` +/// instances (via [`SystemFontServiceProxyTrait`]). +#[derive(Debug)] +pub struct SystemFontServiceProxy { + sender: ReentrantMutex>, + templates: RwLock>>, + data_cache: RwLock>>, } /// A version of `FontStyle` from Stylo that is serializable. Normally this is not @@ -417,44 +448,24 @@ impl From<&FontFaceRuleData> for CSSFontFaceDescriptors { } } -impl FontCacheThread { - pub fn new(webrender_api: Box) -> FontCacheThread { - let (chan, port) = ipc::channel().unwrap(); - - thread::Builder::new() - .name("FontCache".to_owned()) - .spawn(move || { - #[allow(clippy::default_constructed_unit_structs)] - let mut cache = FontCache { - port, - font_data: HashMap::new(), - local_families: Default::default(), - webrender_api, - webrender_fonts: HashMap::new(), - font_instances: HashMap::new(), - generic_fonts: Default::default(), - }; - - cache.refresh_local_families(); - cache.run(); - }) - .expect("Thread spawning failed"); - - FontCacheThread { chan } - } - +impl SystemFontServiceProxy { pub fn exit(&self) { let (response_chan, response_port) = ipc::channel().unwrap(); - self.chan + self.sender + .lock() .send(Command::Exit(response_chan)) - .expect("Couldn't send FontCacheThread exit message"); + .expect("Couldn't send SystemFontService exit message"); response_port .recv() - .expect("Couldn't receive FontCacheThread reply"); + .expect("Couldn't receive SystemFontService reply"); + } + + pub fn to_sender(&self) -> SystemFontServiceProxySender { + SystemFontServiceProxySender(self.sender.lock().clone()) } } -impl FontSource for FontCacheThread { +impl SystemFontServiceProxyTrait for SystemFontServiceProxy { fn get_system_font_instance( &self, identifier: FontIdentifier, @@ -462,23 +473,24 @@ impl FontSource for FontCacheThread { flags: FontInstanceFlags, ) -> FontInstanceKey { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); - self.chan + self.sender + .lock() .send(Command::GetFontInstance( identifier, size, flags, response_chan, )) - .expect("failed to send message to font cache thread"); + .expect("failed to send message to system font service"); let instance_key = response_port.recv(); if instance_key.is_err() { - let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); + let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err(); assert!( font_thread_has_closed, "Failed to receive a response from live font cache" ); - panic!("Font cache thread has already exited."); + panic!("SystemFontService has already exited."); } instance_key.unwrap() } @@ -486,51 +498,59 @@ impl FontSource for FontCacheThread { fn find_matching_font_templates( &self, descriptor_to_match: Option<&FontDescriptor>, - font_family: &SingleFontFamily, + family_descriptor: &SingleFontFamily, ) -> Vec { + let cache_key = FontTemplateCacheKey { + font_descriptor: descriptor_to_match.cloned(), + family_descriptor: family_descriptor.clone(), + }; + if let Some(templates) = self.templates.read().get(&cache_key).cloned() { + return templates; + } + + debug!( + "SystemFontServiceProxy: cache miss for template_descriptor={:?} family_descriptor={:?}", + descriptor_to_match, family_descriptor + ); + let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); - self.chan + self.sender + .lock() .send(Command::GetFontTemplates( descriptor_to_match.cloned(), - font_family.clone(), + family_descriptor.clone(), response_chan, )) - .expect("failed to send message to font cache thread"); + .expect("failed to send message to system font service"); let reply = response_port.recv(); - if reply.is_err() { - let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); + let Ok(reply) = reply else { + let font_thread_has_closed = self.sender.lock().send(Command::Ping).is_err(); assert!( font_thread_has_closed, "Failed to receive a response from live font cache" ); - panic!("Font cache thread has already exited."); - } + panic!("SystemFontService has already exited."); + }; - reply - .unwrap() + let templates: Vec<_> = reply + .templates .into_iter() - .map(|serialized_font_template| { - let font_data = serialized_font_template.bytes_receiver.recv().ok(); - Arc::new(AtomicRefCell::new(FontTemplate { - identifier: serialized_font_template.identifier, - descriptor: serialized_font_template.descriptor.clone(), - data: font_data.map(Arc::new), - stylesheet: None, - })) - }) - .collect() + .map(AtomicRefCell::new) + .map(Arc::new) + .collect(); + self.data_cache.write().extend(reply.template_data); + + templates } - fn get_web_font(&self, data: Arc>, index: u32) -> FontKey { + fn get_web_font(&self, data: Arc, index: u32) -> FontKey { let (result_sender, result_receiver) = ipc::channel().expect("failed to create IPC channel"); - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); let _ = self - .chan - .send(Command::GetWebFont(bytes_receiver, index, result_sender)); - let _ = bytes_sender.send(&data); + .sender + .lock() + .send(Command::GetWebFont(data, index, result_sender)); result_receiver.recv().unwrap() } @@ -542,7 +562,7 @@ impl FontSource for FontCacheThread { ) -> FontInstanceKey { let (result_sender, result_receiver) = ipc::channel().expect("failed to create IPC channel"); - let _ = self.chan.send(Command::GetWebFontInstance( + let _ = self.sender.lock().send(Command::GetWebFontInstance( font_key, font_size, font_flags, @@ -550,6 +570,10 @@ impl FontSource for FontCacheThread { )); result_receiver.recv().unwrap() } + + fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { + self.data_cache.read().get(identifier).cloned() + } } #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs index 91eddaeccc3..470155a2871 100644 --- a/components/fonts/tests/font_context.rs +++ b/components/fonts/tests/font_context.rs @@ -2,22 +2,24 @@ * 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::{Cell, RefCell}; use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; -use std::rc::Rc; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Arc; use app_units::Au; +use fonts::platform::font::PlatformFont; use fonts::{ - fallback_font_families, CSSFontFaceDescriptors, FallbackFontSelectionOptions, FontContext, - FontDescriptor, FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontSource, - FontTemplate, FontTemplateRef, FontTemplates, + fallback_font_families, FallbackFontSelectionOptions, FontContext, FontData, FontDescriptor, + FontFamilyDescriptor, FontIdentifier, FontSearchScope, FontTemplate, FontTemplateRef, + FontTemplates, PlatformFontMethods, SystemFontServiceProxyTrait, }; use ipc_channel::ipc; use net_traits::ResourceThreads; -use servo_arc::Arc; +use parking_lot::Mutex; +use servo_arc::Arc as ServoArc; use servo_atoms::Atom; use servo_url::ServoUrl; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; @@ -31,35 +33,40 @@ use style::values::generics::font::LineHeight; use style::ArcSlice; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, IdNamespace}; -#[derive(Clone)] struct MockFontCacheThread { - families: RefCell>, - find_font_count: Rc>, + families: Mutex>, + data: Mutex>>, + find_font_count: AtomicI32, } impl MockFontCacheThread { - fn new() -> MockFontCacheThread { + fn new() -> Self { + let proxy = Self { + families: Default::default(), + data: Default::default(), + find_font_count: AtomicI32::new(0), + }; + let mut csstest_ascii = FontTemplates::default(); - Self::add_face(&mut csstest_ascii, "csstest-ascii"); + proxy.add_face(&mut csstest_ascii, "csstest-ascii"); let mut csstest_basic = FontTemplates::default(); - Self::add_face(&mut csstest_basic, "csstest-basic-regular"); + proxy.add_face(&mut csstest_basic, "csstest-basic-regular"); let mut fallback = FontTemplates::default(); - Self::add_face(&mut fallback, "csstest-basic-regular"); + proxy.add_face(&mut fallback, "csstest-basic-regular"); - let mut families = HashMap::new(); - families.insert("CSSTest ASCII".to_owned(), csstest_ascii); - families.insert("CSSTest Basic".to_owned(), csstest_basic); - families.insert( - fallback_font_families(FallbackFontSelectionOptions::default())[0].to_owned(), - fallback, - ); - - MockFontCacheThread { - families: RefCell::new(families), - find_font_count: Rc::new(Cell::new(0)), + { + let mut families = proxy.families.lock(); + families.insert("CSSTest ASCII".to_owned(), csstest_ascii); + families.insert("CSSTest Basic".to_owned(), csstest_basic); + families.insert( + fallback_font_families(FallbackFontSelectionOptions::default())[0].to_owned(), + fallback, + ); } + + proxy } fn identifier_for_font_name(name: &str) -> FontIdentifier { @@ -74,39 +81,44 @@ impl MockFontCacheThread { ServoUrl::from_file_path(path).unwrap() } - fn add_face(family: &mut FontTemplates, name: &str) { + fn add_face(&self, family: &mut FontTemplates, name: &str) { let mut path: PathBuf = [env!("CARGO_MANIFEST_DIR"), "tests", "support", "CSSTest"] .iter() .collect(); path.push(format!("{}.ttf", name)); let file = File::open(path).unwrap(); - let data: Vec = file.bytes().map(|b| b.unwrap()).collect(); - family.add_template( - FontTemplate::new_for_remote_web_font( - Self::url_for_font_name(name), - std::sync::Arc::new(data), - &CSSFontFaceDescriptors::new(name), - None, - ) - .unwrap(), - ); + let data = Arc::new(FontData::from_bytes( + file.bytes().map(|b| b.unwrap()).collect(), + )); + + let url = Self::url_for_font_name(name); + let identifier = FontIdentifier::Web(url.clone()); + let handle = + PlatformFont::new_from_data(identifier.clone(), data.as_arc().clone(), 0, None) + .expect("Could not load test font"); + let template = + FontTemplate::new_for_remote_web_font(url, handle.descriptor(), None).unwrap(); + family.add_template(template); + + self.data.lock().insert(identifier, data); } } -impl FontSource for MockFontCacheThread { +impl SystemFontServiceProxyTrait for MockFontCacheThread { fn find_matching_font_templates( &self, descriptor_to_match: Option<&FontDescriptor>, font_family: &SingleFontFamily, ) -> Vec { - self.find_font_count.set(self.find_font_count.get() + 1); + self.find_font_count.fetch_add(1, Ordering::Relaxed); + let SingleFontFamily::FamilyName(family_name) = font_family else { return Vec::new(); }; self.families - .borrow_mut() + .lock() .get_mut(&*family_name.name) .map(|family| family.find_for_descriptor(descriptor_to_match)) .unwrap_or_default() @@ -114,25 +126,29 @@ impl FontSource for MockFontCacheThread { fn get_system_font_instance( &self, - _: FontIdentifier, - _: Au, - _: FontInstanceFlags, + _font_identifier: FontIdentifier, + _size: Au, + _flags: FontInstanceFlags, ) -> FontInstanceKey { FontInstanceKey(IdNamespace(0), 0) } - fn get_web_font(&self, _: std::sync::Arc>, _: u32) -> webrender_api::FontKey { + fn get_web_font(&self, _data: Arc, _index: u32) -> FontKey { FontKey(IdNamespace(0), 0) } fn get_web_font_instance( &self, - _: webrender_api::FontKey, - _: f32, - _: FontInstanceFlags, + _font_key: FontKey, + _size: f32, + _flags: FontInstanceFlags, ) -> FontInstanceKey { FontInstanceKey(IdNamespace(0), 0) } + + fn get_font_data(&self, identifier: &FontIdentifier) -> Option> { + self.data.lock().get(identifier).cloned() + } } fn style() -> FontStyleStruct { @@ -177,7 +193,7 @@ fn mock_resource_threads() -> ResourceThreads { #[test] fn test_font_group_is_cached_by_style() { - let source = MockFontCacheThread::new(); + let source = Arc::new(MockFontCacheThread::new()); let context = FontContext::new(source, mock_resource_threads()); let style1 = style(); @@ -187,16 +203,16 @@ fn test_font_group_is_cached_by_style() { assert!( std::ptr::eq( - &*context.font_group(Arc::new(style1.clone())).read(), - &*context.font_group(Arc::new(style1.clone())).read() + &*context.font_group(ServoArc::new(style1.clone())).read(), + &*context.font_group(ServoArc::new(style1.clone())).read() ), "the same font group should be returned for two styles with the same hash" ); assert!( !std::ptr::eq( - &*context.font_group(Arc::new(style1.clone())).read(), - &*context.font_group(Arc::new(style2.clone())).read() + &*context.font_group(ServoArc::new(style1.clone())).read(), + &*context.font_group(ServoArc::new(style2.clone())).read() ), "different font groups should be returned for two styles with different hashes" ) @@ -204,14 +220,13 @@ fn test_font_group_is_cached_by_style() { #[test] fn test_font_group_find_by_codepoint() { - let source = MockFontCacheThread::new(); - let count = source.find_font_count.clone(); - let mut context = FontContext::new(source, mock_resource_threads()); + let source = Arc::new(MockFontCacheThread::new()); + let mut context = FontContext::new(source.clone(), mock_resource_threads()); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII", "CSSTest Basic"])); - let group = context.font_group(Arc::new(style)); + let group = context.font_group(ServoArc::new(style)); let font = group .write() @@ -222,7 +237,7 @@ fn test_font_group_find_by_codepoint() { MockFontCacheThread::identifier_for_font_name("csstest-ascii") ); assert_eq!( - count.get(), + source.find_font_count.fetch_add(0, Ordering::Relaxed), 1, "only the first font in the list should have been loaded" ); @@ -236,7 +251,7 @@ fn test_font_group_find_by_codepoint() { MockFontCacheThread::identifier_for_font_name("csstest-ascii") ); assert_eq!( - count.get(), + source.find_font_count.fetch_add(0, Ordering::Relaxed), 1, "we shouldn't load the same font a second time" ); @@ -249,18 +264,22 @@ fn test_font_group_find_by_codepoint() { font.identifier(), MockFontCacheThread::identifier_for_font_name("csstest-basic-regular") ); - assert_eq!(count.get(), 2, "both fonts should now have been loaded"); + assert_eq!( + source.find_font_count.fetch_add(0, Ordering::Relaxed), + 2, + "both fonts should now have been loaded" + ); } #[test] fn test_font_fallback() { - let source = MockFontCacheThread::new(); + let source = Arc::new(MockFontCacheThread::new()); let mut context = FontContext::new(source, mock_resource_threads()); let mut style = style(); style.set_font_family(font_family(vec!["CSSTest ASCII"])); - let group = context.font_group(Arc::new(style)); + let group = context.font_group(ServoArc::new(style)); let font = group .write() @@ -285,9 +304,8 @@ fn test_font_fallback() { #[test] fn test_font_template_is_cached() { - let source = MockFontCacheThread::new(); - let count = source.find_font_count.clone(); - let context = FontContext::new(source, mock_resource_threads()); + let source = Arc::new(MockFontCacheThread::new()); + let context = FontContext::new(source.clone(), mock_resource_threads()); let mut font_descriptor = FontDescriptor { weight: FontWeight::normal(), @@ -320,7 +338,7 @@ fn test_font_template_is_cached() { ); assert_eq!( - count.get(), + source.find_font_count.fetch_add(0, Ordering::Relaxed), 1, "we should only have fetched the template data from the cache thread once" ); diff --git a/components/layout/context.rs b/components/layout/context.rs index 022d5c8d5b8..7cb4934d5a1 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -11,7 +11,7 @@ use std::thread; use base::id::PipelineId; use fnv::FnvHasher; -use fonts::{FontCacheThread, FontContext}; +use fonts::{FontContext, SystemFontServiceProxy}; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; @@ -24,7 +24,7 @@ use style::context::{RegisteredSpeculativePainter, SharedStyleContext}; use crate::display_list::items::{OpaqueNode, WebRenderImageInfo}; -pub type LayoutFontContext = FontContext; +pub type LayoutFontContext = FontContext; type WebrenderImageCache = HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault>; @@ -44,7 +44,7 @@ pub struct LayoutContext<'a> { pub image_cache: Arc, /// A FontContext to be used during layout. - pub font_context: Arc>, + pub font_context: Arc>, /// A cache of WebRender image info. pub webrender_image_cache: Arc>, diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs index 82b953f18aa..352b31efd31 100644 --- a/components/layout_2020/context.rs +++ b/components/layout_2020/context.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use base::id::PipelineId; use fnv::FnvHashMap; -use fonts::{FontCacheThread, FontContext}; +use fonts::{FontContext, SystemFontServiceProxy}; use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; @@ -27,7 +27,7 @@ pub struct LayoutContext<'a> { pub style_context: SharedStyleContext<'a>, /// A FontContext to be used during layout. - pub font_context: Arc>, + pub font_context: Arc>, /// Reference to the script thread image cache. pub image_cache: Arc, diff --git a/components/layout_2020/flow/inline/text_run.rs b/components/layout_2020/flow/inline/text_run.rs index dc5f457b33e..0b647d35097 100644 --- a/components/layout_2020/flow/inline/text_run.rs +++ b/components/layout_2020/flow/inline/text_run.rs @@ -8,7 +8,7 @@ use std::ops::Range; use app_units::Au; use base::text::is_bidi_control; use fonts::{ - FontCacheThread, FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, + FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions, SystemFontServiceProxy, LAST_RESORT_GLYPH_ADVANCE, }; use fonts_traits::ByteIndex; @@ -342,7 +342,7 @@ impl TextRun { pub(super) fn segment_and_shape( &mut self, formatting_context_text: &str, - font_context: &FontContext, + font_context: &FontContext, linebreaker: &mut LineBreaker, font_cache: &mut Vec, bidi_info: &BidiInfo, @@ -410,7 +410,7 @@ impl TextRun { fn segment_text_by_font( &mut self, formatting_context_text: &str, - font_context: &FontContext, + font_context: &FontContext, font_cache: &mut Vec, bidi_info: &BidiInfo, ) -> Vec<(TextRunSegment, FontRef)> { @@ -556,7 +556,7 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec, + font_context: &FontContext, ) -> Option { let font = font_context .font_group(style.clone_font()) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 765b5c3cd28..6100463eed9 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -23,8 +23,8 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as use euclid::{Point2D, Rect, Scale, Size2D}; use fnv::FnvHashMap; use fonts::{ - get_and_reset_text_shaping_performance_counter, FontCacheThread, FontContext, - FontContextWebFontMethods, + get_and_reset_text_shaping_performance_counter, FontContext, FontContextWebFontMethods, + SystemFontServiceProxy, }; use fonts_traits::WebFontLoadFinishedCallback; use fxhash::{FxHashMap, FxHashSet}; @@ -133,10 +133,10 @@ pub struct LayoutThread { /// Reference to the script thread image cache. image_cache: Arc, - /// A FontContext tFontCacheThreadImplg layout. - font_context: Arc>, + /// A per-layout FontContext managing font access. + font_context: Arc>, - /// Is this the first reflow iFontCacheThreadImplread? + /// Is this the first reflow in this layout? first_reflow: Cell, /// Flag to indicate whether to use parallel operations @@ -199,7 +199,7 @@ impl LayoutFactory for LayoutFactoryImpl { config.script_chan, config.image_cache, config.resource_threads, - config.font_cache_thread, + config.system_font_service, config.time_profiler_chan, config.webrender_api_sender, config.paint_time_metrics, @@ -560,7 +560,7 @@ impl LayoutThread { script_chan: IpcSender, image_cache: Arc, resource_threads: ResourceThreads, - font_cache_thread: FontCacheThread, + system_font_service: Arc, time_profiler_chan: profile_time::ProfilerChan, webrender_api: WebRenderScriptApi, paint_time_metrics: PaintTimeMetrics, @@ -577,7 +577,7 @@ impl LayoutThread { keyword_info: KeywordInfo::medium(), }; - let font_context = Arc::new(FontContext::new(font_cache_thread, resource_threads)); + let font_context = Arc::new(FontContext::new(system_font_service, resource_threads)); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index c060f560ea7..de8f48886e0 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -23,7 +23,7 @@ use embedder_traits::resources::{self, Resource}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; use euclid::{Point2D, Scale, Size2D, Vector2D}; use fnv::FnvHashMap; -use fonts::{FontCacheThread, FontContext, FontContextWebFontMethods}; +use fonts::{FontContext, FontContextWebFontMethods, SystemFontServiceProxy}; use fonts_traits::WebFontLoadFinishedCallback; use fxhash::FxHashMap; use ipc_channel::ipc::IpcSender; @@ -121,7 +121,7 @@ pub struct LayoutThread { image_cache: Arc, /// A FontContext to be used during layout. - font_context: Arc>, + font_context: Arc>, /// Is this the first reflow in this LayoutThread? first_reflow: Cell, @@ -177,7 +177,7 @@ impl LayoutFactory for LayoutFactoryImpl { config.script_chan, config.image_cache, config.resource_threads, - config.font_cache_thread, + config.system_font_service, config.time_profiler_chan, config.webrender_api_sender, config.paint_time_metrics, @@ -502,7 +502,7 @@ impl LayoutThread { script_chan: IpcSender, image_cache: Arc, resource_threads: ResourceThreads, - font_cache_thread: FontCacheThread, + system_font_service: Arc, time_profiler_chan: profile_time::ProfilerChan, webrender_api_sender: WebRenderScriptApi, paint_time_metrics: PaintTimeMetrics, @@ -521,7 +521,7 @@ 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_context = Arc::new(FontContext::new(font_cache_thread, resource_threads)); + let font_context = Arc::new(FontContext::new(system_font_service, resource_threads)); let device = Device::new( MediaType::screen(), QuirksMode::NoQuirks, @@ -1227,7 +1227,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl { } } -struct LayoutFontMetricsProvider(Arc>); +struct LayoutFontMetricsProvider(Arc>); impl FontMetricsProvider for LayoutFontMetricsProvider { fn query_font_metrics( diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 6ed36d6b8b5..21145934c71 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -48,7 +48,7 @@ use devtools_traits::{ }; use embedder_traits::EmbedderMsg; use euclid::default::{Point2D, Rect}; -use fonts::FontCacheThread; +use fonts::SystemFontServiceProxy; use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader}; use html5ever::{local_name, namespace_url, ns}; use hyper_serde::Serde; @@ -593,9 +593,9 @@ pub struct ScriptThread { #[no_trace] layout_to_constellation_chan: IpcSender, - /// The font cache thread to use for layout that happens in this [`ScriptThread`]. + /// A proxy to the `SystemFontService` to use for accessing system font lists. #[no_trace] - font_cache_thread: FontCacheThread, + system_font_service: Arc, /// The port on which we receive messages from the image cache #[no_trace] @@ -786,7 +786,7 @@ impl ScriptThreadFactory for ScriptThread { fn create( state: InitialScriptState, layout_factory: Arc, - font_cache_thread: FontCacheThread, + system_font_service: Arc, load_data: LoadData, user_agent: Cow<'static, str>, ) { @@ -813,7 +813,7 @@ impl ScriptThreadFactory for ScriptThread { script_port, script_chan.clone(), layout_factory, - font_cache_thread, + system_font_service, user_agent, ); @@ -1282,7 +1282,7 @@ impl ScriptThread { port: Receiver, chan: Sender, layout_factory: Arc, - font_cache_thread: FontCacheThread, + system_font_service: Arc, user_agent: Cow<'static, str>, ) -> ScriptThread { let opts = opts::get(); @@ -1387,7 +1387,7 @@ impl ScriptThread { mutation_observers: Default::default(), layout_to_constellation_chan: state.layout_to_constellation_chan, - font_cache_thread, + system_font_service, webgl_chan: state.webgl_chan, webxr_registry: state.webxr_registry, @@ -3657,7 +3657,7 @@ impl ScriptThread { constellation_chan: self.layout_to_constellation_chan.clone(), script_chan: self.control_chan.clone(), image_cache: self.image_cache.clone(), - font_cache_thread: self.font_cache_thread.clone(), + system_font_service: self.system_font_service.clone(), resource_threads: self.resource_threads.clone(), time_profiler_chan: self.time_profiler_chan.clone(), webrender_api_sender: self.webrender_api_sender.clone(), diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 38a47f89148..15438de42fe 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -54,7 +54,7 @@ use crossbeam_channel::{unbounded, Sender}; use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker}; use env_logger::Builder as EnvLoggerBuilder; use euclid::Scale; -use fonts::FontCacheThread; +use fonts::SystemFontService; #[cfg(all( not(target_os = "windows"), not(target_os = "ios"), @@ -66,7 +66,7 @@ use fonts::FontCacheThread; use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; pub use gleam::gl; use gleam::gl::RENDERER; -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; #[cfg(feature = "layout_2013")] pub use layout_thread_2013; use log::{error, trace, warn, Log, Metadata, Record}; @@ -89,7 +89,6 @@ use surfman::platform::generic::multi::context::NativeContext as LinuxNativeCont use surfman::{GLApi, GLVersion}; #[cfg(all(target_os = "linux", not(target_env = "ohos")))] use surfman::{NativeConnection, NativeContext}; -use tracing::{span, Level}; use webgpu::swapchain::WGPUImageMap; use webrender::{RenderApiSender, ShaderPrecacheFlags, UploadMethod, ONE_TIME_USAGE_HINT}; use webrender_api::{ @@ -1040,13 +1039,16 @@ fn create_constellation( Arc::new(protocols), ); - let font_cache_thread = FontCacheThread::new(Box::new(WebRenderFontApiCompositorProxy( - compositor_proxy.clone(), - ))); + let system_font_service = Arc::new( + SystemFontService::spawn(Box::new(WebRenderFontApiCompositorProxy( + compositor_proxy.clone(), + ))) + .to_proxy(), + ); let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start( Box::new(CanvasWebrenderApi(compositor_proxy.clone())), - font_cache_thread.clone(), + system_font_service.clone(), public_resource_threads.clone(), ); @@ -1055,7 +1057,7 @@ fn create_constellation( embedder_proxy, devtools_sender, bluetooth_thread, - font_cache_thread, + system_font_service, public_resource_threads, private_resource_threads, time_profiler_chan, @@ -1106,20 +1108,12 @@ impl WebRenderFontApi for WebRenderFontApiCompositorProxy { receiver.recv().unwrap() } - #[tracing::instrument(skip(self), fields(servo_profiling = true))] - fn add_font(&self, data: Arc>, index: u32) -> FontKey { + fn add_font(&self, data: Arc, index: u32) -> FontKey { let (sender, receiver) = unbounded(); - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); self.0 .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddFont(sender, index, bytes_receiver), + FontToCompositorMsg::AddFont(sender, index, data), ))); - { - let span = span!(Level::TRACE, "add_font send", servo_profiling = true); - let _span = span.enter(); - let _ = bytes_sender.send(&data); - } receiver.recv().unwrap() } @@ -1134,14 +1128,14 @@ impl WebRenderFontApi for WebRenderFontApiCompositorProxy { fn forward_add_font_message( &self, - bytes_receiver: ipc::IpcBytesReceiver, + data: Arc, font_index: u32, result_sender: IpcSender, ) { let (sender, receiver) = unbounded(); self.0 .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Font( - FontToCompositorMsg::AddFont(sender, font_index, bytes_receiver), + FontToCompositorMsg::AddFont(sender, font_index, data), ))); let _ = result_sender.send(receiver.recv().unwrap()); } diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 1faff34b767..1ad180411d3 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -24,7 +24,7 @@ use canvas_traits::canvas::{CanvasId, CanvasMsg}; use crossbeam_channel::Sender; use euclid::default::{Point2D, Rect}; use euclid::Size2D; -use fonts::FontCacheThread; +use fonts::SystemFontServiceProxy; use ipc_channel::ipc::IpcSender; use libc::c_void; use malloc_size_of_derive::MallocSizeOf; @@ -175,7 +175,7 @@ pub struct LayoutConfig { pub script_chan: IpcSender, pub image_cache: Arc, pub resource_threads: ResourceThreads, - pub font_cache_thread: FontCacheThread, + pub system_font_service: Arc, pub time_profiler_chan: time::ProfilerChan, pub webrender_api_sender: WebRenderScriptApi, pub paint_time_metrics: PaintTimeMetrics, @@ -281,7 +281,7 @@ pub trait ScriptThreadFactory { fn create( state: InitialScriptState, layout_factory: Arc, - font_cache_thread: FontCacheThread, + system_font_service: Arc, load_data: LoadData, user_agent: Cow<'static, str>, ); diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index 718d53e44a1..14f3d3d338a 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -15,7 +15,7 @@ use crossbeam_channel::Sender; use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId}; use embedder_traits::Cursor; use euclid::default::Size2D; -use ipc_channel::ipc::{self, IpcBytesReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; use libc::c_void; use log::warn; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -190,14 +190,14 @@ pub trait WebRenderFontApi { size: f32, flags: FontInstanceFlags, ) -> FontInstanceKey; - fn add_font(&self, data: Arc>, index: u32) -> FontKey; + fn add_font(&self, data: Arc, index: u32) -> FontKey; fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; /// Forward a `AddFont` message, sending it on to the compositor. This is used to get WebRender /// [`FontKey`]s for web fonts in the per-layout `FontContext`. fn forward_add_font_message( &self, - bytes_receiver: IpcBytesReceiver, + data: Arc, font_index: u32, result_sender: IpcSender, ); @@ -219,7 +219,7 @@ pub enum CanvasToCompositorMsg { pub enum FontToCompositorMsg { AddFontInstance(FontKey, f32, FontInstanceFlags, Sender), - AddFont(Sender, u32, IpcBytesReceiver), + AddFont(Sender, u32, Arc), AddSystemFont(Sender, NativeFontHandle), }