fonts: Make FontKey and FontInstanceKey generation asynchronous (#33600)

Instead of a blocking a layout thread on the generation of WebRender
`FontKey`s and `FontInstanceKey`s, generate the keys ahead of time and
send the font data to WebRender asynchronously. This has the benefit of
allowing use of the font much more quickly in layout, though blocking
display list sending itself on the font data upload.

In order to make this work for web fonts, `FontContext` now asks the
`SystemFontService` for a `FontKey`s and `FontInstanceKey`s for new web
fonts. This should happen much more quickly as the `SystemFontService`
is only blocking in order to load system fonts into memory now. In
practice this still drops layout thread blocking to fractions of a
millisecond instead of multiple milliseconds as before.

In addition, ensure that we don't send font data or generate keys for
fonts that are used in layout but never added to display lists. This
should help to reduce memory usage and increase performance.

Performance of this change was verified by putting a microbenchmark
around `FontContext::create_font` which is what triggered font key
generation.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2024-10-01 17:31:26 +02:00 committed by GitHub
parent 05ecb8eddb
commit abad89a49c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 302 additions and 216 deletions

View file

@ -844,20 +844,20 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
}, },
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFont( ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFont(
font_key,
data, data,
index, index,
key_sender,
)) => { )) => {
let _ = key_sender.send(self.add_font(index, data)); self.add_font(font_key, index, data);
}, },
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFontInstance( ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFontInstance(
font_instance_key,
font_key, font_key,
size, size,
flags, flags,
sender,
)) => { )) => {
let _ = sender.send(self.add_font_instance(font_key, size, flags)); self.add_font_instance(font_instance_key, font_key, size, flags);
}, },
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts( ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts(
@ -884,33 +884,45 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn); .send_transaction(self.webrender_document, txn);
}, },
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::GenerateKeys(
number_of_font_keys,
number_of_font_instance_keys,
result_sender,
)) => {
let font_keys = (0..number_of_font_keys)
.map(|_| self.webrender_api.generate_font_key())
.collect();
let font_instance_keys = (0..number_of_font_instance_keys)
.map(|_| self.webrender_api.generate_font_instance_key())
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
},
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance(
font_instance_key,
font_key, font_key,
size, size,
flags, flags,
sender,
)) => { )) => {
let _ = sender.send(self.add_font_instance(font_key, size, flags)); self.add_font_instance(font_instance_key, font_key, size, flags);
}, },
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont(
key_sender, font_key,
index, index,
data, data,
)) => { )) => {
let _ = key_sender.send(self.add_font(index, data)); self.add_font(font_key, index, data);
}, },
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont(
key_sender, font_key,
native_handle, native_handle,
)) => { )) => {
let font_key = self.webrender_api.generate_font_key();
let mut transaction = Transaction::new(); let mut transaction = Transaction::new();
transaction.add_native_font(font_key, native_handle); transaction.add_native_font(font_key, native_handle);
self.webrender_api self.webrender_api
.send_transaction(self.webrender_document, transaction); .send_transaction(self.webrender_document, transaction);
let _ = key_sender.send(font_key);
}, },
ForwardedToCompositorMsg::Canvas(CanvasToCompositorMsg::GenerateKey(sender)) => { ForwardedToCompositorMsg::Canvas(CanvasToCompositorMsg::GenerateKey(sender)) => {
@ -956,26 +968,6 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.remove_pipeline_root_layer(pipeline_id); self.remove_pipeline_root_layer(pipeline_id);
let _ = sender.send(()); let _ = sender.send(());
}, },
CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService(
FontToCompositorMsg::AddFontInstance(_, _, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_instance_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout(
ScriptToCompositorMsg::AddFontInstance(_, _, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_instance_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::SystemFontService(
FontToCompositorMsg::AddFont(sender, _, _),
)) => {
let _ = sender.send(self.webrender_api.generate_font_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Layout(
ScriptToCompositorMsg::AddFont(_, _, sender),
)) => {
let _ = sender.send(self.webrender_api.generate_font_key());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas( CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas(
CanvasToCompositorMsg::GenerateKey(sender), CanvasToCompositorMsg::GenerateKey(sender),
)) => { )) => {
@ -2527,11 +2519,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn add_font_instance( fn add_font_instance(
&mut self, &mut self,
instance_key: FontInstanceKey,
font_key: FontKey, font_key: FontKey,
size: f32, size: f32,
flags: FontInstanceFlags, flags: FontInstanceFlags,
) -> FontInstanceKey { ) {
let instance_key = self.webrender_api.generate_font_instance_key();
let mut transaction = Transaction::new(); let mut transaction = Transaction::new();
let font_instance_options = FontInstanceOptions { let font_instance_options = FontInstanceOptions {
@ -2549,15 +2541,12 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.webrender_api self.webrender_api
.send_transaction(self.webrender_document, transaction); .send_transaction(self.webrender_document, transaction);
instance_key
} }
fn add_font(&mut self, index: u32, data: Arc<IpcSharedMemory>) -> FontKey { fn add_font(&mut self, font_key: FontKey, index: u32, data: Arc<IpcSharedMemory>) {
let font_key = self.webrender_api.generate_font_key();
let mut transaction = Transaction::new(); let mut transaction = Transaction::new();
transaction.add_raw_font(font_key, (**data).into(), index); transaction.add_raw_font(font_key, (**data).into(), index);
self.webrender_api self.webrender_api
.send_transaction(self.webrender_document, transaction); .send_transaction(self.webrender_document, transaction);
font_key
} }
} }

View file

@ -224,7 +224,7 @@ pub struct Font {
pub descriptor: FontDescriptor, pub descriptor: FontDescriptor,
shaper: OnceLock<Shaper>, shaper: OnceLock<Shaper>,
cached_shape_data: RwLock<CachedShapeData>, cached_shape_data: RwLock<CachedShapeData>,
pub font_key: FontInstanceKey, pub font_instance_key: OnceLock<FontInstanceKey>,
/// If this is a synthesized small caps font, then this font reference is for /// If this is a synthesized small caps font, then this font reference is for
/// the version of the font used to replace lowercase ASCII letters. It's up /// the version of the font used to replace lowercase ASCII letters. It's up
@ -252,7 +252,9 @@ impl malloc_size_of::MallocSizeOf for Font {
self.metrics.size_of(ops) + self.metrics.size_of(ops) +
self.descriptor.size_of(ops) + self.descriptor.size_of(ops) +
self.cached_shape_data.read().size_of(ops) + self.cached_shape_data.read().size_of(ops) +
self.font_key.size_of(ops) self.font_instance_key
.get()
.map_or(0, |key| key.size_of(ops))
} }
} }
@ -278,7 +280,7 @@ impl Font {
descriptor, descriptor,
metrics, metrics,
cached_shape_data: Default::default(), cached_shape_data: Default::default(),
font_key: FontInstanceKey::default(), font_instance_key: Default::default(),
synthesized_small_caps, synthesized_small_caps,
has_color_bitmap_or_colr_table: OnceLock::new(), has_color_bitmap_or_colr_table: OnceLock::new(),
can_do_fast_shaping: OnceLock::new(), can_do_fast_shaping: OnceLock::new(),
@ -301,6 +303,12 @@ impl Font {
self.table_for_tag(COLR).is_some() self.table_for_tag(COLR).is_some()
}) })
} }
pub fn key(&self, font_context: &FontContext) -> FontInstanceKey {
*self
.font_instance_key
.get_or_init(|| font_context.create_font_instance_key(self))
}
} }
bitflags! { bitflags! {

View file

@ -12,7 +12,6 @@ use app_units::Au;
use crossbeam_channel::unbounded; use crossbeam_channel::unbounded;
use fnv::FnvHasher; use fnv::FnvHasher;
use fonts_traits::WebFontLoadFinishedCallback; use fonts_traits::WebFontLoadFinishedCallback;
use ipc_channel::ipc;
use log::{debug, trace}; use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
@ -36,7 +35,7 @@ use webrender_traits::{ScriptToCompositorMsg, WebRenderScriptApi};
use crate::font::{ use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope,
}; };
use crate::font_store::{CrossThreadFontStore, CrossThreadWebRenderFontStore}; use crate::font_store::CrossThreadFontStore;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods};
use crate::platform::font::PlatformFont; use crate::platform::font::PlatformFont;
use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier}; use crate::system_font_service::{CSSFontFaceDescriptors, FontIdentifier};
@ -66,7 +65,15 @@ pub struct FontContext {
RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>, RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
web_fonts: CrossThreadFontStore, web_fonts: CrossThreadFontStore,
webrender_font_store: CrossThreadWebRenderFontStore,
/// A collection of WebRender [`FontKey`]s generated for the web fonts that this
/// [`FontContext`] controls.
webrender_font_keys: RwLock<HashMap<FontIdentifier, FontKey>>,
/// A collection of WebRender [`FontInstanceKey`]s generated for the web fonts that
/// this [`FontContext`] controls.
webrender_font_instance_keys: RwLock<HashMap<(FontKey, Au), FontInstanceKey>>,
have_removed_web_fonts: AtomicBool, have_removed_web_fonts: AtomicBool,
} }
@ -104,7 +111,8 @@ impl FontContext {
fonts: Default::default(), fonts: Default::default(),
resolved_font_groups: Default::default(), resolved_font_groups: Default::default(),
web_fonts: Arc::new(RwLock::default()), web_fonts: Arc::new(RwLock::default()),
webrender_font_store: Arc::new(RwLock::default()), webrender_font_keys: RwLock::default(),
webrender_font_instance_keys: RwLock::default(),
have_removed_web_fonts: AtomicBool::new(false), have_removed_web_fonts: AtomicBool::new(false),
} }
} }
@ -274,71 +282,82 @@ impl FontContext {
font_descriptor: FontDescriptor, font_descriptor: FontDescriptor,
synthesized_small_caps: Option<FontRef>, synthesized_small_caps: Option<FontRef>,
) -> Result<FontRef, &'static str> { ) -> Result<FontRef, &'static str> {
let mut font = Font::new( Ok(Arc::new(Font::new(
font_template.clone(), font_template.clone(),
font_descriptor.clone(), font_descriptor.clone(),
self.get_font_data(&font_template.identifier()), self.get_font_data(&font_template.identifier()),
synthesized_small_caps, synthesized_small_caps,
)?; )?))
}
font.font_key = match font_template.identifier() { pub(crate) fn create_font_instance_key(&self, font: &Font) -> FontInstanceKey {
let result = match font.template.identifier() {
FontIdentifier::Local(_) | FontIdentifier::Mock(_) => { FontIdentifier::Local(_) | FontIdentifier::Mock(_) => {
self.system_font_service_proxy.get_system_font_instance( self.system_font_service_proxy.get_system_font_instance(
font_template.identifier(), font.template.identifier(),
font_descriptor.pt_size, font.descriptor.pt_size,
font.webrender_font_instance_flags(), font.webrender_font_instance_flags(),
) )
}, },
FontIdentifier::Web(_) => self.webrender_font_store.write().get_font_instance( FontIdentifier::Web(_) => self.create_web_font_instance(
self, self,
font_template.clone(), font.template.clone(),
font_descriptor.pt_size, font.descriptor.pt_size,
font.webrender_font_instance_flags(), font.webrender_font_instance_flags(),
), ),
}; };
result
}
Ok(Arc::new(font)) pub(crate) fn create_web_font_instance(
&self,
font_context: &FontContext,
font_template: FontTemplateRef,
pt_size: Au,
flags: FontInstanceFlags,
) -> FontInstanceKey {
let identifier = font_template.identifier().clone();
let font_data = font_context.get_font_data(&identifier);
let font_key = *self
.webrender_font_keys
.write()
.entry(identifier.clone())
.or_insert_with(|| {
let font_key = self.system_font_service_proxy.generate_font_key();
let _ = self
.webrender_api
.lock()
.sender()
.send(ScriptToCompositorMsg::AddFont(
font_key,
font_data.as_ipc_shared_memory(),
identifier.index(),
));
font_key
});
let key = *self
.webrender_font_instance_keys
.write()
.entry((font_key, pt_size))
.or_insert_with(|| {
let font_instance_key = self.system_font_service_proxy.generate_font_instance_key();
let _ = self.webrender_api.lock().sender().send(
ScriptToCompositorMsg::AddFontInstance(
font_instance_key,
font_key,
pt_size.to_f32_px(),
flags,
),
);
font_instance_key
});
key
} }
fn invalidate_font_groups_after_web_font_load(&self) { fn invalidate_font_groups_after_web_font_load(&self) {
self.resolved_font_groups.write().clear(); self.resolved_font_groups.write().clear();
} }
pub(crate) 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 _ = self
.webrender_api
.lock()
.sender()
.send(ScriptToCompositorMsg::AddFont(
data.as_ipc_shared_memory(),
index,
result_sender,
));
result_receiver.recv().unwrap()
}
pub(crate) fn get_web_font_instance(
&self,
font_key: FontKey,
font_size: f32,
font_flags: FontInstanceFlags,
) -> FontInstanceKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
let _ = self
.webrender_api
.lock()
.sender()
.send(ScriptToCompositorMsg::AddFontInstance(
font_key,
font_size,
font_flags,
result_sender,
));
result_receiver.recv().unwrap()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -538,9 +557,16 @@ impl FontContextWebFontMethods for Arc<FontContext> {
all: bool, all: bool,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) { ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
if all { if all {
let mut webrender_font_store = self.webrender_font_store.write(); let mut webrender_font_keys = self.webrender_font_keys.write();
let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
self.have_removed_web_fonts.store(false, Ordering::Relaxed); self.have_removed_web_fonts.store(false, Ordering::Relaxed);
return webrender_font_store.remove_all_fonts(); return (
webrender_font_keys.drain().map(|(_, key)| key).collect(),
webrender_font_instance_keys
.drain()
.map(|(_, key)| key)
.collect(),
);
} }
if !self.have_removed_web_fonts.load(Ordering::Relaxed) { if !self.have_removed_web_fonts.load(Ordering::Relaxed) {
@ -551,13 +577,11 @@ impl FontContextWebFontMethods for Arc<FontContext> {
let mut web_fonts = self.web_fonts.write(); let mut web_fonts = self.web_fonts.write();
let _fonts = self.fonts.write(); let _fonts = self.fonts.write();
let _font_groups = self.resolved_font_groups.write(); let _font_groups = self.resolved_font_groups.write();
let mut webrender_font_store = self.webrender_font_store.write(); let mut webrender_font_keys = self.webrender_font_keys.write();
let mut webrender_font_instance_keys = self.webrender_font_instance_keys.write();
let mut unused_identifiers: HashSet<FontIdentifier> = webrender_font_store let mut unused_identifiers: HashSet<FontIdentifier> =
.webrender_font_key_map webrender_font_keys.keys().cloned().collect();
.keys()
.cloned()
.collect();
for templates in web_fonts.families.values() { for templates in web_fonts.families.values() {
templates.for_all_identifiers(|identifier| { templates.for_all_identifiers(|identifier| {
unused_identifiers.remove(identifier); unused_identifiers.remove(identifier);
@ -567,7 +591,31 @@ impl FontContextWebFontMethods for Arc<FontContext> {
web_fonts.remove_all_font_data_for_identifiers(&unused_identifiers); web_fonts.remove_all_font_data_for_identifiers(&unused_identifiers);
self.have_removed_web_fonts.store(false, Ordering::Relaxed); self.have_removed_web_fonts.store(false, Ordering::Relaxed);
webrender_font_store.remove_all_fonts_for_identifiers(&unused_identifiers)
let mut removed_keys: HashSet<FontKey> = HashSet::new();
webrender_font_keys.retain(|identifier, font_key| {
if unused_identifiers.contains(identifier) {
removed_keys.insert(*font_key);
false
} else {
true
}
});
let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
webrender_font_instance_keys.retain(|(font_key, _), instance_key| {
if removed_keys.contains(font_key) {
removed_instance_keys.insert(*instance_key);
false
} else {
true
}
});
(
removed_keys.into_iter().collect(),
removed_instance_keys.into_iter().collect(),
)
} }
} }

View file

@ -5,7 +5,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::{Arc, OnceLock}; use std::sync::{Arc, OnceLock};
use app_units::Au;
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use ipc_channel::ipc::IpcSharedMemory; use ipc_channel::ipc::IpcSharedMemory;
use log::warn; use log::warn;
@ -13,13 +12,11 @@ use parking_lot::RwLock;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use style::stylesheets::DocumentStyleSheet; use style::stylesheets::DocumentStyleSheet;
use style::values::computed::{FontStyle, FontWeight}; use style::values::computed::{FontStyle, FontWeight};
use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey};
use crate::font::FontDescriptor; use crate::font::FontDescriptor;
use crate::font_context::WebFontDownloadState; use crate::font_context::WebFontDownloadState;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique}; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName}; use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName};
use crate::FontContext;
/// A data structure to store data for fonts. If sent across IPC channels and only a /// 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 /// [`IpcSharedMemory`] handle is sent, avoiding the overhead of serialization and
@ -186,84 +183,6 @@ impl FontStore {
} }
} }
#[derive(Default)]
pub struct WebRenderFontStore {
pub(crate) webrender_font_key_map: HashMap<FontIdentifier, FontKey>,
pub(crate) webrender_font_instance_map: HashMap<(FontKey, Au), FontInstanceKey>,
}
pub(crate) type CrossThreadWebRenderFontStore = Arc<RwLock<WebRenderFontStore>>;
impl WebRenderFontStore {
pub(crate) fn get_font_instance(
&mut self,
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(|| {
let data = font_context.get_font_data(&identifier);
font_context.get_web_font(data, identifier.index())
});
*self
.webrender_font_instance_map
.entry((font_key, pt_size))
.or_insert_with(|| {
font_context.get_web_font_instance(font_key, pt_size.to_f32_px(), flags)
})
}
pub(crate) fn remove_all_fonts(&mut self) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
(
self.webrender_font_key_map
.drain()
.map(|(_, key)| key)
.collect(),
self.webrender_font_instance_map
.drain()
.map(|(_, key)| key)
.collect(),
)
}
pub(crate) fn remove_all_fonts_for_identifiers(
&mut self,
identifiers: &HashSet<FontIdentifier>,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
let mut removed_keys: HashSet<FontKey> = HashSet::new();
self.webrender_font_key_map.retain(|identifier, font_key| {
if identifiers.contains(identifier) {
removed_keys.insert(*font_key);
false
} else {
true
}
});
let mut removed_instance_keys: HashSet<FontInstanceKey> = HashSet::new();
self.webrender_font_instance_map
.retain(|(font_key, _), instance_key| {
if removed_keys.contains(font_key) {
removed_instance_keys.insert(*instance_key);
false
} else {
true
}
});
(
removed_keys.into_iter().collect(),
removed_instance_keys.into_iter().collect(),
)
}
}
/// A struct that represents the available templates in a "simple family." A simple family /// A struct that represents the available templates in a "simple family." A simple family
/// is one that contains <= 4 available faces: regular, bold, italic, and bold italic. Having /// is one that contains <= 4 available faces: regular, bold, italic, and bold italic. Having
/// this simple family abstraction makes font matching much faster for families that don't /// this simple family abstraction makes font matching much faster for families that don't

View file

@ -74,6 +74,8 @@ pub enum SystemFontServiceMessage {
FontInstanceFlags, FontInstanceFlags,
IpcSender<FontInstanceKey>, IpcSender<FontInstanceKey>,
), ),
GetFontKey(IpcSender<FontKey>),
GetFontInstanceKey(IpcSender<FontInstanceKey>),
Exit(IpcSender<()>), Exit(IpcSender<()>),
Ping, Ping,
} }
@ -99,6 +101,18 @@ pub struct SystemFontService {
webrender_fonts: HashMap<FontIdentifier, FontKey>, webrender_fonts: HashMap<FontIdentifier, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>, font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
generic_fonts: ResolvedGenericFontFamilies, generic_fonts: ResolvedGenericFontFamilies,
/// This is an optimization that allows the [`SystemFontService`] to send font data to
/// the compositor asynchronously for creating WebRender fonts, while immediately
/// returning a font key for that data. Once the free keys are exhausted, the
/// [`SystemFontService`] will fetch a new batch.
free_font_keys: Vec<FontKey>,
/// This is an optimization that allows the [`SystemFontService`] to create WebRender font
/// instances in the compositor asynchronously, while immediately returning a font
/// instance key for the instance. Once the free keys are exhausted, the
/// [`SystemFontService`] will fetch a new batch.
free_font_instance_keys: Vec<FontInstanceKey>,
} }
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
@ -129,6 +143,8 @@ impl SystemFontService {
webrender_fonts: HashMap::new(), webrender_fonts: HashMap::new(),
font_instances: HashMap::new(), font_instances: HashMap::new(),
generic_fonts: Default::default(), generic_fonts: Default::default(),
free_font_keys: Default::default(),
free_font_instance_keys: Default::default(),
}; };
cache.refresh_local_families(); cache.refresh_local_families();
@ -158,6 +174,14 @@ impl SystemFontService {
SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => { SystemFontServiceMessage::GetFontInstance(identifier, pt_size, flags, result) => {
let _ = result.send(self.get_font_instance(identifier, pt_size, flags)); let _ = result.send(self.get_font_instance(identifier, pt_size, flags));
}, },
SystemFontServiceMessage::GetFontKey(result_sender) => {
self.fetch_new_keys();
let _ = result_sender.send(self.free_font_keys.pop().unwrap());
},
SystemFontServiceMessage::GetFontInstanceKey(result_sender) => {
self.fetch_new_keys();
let _ = result_sender.send(self.free_font_instance_keys.pop().unwrap());
},
SystemFontServiceMessage::Ping => (), SystemFontServiceMessage::Ping => (),
SystemFontServiceMessage::Exit(result) => { SystemFontServiceMessage::Exit(result) => {
let _ = result.send(()); let _ = result.send(());
@ -167,6 +191,22 @@ impl SystemFontService {
} }
} }
fn fetch_new_keys(&mut self) {
if !self.free_font_keys.is_empty() && !self.free_font_instance_keys.is_empty() {
return;
}
const FREE_FONT_KEYS_BATCH_SIZE: usize = 20;
const FREE_FONT_INSTANCE_KEYS_BATCH_SIZE: usize = 20;
let (mut new_font_keys, mut new_font_instance_keys) = self.webrender_api.fetch_font_keys(
FREE_FONT_KEYS_BATCH_SIZE - self.free_font_keys.len(),
FREE_FONT_INSTANCE_KEYS_BATCH_SIZE - self.free_font_instance_keys.len(),
);
self.free_font_keys.append(&mut new_font_keys);
self.free_font_instance_keys
.append(&mut new_font_instance_keys);
}
fn get_font_templates( fn get_font_templates(
&mut self, &mut self,
font_descriptor: Option<FontDescriptor>, font_descriptor: Option<FontDescriptor>,
@ -239,6 +279,8 @@ impl SystemFontService {
pt_size: Au, pt_size: Au,
flags: FontInstanceFlags, flags: FontInstanceFlags,
) -> FontInstanceKey { ) -> FontInstanceKey {
self.fetch_new_keys();
let webrender_font_api = &self.webrender_api; let webrender_font_api = &self.webrender_api;
let webrender_fonts = &mut self.webrender_fonts; let webrender_fonts = &mut self.webrender_fonts;
let font_data = self.local_families.get_or_initialize_font_data(&identifier); let font_data = self.local_families.get_or_initialize_font_data(&identifier);
@ -246,6 +288,7 @@ impl SystemFontService {
let font_key = *webrender_fonts let font_key = *webrender_fonts
.entry(identifier.clone()) .entry(identifier.clone())
.or_insert_with(|| { .or_insert_with(|| {
let font_key = self.free_font_keys.pop().unwrap();
// CoreText cannot reliably create CoreTextFonts for system fonts stored // CoreText cannot reliably create CoreTextFonts for system fonts stored
// as part of TTC files, so on CoreText platforms, create a system font in // as part of TTC files, so on CoreText platforms, create a system font in
// WebRender using the LocalFontIdentifier. This has the downside of // WebRender using the LocalFontIdentifier. This has the downside of
@ -253,18 +296,31 @@ impl SystemFontService {
// this for those platforms. // this for those platforms.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
if let FontIdentifier::Local(local_font_identifier) = identifier { if let FontIdentifier::Local(local_font_identifier) = identifier {
return webrender_font_api webrender_font_api
.add_system_font(local_font_identifier.native_font_handle()); .add_system_font(font_key, local_font_identifier.native_font_handle());
return font_key;
} }
webrender_font_api.add_font(font_data.as_ipc_shared_memory(), identifier.index()) webrender_font_api.add_font(
font_key,
font_data.as_ipc_shared_memory(),
identifier.index(),
);
font_key
}); });
*self *self
.font_instances .font_instances
.entry((font_key, pt_size)) .entry((font_key, pt_size))
.or_insert_with(|| { .or_insert_with(|| {
webrender_font_api.add_font_instance(font_key, pt_size.to_f32_px(), flags) let font_instance_key = self.free_font_instance_keys.pop().unwrap();
webrender_font_api.add_font_instance(
font_instance_key,
font_key,
pt_size.to_f32_px(),
flags,
);
font_instance_key
}) })
} }
@ -514,6 +570,30 @@ impl SystemFontServiceProxy {
pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> { pub(crate) fn get_font_data(&self, identifier: &FontIdentifier) -> Option<Arc<FontData>> {
self.data_cache.read().get(identifier).cloned() self.data_cache.read().get(identifier).cloned()
} }
pub(crate) fn generate_font_key(&self) -> FontKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
self.sender
.lock()
.send(SystemFontServiceMessage::GetFontKey(result_sender))
.expect("failed to send message to system font service");
result_receiver
.recv()
.expect("Failed to communicate with system font service.")
}
pub(crate) fn generate_font_instance_key(&self) -> FontInstanceKey {
let (result_sender, result_receiver) =
ipc::channel().expect("failed to create IPC channel");
self.sender
.lock()
.send(SystemFontServiceMessage::GetFontInstanceKey(result_sender))
.expect("failed to send message to system font service");
result_receiver
.recv()
.expect("Failed to communicate with system font service.")
}
} }
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]

View file

@ -32,7 +32,7 @@ use style::values::computed::font::{
use style::values::computed::{FontLanguageOverride, XLang}; use style::values::computed::{FontLanguageOverride, XLang};
use style::values::generics::font::LineHeight; use style::values::generics::font::LineHeight;
use style::ArcSlice; use style::ArcSlice;
use webrender_api::{FontInstanceKey, IdNamespace}; use webrender_api::{FontInstanceKey, FontKey, IdNamespace};
use webrender_traits::WebRenderScriptApi; use webrender_traits::WebRenderScriptApi;
struct TestContext { struct TestContext {
@ -125,9 +125,13 @@ impl MockSystemFontService {
template_data, template_data,
}); });
}, },
SystemFontServiceMessage::GetFontInstanceKey(result_sender) |
SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => { SystemFontServiceMessage::GetFontInstance(_, _, _, result_sender) => {
let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0)); let _ = result_sender.send(FontInstanceKey(IdNamespace(0), 0));
}, },
SystemFontServiceMessage::GetFontKey(result_sender) => {
let _ = result_sender.send(FontKey(IdNamespace(0), 0));
},
SystemFontServiceMessage::Exit(result_sender) => { SystemFontServiceMessage::Exit(result_sender) => {
let _ = result_sender.send(()); let _ = result_sender.send(());
break; break;

View file

@ -378,8 +378,10 @@ impl TextRunScanner {
}, },
}; };
let font_instance_key = font.key(font_context);
let (run, break_at_zero) = TextRun::new( let (run, break_at_zero) = TextRun::new(
font, font,
font_instance_key,
run_info.text, run_info.text,
&options, &options,
run_info.bidi_level, run_info.bidi_level,

View file

@ -160,6 +160,7 @@ impl<'a> TextRun {
/// Constructs a new text run. Also returns if there is a line break at the beginning /// Constructs a new text run. Also returns if there is a line break at the beginning
pub fn new( pub fn new(
font: FontRef, font: FontRef,
font_key: FontInstanceKey,
text: String, text: String,
options: &ShapingOptions, options: &ShapingOptions,
bidi_level: bidi::Level, bidi_level: bidi::Level,
@ -171,7 +172,7 @@ impl<'a> TextRun {
TextRun { TextRun {
text: Arc::new(text), text: Arc::new(text),
font_metrics: font.metrics.clone(), font_metrics: font.metrics.clone(),
font_key: font.font_key, font_key,
pt_size: font.descriptor.pt_size, pt_size: font.descriptor.pt_size,
glyphs: Arc::new(glyphs), glyphs: Arc::new(glyphs),
bidi_level, bidi_level,

View file

@ -1528,8 +1528,11 @@ impl InlineFormattingContext {
&inline_box.style, &inline_box.style,
&layout_context.font_context, &layout_context.font_context,
) { ) {
inline_box.default_font_index = inline_box.default_font_index = Some(add_or_get_font(
Some(add_or_get_font(&font, &mut font_metrics)); &font,
&mut font_metrics,
&layout_context.font_context,
));
} }
}, },
InlineItem::Atomic(_, index_in_text, bidi_level) => { InlineItem::Atomic(_, index_in_text, bidi_level) => {

View file

@ -109,6 +109,7 @@ impl TextRunSegment {
script: Script, script: Script,
bidi_level: Level, bidi_level: Level,
fonts: &[FontKeyAndMetrics], fonts: &[FontKeyAndMetrics],
font_context: &FontContext,
) -> bool { ) -> bool {
fn is_specific(script: Script) -> bool { fn is_specific(script: Script) -> bool {
script != Script::Common && script != Script::Inherited script != Script::Common && script != Script::Inherited
@ -119,7 +120,7 @@ impl TextRunSegment {
} }
let current_font_key_and_metrics = &fonts[self.font_index]; let current_font_key_and_metrics = &fonts[self.font_index];
if new_font.font_key != current_font_key_and_metrics.key || if new_font.key(font_context) != current_font_key_and_metrics.key ||
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
{ {
return false; return false;
@ -440,15 +441,18 @@ impl TextRun {
let script = Script::from(character); let script = Script::from(character);
let bidi_level = bidi_info.levels[current_byte_index]; let bidi_level = bidi_info.levels[current_byte_index];
if let Some(current) = current.as_mut() { if let Some(current) = current.as_mut() {
if current if current.0.update_if_compatible(
.0 &font,
.update_if_compatible(&font, script, bidi_level, font_cache) script,
{ bidi_level,
font_cache,
font_context,
) {
continue; continue;
} }
} }
let font_index = add_or_get_font(&font, font_cache); let font_index = add_or_get_font(&font, font_cache, font_context);
// Add the new segment and finish the existing one, if we had one. If the first // Add the new segment and finish the existing one, if we had one. If the first
// characters in the run were control characters we may be creating the first // characters in the run were control characters we may be creating the first
@ -473,7 +477,7 @@ impl TextRun {
// of those cases, just use the first font. // of those cases, just use the first font.
if current.is_none() { if current.is_none() {
current = font_group.write().first(font_context).map(|font| { current = font_group.write().first(font_context).map(|font| {
let font_index = add_or_get_font(&font, font_cache); let font_index = add_or_get_font(&font, font_cache, font_context);
( (
TextRunSegment::new( TextRunSegment::new(
font_index, font_index,
@ -539,15 +543,22 @@ fn char_does_not_change_font(character: char) -> bool {
class == XI_LINE_BREAKING_CLASS_ZWJ class == XI_LINE_BREAKING_CLASS_ZWJ
} }
pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize { pub(super) fn add_or_get_font(
font: &FontRef,
ifc_fonts: &mut Vec<FontKeyAndMetrics>,
font_context: &FontContext,
) -> usize {
let font_instance_key = font.key(font_context);
for (index, ifc_font_info) in ifc_fonts.iter().enumerate() { for (index, ifc_font_info) in ifc_fonts.iter().enumerate() {
if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size { if ifc_font_info.key == font_instance_key &&
ifc_font_info.pt_size == font.descriptor.pt_size
{
return index; return index;
} }
} }
ifc_fonts.push(FontKeyAndMetrics { ifc_fonts.push(FontKeyAndMetrics {
metrics: font.metrics.clone(), metrics: font.metrics.clone(),
key: font.font_key, key: font_instance_key,
pt_size: font.descriptor.pt_size, pt_size: font.descriptor.pt_size,
}); });
ifc_fonts.len() - 1 ifc_fonts.len() - 1

View file

@ -1096,34 +1096,48 @@ struct WebRenderFontApiCompositorProxy(CompositorProxy);
impl WebRenderFontApi for WebRenderFontApiCompositorProxy { impl WebRenderFontApi for WebRenderFontApiCompositorProxy {
fn add_font_instance( fn add_font_instance(
&self, &self,
font_instance_key: FontInstanceKey,
font_key: FontKey, font_key: FontKey,
size: f32, size: f32,
flags: FontInstanceFlags, flags: FontInstanceFlags,
) -> FontInstanceKey { ) {
let (sender, receiver) = unbounded();
self.0.send(CompositorMsg::Forwarded( self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance(
font_key, size, flags, sender, font_instance_key,
font_key,
size,
flags,
)), )),
)); ));
receiver.recv().unwrap()
} }
fn add_font(&self, data: Arc<IpcSharedMemory>, index: u32) -> FontKey { fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32) {
let (sender, receiver) = unbounded();
self.0.send(CompositorMsg::Forwarded( self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont(
sender, index, data, font_key, index, data,
)), )),
)); ));
receiver.recv().unwrap()
} }
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey { fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle) {
let (sender, receiver) = unbounded();
self.0.send(CompositorMsg::Forwarded( self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont( ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont(
sender, handle, font_key, handle,
)),
));
}
fn fetch_font_keys(
&self,
number_of_font_keys: usize,
number_of_font_instance_keys: usize,
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
let (sender, receiver) = unbounded();
self.0.send(CompositorMsg::Forwarded(
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::GenerateKeys(
number_of_font_keys,
number_of_font_instance_keys,
sender,
)), )),
)); ));
receiver.recv().unwrap() receiver.recv().unwrap()

View file

@ -186,12 +186,18 @@ impl ExternalImageHandler for WebrenderExternalImageHandlers {
pub trait WebRenderFontApi { pub trait WebRenderFontApi {
fn add_font_instance( fn add_font_instance(
&self, &self,
font_instance_key: FontInstanceKey,
font_key: FontKey, font_key: FontKey,
size: f32, size: f32,
flags: FontInstanceFlags, flags: FontInstanceFlags,
) -> FontInstanceKey; );
fn add_font(&self, data: Arc<IpcSharedMemory>, index: u32) -> FontKey; fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32);
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle);
fn fetch_font_keys(
&self,
number_of_font_keys: usize,
number_of_font_instance_keys: usize,
) -> (Vec<FontKey>, Vec<FontInstanceKey>);
} }
pub enum CanvasToCompositorMsg { pub enum CanvasToCompositorMsg {
@ -200,9 +206,10 @@ pub enum CanvasToCompositorMsg {
} }
pub enum FontToCompositorMsg { pub enum FontToCompositorMsg {
AddFontInstance(FontKey, f32, FontInstanceFlags, Sender<FontInstanceKey>), GenerateKeys(usize, usize, Sender<(Vec<FontKey>, Vec<FontInstanceKey>)>),
AddFont(Sender<FontKey>, u32, Arc<IpcSharedMemory>), AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags),
AddSystemFont(Sender<FontKey>, NativeFontHandle), AddFont(FontKey, u32, Arc<IpcSharedMemory>),
AddSystemFont(FontKey, NativeFontHandle),
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
@ -242,8 +249,8 @@ pub enum ScriptToCompositorMsg {
UpdateImages(Vec<SerializedImageUpdate>), UpdateImages(Vec<SerializedImageUpdate>),
/// Remove the given font resources from our WebRender instance. /// Remove the given font resources from our WebRender instance.
RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>), RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
AddFontInstance(FontKey, f32, FontInstanceFlags, IpcSender<FontInstanceKey>), AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags),
AddFont(Arc<IpcSharedMemory>, u32, IpcSender<FontKey>), AddFont(FontKey, Arc<IpcSharedMemory>, u32),
} }
/// A mechanism to send messages from networking to the WebRender instance. /// A mechanism to send messages from networking to the WebRender instance.