diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-09-25 09:31:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-25 07:31:55 +0000 |
commit | ade902207fc1f941fc77fa47bff1db0375ed7220 (patch) | |
tree | d94e171f40e2c6ad56b7e1a07cb1a78173509644 /components | |
parent | 2c6d9a190f947ca6fe58a06d2549c4924e678d3a (diff) | |
download | servo-ade902207fc1f941fc77fa47bff1db0375ed7220.tar.gz servo-ade902207fc1f941fc77fa47bff1db0375ed7220.zip |
fonts: Use `IpcSharedMemory` to send font data (#33530)
This changes modifes the way that font data is sent over IPC channels.
Instead of serializing the data or sending it via IPC byte senders, font
data is copied into shared memory and a copy of the handle is sent over
the channel.
There is also the idea of sending the file handle of the on disk data of
system fonts. This could be implemented as a further followup once there
is an abstraction in `ipc-channel` over file handles.
To accomplish this, a `FontData` abstraction is added, which also allows
caching an in-memory shared `Arc<Vec<u8>>` version of the data (neeeded
by some APIs). This could also be a place for caching font tables in the
future.
Finally, the `FontCacheThread` is renamed to the `SystemFontService`
while the proxy for this is now named `SystemFontServiceProxy`.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Diffstat (limited to 'components')
26 files changed, 600 insertions, 543 deletions
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<ImageKey>, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option<ImageKey>, - font_context: Arc<FontContext<FontCacheThread>>, + font_context: Arc<FontContext<SystemFontServiceProxy>>, } fn create_backend() -> Box<dyn Backend> { @@ -446,7 +446,7 @@ impl<'a> CanvasData<'a> { size: Size2D<u64>, webrender_api: Box<dyn WebrenderApi>, antialias: AntialiasMode, - font_context: Arc<FontContext<FontCacheThread>>, + font_context: Arc<FontContext<SystemFontServiceProxy>>, ) -> 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<CanvasId, CanvasData<'a>>, next_canvas_id: CanvasId, webrender_api: Box<dyn WebrenderApi>, - font_context: Arc<FontContext<FontCacheThread>>, + font_context: Arc<FontContext<SystemFontServiceProxy>>, } impl<'a> CanvasPaintThread<'a> { fn new( webrender_api: Box<dyn WebrenderApi>, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<dyn WebrenderApi + Send>, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, resource_threads: ResourceThreads, ) -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) { let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().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<Window: WindowMethods + ?Sized> IOCompositor<Window> { 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<STF, SWF> { /// A channel for the constellation to send messages to the font /// cache thread. - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, /// 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<BluetoothRequest>, - /// 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<SystemFontServiceProxy>, /// 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<SWManagerMsg>, - /// 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<SystemFontServiceProxy>, /// 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<IpcSender<ScriptToDevtoolsControlMsg>>, bluetooth_thread: IpcSender<BluetoothRequest>, swmanager_thread: IpcSender<SWManagerMsg>, - 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<Au>, + data: &Arc<Vec<u8>>, ) -> Result<PlatformFont, &'static str> { - 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<FontData>, pub template: FontTemplateRef, pub metrics: FontMetrics, pub descriptor: FontDescriptor, @@ -251,13 +254,19 @@ impl Font { pub fn new( template: FontTemplateRef, descriptor: FontDescriptor, + data: Arc<FontData>, synthesized_small_caps: Option<FontRef>, ) -> Result<Font, &'static str> { - 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<S: FontSource>( + pub fn find_by_codepoint<S: SystemFontServiceProxyTrait>( &mut self, font_context: &FontContext<S>, 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<S: FontSource>(&mut self, font_context: &FontContext<S>) -> Option<FontRef> { + pub fn first<S: SystemFontServiceProxyTrait>( + &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 @@ -629,7 +641,7 @@ impl FontGroup { font_predicate: FontPredicate, ) -> Option<FontRef> where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -659,7 +671,7 @@ impl FontGroup { font_predicate: FontPredicate, ) -> Option<FontRef> where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -731,7 +743,7 @@ impl FontGroupFamily { font_predicate: &FontPredicate, ) -> Option<FontRef> where - S: FontSource, + S: SystemFontServiceProxyTrait, TemplatePredicate: Fn(FontTemplateRef) -> bool, FontPredicate: Fn(&FontRef) -> bool, { @@ -754,7 +766,7 @@ impl FontGroupFamily { .next() } - fn members<S: FontSource>( + fn members<S: SystemFontServiceProxyTrait>( &mut self, font_descriptor: &FontDescriptor, font_context: &FontContext<S>, @@ -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<Vec<u8>> = 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<S: FontSource> { - font_source: ReentrantMutex<S>, +pub struct FontContext<Proxy: SystemFontServiceProxyTrait> { + pub(crate) system_font_service_proxy: Arc<Proxy>, resource_threads: ReentrantMutex<CoreResourceThread>, - cache: CachingFontSource<S>, + + /// 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<HashMap<FontCacheKey, Option<FontRef>>>, + + /// 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<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, + web_fonts: CrossThreadFontStore, webrender_font_store: CrossThreadWebRenderFontStore, have_removed_web_fonts: AtomicBool, } -impl<S: FontSource> MallocSizeOf for FontContext<S> { +impl<S: SystemFontServiceProxyTrait> MallocSizeOf for FontContext<S> { 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::<usize>(); + 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::<usize>(); + font_cache_size + font_group_cache_size } } -impl<S: FontSource> FontContext<S> { - pub fn new(font_source: S, resource_threads: ResourceThreads) -> FontContext<S> { +impl<Proxy: SystemFontServiceProxyTrait> FontContext<Proxy> { + pub fn new(system_font_service_proxy: Arc<Proxy>, 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<S: FontSource> FontContext<S> { self.web_fonts.read().number_of_fonts_still_loading() } + pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Arc<FontData> { + 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<S: FontSource> FontContext<S> { 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<S: FontSource> FontContext<S> { style: ServoArc<FontStyleStruct>, size: Au, ) -> Arc<RwLock<FontGroup>> { - 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<S: FontSource> FontContext<S> { 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<S: FontSource> FontContext<S> { 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<S: FontSource> FontContext<S> { } /// 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<S: FontSource> FontContext<S> { ) -> Vec<FontTemplateRef> { 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<S: FontSource> FontContext<S> { 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<S: FontSource> FontContext<S> { 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<FontKey>, Vec<FontInstanceKey>); } -impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontContext<S>> { +impl<S: SystemFontServiceProxyTrait + 'static> FontContextWebFontMethods for Arc<FontContext<S>> { fn add_all_web_fonts_from_stylesheet( &self, stylesheet: &DocumentStyleSheet, @@ -325,8 +380,7 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte name: family_name.name.clone(), syntax: FontFamilyNameSyntax::Quoted, }); - self.font_source - .lock() + self.system_font_service_proxy .find_matching_font_templates(None, &family) .first() .cloned() @@ -373,24 +427,27 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte RemoteWebFontDownloader::download(url_source, this, web_font_family_name, state) }, Source::Local(ref local_family_name) => { - 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<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet) { let mut web_fonts = self.web_fonts.write(); - let mut fonts = self.cache.fonts.write(); - let mut font_groups = self.cache.resolved_font_groups.write(); + let mut fonts = self.fonts.write(); + let mut font_groups = self.resolved_font_groups.write(); // Cancel any currently in-progress web font loads. web_fonts.handle_stylesheet_removed(stylesheet); @@ -443,9 +500,9 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte } // Lock everything to prevent adding new fonts while we are cleaning up the old ones. - let web_fonts = self.web_fonts.write(); - let _fonts = self.cache.fonts.write(); - let _font_groups = self.cache.resolved_font_groups.write(); + let mut web_fonts = self.web_fonts.write(); + let _fonts = self.fonts.write(); + let _font_groups = self.resolved_font_groups.write(); let mut webrender_font_store = self.webrender_font_store.write(); let mut unused_identifiers: HashSet<FontIdentifier> = webrender_font_store @@ -459,13 +516,15 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte }); } + web_fonts.remove_all_font_data_for_identifiers(&unused_identifiers); + self.have_removed_web_fonts.store(false, Ordering::Relaxed); - webrender_font_store.remove_all_fonts_for_identifiers(unused_identifiers) + webrender_font_store.remove_all_fonts_for_identifiers(&unused_identifiers) } } -struct RemoteWebFontDownloader<FCT: FontSource> { - font_context: Arc<FontContext<FCT>>, +struct RemoteWebFontDownloader<Proxy: SystemFontServiceProxyTrait> { + font_context: Arc<FontContext<Proxy>>, url: ServoArc<Url>, web_font_family_name: LowercaseFontFamilyName, response_valid: Mutex<bool>, @@ -478,10 +537,10 @@ enum DownloaderResponseResult { Failure, } -impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> { +impl<Proxy: SystemFontServiceProxyTrait + 'static> RemoteWebFontDownloader<Proxy> { fn download( url_source: UrlSource, - font_context: Arc<FontContext<FCT>>, + font_context: Arc<FontContext<Proxy>>, web_font_family_name: LowercaseFontFamilyName, state: WebFontDownloadState, ) { @@ -548,7 +607,7 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> { ); 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<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> { }, }; - 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 not_cancelled = self - .font_context - .web_fonts - .write() - .handle_web_font_loaded(state, new_template); + 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, + font_data, + ); self.font_context .handle_web_font_load_finished(&state.finished_callback, not_cancelled); @@ -623,121 +690,12 @@ impl<FCT: FontSource + Send + 'static> RemoteWebFontDownloader<FCT> { } } -#[derive(Default)] -pub struct CachingFontSource<FCT: FontSource> { - font_cache_thread: ReentrantMutex<FCT>, - fonts: RwLock<HashMap<FontCacheKey, Option<FontRef>>>, - templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>, - resolved_font_groups: - RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, -} - -impl<FCT: FontSource> CachingFontSource<FCT> { - 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<FontTemplateRef> { - 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<FontStyleStruct>, - size: Au, - ) -> Arc<RwLock<FontGroup>> { - 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<FCT: FontSource> MallocSizeOf for CachingFontSource<FCT> { - 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::<usize>(); - 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::<usize>(); - key.size_of(ops) + templates_size - }) - .sum::<usize>(); - 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::<usize>(); - - 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<Vec<u8>>`), 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<IpcSharedMemory>, + /// A lazily-initialized copy of the data behind an [`Arc`] which can be used when + /// passing it to various APIs. + #[serde(skip)] + arc: OnceLock<Arc<Vec<u8>>>, +} + +impl FontData { + pub fn from_bytes(data: Vec<u8>) -> 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<Vec<u8>> { + 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<IpcSharedMemory> { + self.shared_memory.clone() + } +} #[derive(Default)] pub struct FontStore { pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>, 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<FontIdentifier, Arc<FontData>>, } pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>; @@ -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<FontData>, ) -> 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.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<FontData>, + ) { + self.font_data.insert(new_template.identifier.clone(), data); self.families .entry(family_name) .or_default() .add_template(new_template); - self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet); - true } 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<FontData> { + 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<Arc<FontData>> { + self.font_data.get(identifier).cloned() + } + + pub(crate) fn remove_all_font_data_for_identifiers( + &mut self, + identifiers: &HashSet<FontIdentifier>, + ) { + self.font_data + .retain(|font_identifier, _| identifiers.contains(font_identifier)); + } } #[derive(Default)] @@ -110,26 +196,32 @@ pub struct WebRenderFontStore { pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>; impl WebRenderFontStore { - pub(crate) fn get_font_instance<FCT: FontSource>( + pub(crate) fn get_font_instance<Proxy: SystemFontServiceProxyTrait>( &mut self, - font_cache_thread: &FCT, + font_context: &FontContext<Proxy>, 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<FontIdentifier>, + identifiers: &HashSet<FontIdentifier>, ) -> (Vec<FontKey>, Vec<FontInstanceKey>) { let mut removed_keys: HashSet<FontKey> = 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<AtomicRefCell<FontTemplate>>; @@ -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<Arc<Vec<u8>>>, /// 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<DocumentStyleSheet>, } -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<Vec<u8>>, - css_font_template_descriptors: &CSSFontFaceDescriptors, + descriptor: FontTemplateDescriptor, stylesheet: Option<DocumentStyleSheet>, ) -> Result<FontTemplate, &'static str> { - 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<Arc<Vec<u8>>> { - 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<Vec<u8>>; /// 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<Vec<u8>> { - 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<F>(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<F>(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 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<FontTemplate>, + template_data: Vec<(FontIdentifier, Arc<FontData>)>, } -/// 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<FontDescriptor>, SingleFontFamily, - IpcSender<Vec<SerializedFontTemplate>>, + IpcSender<FontTemplateRequestResult>, ), GetFontInstance( FontIdentifier, @@ -73,7 +72,7 @@ pub enum Command { FontInstanceFlags, IpcSender<FontInstanceKey>, ), - GetWebFont(IpcBytesReceiver, u32, IpcSender<FontKey>), + GetWebFont(Arc<FontData>, u32, IpcSender<FontKey>), GetWebFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>), Exit(IpcSender<()>), Ping, @@ -90,11 +89,11 @@ struct ResolvedGenericFontFamilies { system_ui: OnceCell<LowercaseFontFamilyName>, } -/// 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<Command>, - font_data: HashMap<FontIdentifier, Arc<Vec<u8>>>, local_families: FontStore, webrender_api: Box<dyn WebRenderFontApi>, webrender_fonts: HashMap<FontIdentifier, FontKey>, @@ -102,68 +101,62 @@ struct FontCache { generic_fonts: ResolvedGenericFontFamilies, } -impl FontCache { +#[derive(Clone, Deserialize, Serialize)] +pub struct SystemFontServiceProxySender(IpcSender<Command>); + +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<dyn WebRenderFontApi + Send>) -> 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<SerializedFontTemplate>, - 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<FontDescriptor>, + 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<Vec<u8>>, index: u32) -> FontKey; + fn get_web_font(&self, data: Arc<FontData>, 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<Arc<FontData>>; } -/// 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<Command>, +#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] +struct FontTemplateCacheKey { + font_descriptor: Option<FontDescriptor>, + family_descriptor: SingleFontFamily, +} + +/// The public interface to the [`SystemFontService`], used by per-Document `FontContext` +/// instances (via [`SystemFontServiceProxyTrait`]). +#[derive(Debug)] +pub struct SystemFontServiceProxy { + sender: ReentrantMutex<IpcSender<Command>>, + templates: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>, + data_cache: RwLock<HashMap<FontIdentifier, Arc<FontData>>>, } /// 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<dyn WebRenderFontApi + Send>) -> 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<FontTemplateRef> { + 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<Vec<u8>>, index: u32) -> FontKey { + fn get_web_font(&self, data: Arc<FontData>, 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<Arc<FontData>> { + 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<HashMap<String, FontTemplates>>, - find_font_count: Rc<Cell<isize>>, + families: Mutex<HashMap<String, FontTemplates>>, + data: Mutex<HashMap<FontIdentifier, Arc<FontData>>>, + 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"); - - 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)), + proxy.add_face(&mut fallback, "csstest-basic-regular"); + + { + 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<u8> = 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<FontTemplateRef> { - 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<Vec<u8>>, _: u32) -> webrender_api::FontKey { + fn get_web_font(&self, _data: Arc<fonts::FontData>, _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<Arc<fonts::FontData>> { + 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<FontCacheThread>; +pub type LayoutFontContext = FontContext<SystemFontServiceProxy>; type WebrenderImageCache = HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>; @@ -44,7 +44,7 @@ pub struct LayoutContext<'a> { pub image_cache: Arc<dyn ImageCache>, /// A FontContext to be used during layout. - pub font_context: Arc<FontContext<FontCacheThread>>, + pub font_context: Arc<FontContext<SystemFontServiceProxy>>, /// A cache of WebRender image info. pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>, 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<FontContext<FontCacheThread>>, + pub font_context: Arc<FontContext<SystemFontServiceProxy>>, /// Reference to the script thread image cache. pub image_cache: Arc<dyn ImageCache>, 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<FontCacheThread>, + font_context: &FontContext<SystemFontServiceProxy>, linebreaker: &mut LineBreaker, font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, @@ -410,7 +410,7 @@ impl TextRun { fn segment_text_by_font( &mut self, formatting_context_text: &str, - font_context: &FontContext<FontCacheThread>, + font_context: &FontContext<SystemFontServiceProxy>, font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, ) -> Vec<(TextRunSegment, FontRef)> { @@ -556,7 +556,7 @@ 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: &FontContext<FontCacheThread>, + font_context: &FontContext<SystemFontServiceProxy>, ) -> Option<FontRef> { 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<dyn ImageCache>, - /// A FontContext tFontCacheThreadImplg layout. - font_context: Arc<FontContext<FontCacheThread>>, + /// A per-layout FontContext managing font access. + font_context: Arc<FontContext<SystemFontServiceProxy>>, - /// Is this the first reflow iFontCacheThreadImplread? + /// Is this the first reflow in this layout? first_reflow: Cell<bool>, /// 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<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, resource_threads: ResourceThreads, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<dyn ImageCache>, /// A FontContext to be used during layout. - font_context: Arc<FontContext<FontCacheThread>>, + font_context: Arc<FontContext<SystemFontServiceProxy>>, /// Is this the first reflow in this LayoutThread? first_reflow: Cell<bool>, @@ -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<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, resource_threads: ResourceThreads, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<FontContext<FontCacheThread>>); +struct LayoutFontMetricsProvider(Arc<FontContext<SystemFontServiceProxy>>); 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<LayoutMsg>, - /// 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<SystemFontServiceProxy>, /// 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<dyn LayoutFactory>, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<MainThreadScriptMsg>, chan: Sender<MainThreadScriptMsg>, layout_factory: Arc<dyn LayoutFactory>, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<Vec<u8>>, index: u32) -> FontKey { + fn add_font(&self, data: Arc<IpcSharedMemory>, 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<IpcSharedMemory>, font_index: u32, result_sender: IpcSender<FontKey>, ) { 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<ConstellationControlMsg>, pub image_cache: Arc<dyn ImageCache>, pub resource_threads: ResourceThreads, - pub font_cache_thread: FontCacheThread, + pub system_font_service: Arc<SystemFontServiceProxy>, 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<dyn LayoutFactory>, - font_cache_thread: FontCacheThread, + system_font_service: Arc<SystemFontServiceProxy>, 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<Vec<u8>>, index: u32) -> FontKey; + fn add_font(&self, data: Arc<IpcSharedMemory>, 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<IpcSharedMemory>, font_index: u32, result_sender: IpcSender<FontKey>, ); @@ -219,7 +219,7 @@ pub enum CanvasToCompositorMsg { pub enum FontToCompositorMsg { AddFontInstance(FontKey, f32, FontInstanceFlags, Sender<FontInstanceKey>), - AddFont(Sender<FontKey>, u32, IpcBytesReceiver), + AddFont(Sender<FontKey>, u32, Arc<IpcSharedMemory>), AddSystemFont(Sender<FontKey>, NativeFontHandle), } |