mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
fonts: Make FontContext
thread-safe and share it per-Layout (#32205)
This allows sharing font templates, fonts, and platform fonts across layout threads. It's the first step toward storing web fonts in the layout versus the shared `FontCacheThread`. Now fonts and font groups have some locking (especially on FreeType), which will probably affect performance. On the other hand, we measured memory usage and this saves roughly 40 megabytes of memory when loading servo.org based on data from the memory profiler. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
8ec5344f70
commit
556bfb7dff
27 changed files with 437 additions and 500 deletions
|
@ -2,7 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -12,7 +11,7 @@ use msg::constellation_msg::PipelineId;
|
|||
use net_traits::image_cache::{
|
||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
||||
};
|
||||
use parking_lot::{ReentrantMutex, RwLock};
|
||||
use parking_lot::RwLock;
|
||||
use script_layout_interface::{PendingImage, PendingImageState};
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
use style::context::SharedStyleContext;
|
||||
|
@ -20,8 +19,6 @@ use style::dom::OpaqueNode;
|
|||
|
||||
use crate::display_list::WebRenderImageInfo;
|
||||
|
||||
thread_local!(static FONT_CONTEXT: RefCell<Option<FontContext<FontCacheThread>>> = RefCell::new(None));
|
||||
|
||||
pub struct LayoutContext<'a> {
|
||||
pub id: PipelineId,
|
||||
pub use_rayon: bool,
|
||||
|
@ -31,7 +28,7 @@ pub struct LayoutContext<'a> {
|
|||
pub style_context: SharedStyleContext<'a>,
|
||||
|
||||
/// A FontContext to be used during layout.
|
||||
pub font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>,
|
||||
pub font_context: Arc<FontContext<FontCacheThread>>,
|
||||
|
||||
/// Reference to the script thread image cache.
|
||||
pub image_cache: Arc<dyn ImageCache>,
|
||||
|
@ -133,27 +130,4 @@ impl<'a> LayoutContext<'a> {
|
|||
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_font_context<F, R>(&self, callback: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
|
||||
{
|
||||
with_thread_local_font_context(&self.font_cache_thread, callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_thread_local_font_context<F, R>(
|
||||
font_cache_thread: &ReentrantMutex<FontCacheThread>,
|
||||
callback: F,
|
||||
) -> R
|
||||
where
|
||||
F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
|
||||
{
|
||||
FONT_CONTEXT.with(|font_context| {
|
||||
callback(
|
||||
font_context
|
||||
.borrow_mut()
|
||||
.get_or_insert_with(|| FontContext::new(font_cache_thread.lock().clone())),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1638,10 +1638,9 @@ impl InlineFormattingContext {
|
|||
|
||||
// It's unfortunate that it isn't possible to get this during IFC text processing, but in
|
||||
// that situation the style of the containing block is unknown.
|
||||
let default_font_metrics = layout_context.with_font_context(|font_context| {
|
||||
get_font_for_first_font_for_style(style, font_context)
|
||||
.map(|font| font.borrow().metrics.clone())
|
||||
});
|
||||
let default_font_metrics =
|
||||
get_font_for_first_font_for_style(style, &layout_context.font_context)
|
||||
.map(|font| font.metrics.clone());
|
||||
|
||||
let style_text = containing_block.style.get_inherited_text();
|
||||
let mut ifc = InlineFormattingContextState {
|
||||
|
@ -1768,34 +1767,30 @@ impl InlineFormattingContext {
|
|||
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
|
||||
let mut on_word_boundary = true;
|
||||
|
||||
layout_context.with_font_context(|font_context| {
|
||||
let mut linebreaker = None;
|
||||
self.foreach(|iter_item| match iter_item {
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(
|
||||
ref mut text_run,
|
||||
)) => {
|
||||
text_run.break_and_shape(
|
||||
font_context,
|
||||
&mut linebreaker,
|
||||
&mut ifc_fonts,
|
||||
&mut last_inline_box_ended_with_white_space,
|
||||
&mut on_word_boundary,
|
||||
);
|
||||
},
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
|
||||
if let Some(font) =
|
||||
get_font_for_first_font_for_style(&inline_box.style, font_context)
|
||||
{
|
||||
inline_box.default_font_index =
|
||||
Some(add_or_get_font(&font, &mut ifc_fonts));
|
||||
}
|
||||
},
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
|
||||
last_inline_box_ended_with_white_space = false;
|
||||
on_word_boundary = true;
|
||||
},
|
||||
_ => {},
|
||||
});
|
||||
let mut linebreaker = None;
|
||||
self.foreach(|iter_item| match iter_item {
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(ref mut text_run)) => {
|
||||
text_run.break_and_shape(
|
||||
&layout_context.font_context,
|
||||
&mut linebreaker,
|
||||
&mut ifc_fonts,
|
||||
&mut last_inline_box_ended_with_white_space,
|
||||
&mut on_word_boundary,
|
||||
);
|
||||
},
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
|
||||
if let Some(font) = get_font_for_first_font_for_style(
|
||||
&inline_box.style,
|
||||
&layout_context.font_context,
|
||||
) {
|
||||
inline_box.default_font_index = Some(add_or_get_font(&font, &mut ifc_fonts));
|
||||
}
|
||||
},
|
||||
InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
|
||||
last_inline_box_ended_with_white_space = false;
|
||||
on_word_boundary = true;
|
||||
},
|
||||
_ => {},
|
||||
});
|
||||
|
||||
self.font_metrics = ifc_fonts;
|
||||
|
|
|
@ -111,7 +111,7 @@ impl TextRunSegment {
|
|||
/// compatible with this segment or false otherwise.
|
||||
fn update_if_compatible(
|
||||
&mut self,
|
||||
font: &FontRef,
|
||||
new_font: &FontRef,
|
||||
script: Script,
|
||||
fonts: &[FontKeyAndMetrics],
|
||||
) -> bool {
|
||||
|
@ -120,7 +120,6 @@ impl TextRunSegment {
|
|||
}
|
||||
|
||||
let current_font_key_and_metrics = &fonts[self.font_index];
|
||||
let new_font = font.borrow();
|
||||
if new_font.font_key != current_font_key_and_metrics.key ||
|
||||
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
|
||||
{
|
||||
|
@ -208,7 +207,7 @@ impl TextRun {
|
|||
|
||||
pub(super) fn break_and_shape(
|
||||
&mut self,
|
||||
font_context: &mut FontContext<FontCacheThread>,
|
||||
font_context: &FontContext<FontCacheThread>,
|
||||
linebreaker: &mut Option<LineBreakLeafIter>,
|
||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
||||
|
@ -244,7 +243,6 @@ impl TextRun {
|
|||
let segments = segment_results
|
||||
.into_iter()
|
||||
.map(|(mut segment, font)| {
|
||||
let mut font = font.borrow_mut();
|
||||
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
||||
let space_width = font
|
||||
.glyph_index(' ')
|
||||
|
@ -260,7 +258,7 @@ impl TextRun {
|
|||
};
|
||||
(segment.runs, segment.break_at_start) =
|
||||
gfx::text::text_run::TextRun::break_and_shape(
|
||||
&mut font,
|
||||
font,
|
||||
&self.text
|
||||
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
||||
&shaping_options,
|
||||
|
@ -280,7 +278,7 @@ impl TextRun {
|
|||
/// [`super::InlineFormattingContext`].
|
||||
fn segment_text(
|
||||
&mut self,
|
||||
font_context: &mut FontContext<FontCacheThread>,
|
||||
font_context: &FontContext<FontCacheThread>,
|
||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
||||
on_word_boundary: &mut bool,
|
||||
|
@ -341,7 +339,7 @@ impl TextRun {
|
|||
}
|
||||
|
||||
let font = match font_group
|
||||
.borrow_mut()
|
||||
.write()
|
||||
.find_by_codepoint(font_context, character)
|
||||
{
|
||||
Some(font) => font,
|
||||
|
@ -383,7 +381,7 @@ impl TextRun {
|
|||
// Either we have a current segment or we only had control character and whitespace. In both
|
||||
// of those cases, just use the first font.
|
||||
if current.is_none() {
|
||||
current = font_group.borrow_mut().first(font_context).map(|font| {
|
||||
current = font_group.write().first(font_context).map(|font| {
|
||||
let font_index = add_or_get_font(&font, font_cache);
|
||||
(
|
||||
TextRunSegment::new(font_index, Script::Common, ByteIndex(0)),
|
||||
|
@ -489,7 +487,6 @@ fn char_does_not_change_font(character: char) -> bool {
|
|||
}
|
||||
|
||||
pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize {
|
||||
let font = font.borrow();
|
||||
for (index, ifc_font_info) in ifc_fonts.iter().enumerate() {
|
||||
if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size {
|
||||
return index;
|
||||
|
@ -505,11 +502,11 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetr
|
|||
|
||||
pub(super) fn get_font_for_first_font_for_style(
|
||||
style: &ComputedValues,
|
||||
font_context: &mut FontContext<FontCacheThread>,
|
||||
font_context: &FontContext<FontCacheThread>,
|
||||
) -> Option<FontRef> {
|
||||
let font = font_context
|
||||
.font_group(style.clone_font())
|
||||
.borrow_mut()
|
||||
.write()
|
||||
.first(font_context);
|
||||
if font.is_none() {
|
||||
warn!("Could not find font for style: {:?}", style.clone_font());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue