servo/components/gfx/font_template.rs
Patrick Walton 750bbed2cb gfx: Perform more aggressive caching in
`FontContext::get_layout_font_group_for_style()`.

There are several optimizations here:

* We make font families atoms, to allow for quicker comparisons.

* We precalculate an FNV hash of the relevant fields of the font style
  structure.

* When obtaining a platform font group, we first check pointer equality
  for the font style. If there's no match, we go to the FNV hash. Only
  if both caches miss do we construct and cache a font group. Note that
  individual fonts are *also* cached; thus there are two layers of
  caching here.

15% improvement in total layout thread time for Facebook Timeline.
2015-04-01 08:58:16 -07:00

165 lines
5.8 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use font::FontHandleMethods;
use platform::font_context::FontContextHandle;
use platform::font::FontHandle;
use platform::font_template::FontTemplateData;
use std::borrow::ToOwned;
use std::sync::{Arc, Weak};
use style::computed_values::{font_stretch, font_weight};
/// Describes how to select a font from a given family. This is very basic at the moment and needs
/// to be expanded or refactored when we support more of the font styling parameters.
///
/// NB: If you change this, you will need to update `style::properties::compute_font_hash()`.
#[derive(Clone, Copy, Eq, Hash)]
pub struct FontTemplateDescriptor {
pub weight: font_weight::T,
pub stretch: font_stretch::T,
pub italic: bool,
}
impl FontTemplateDescriptor {
#[inline]
pub fn new(weight: font_weight::T, stretch: font_stretch::T, italic: bool)
-> FontTemplateDescriptor {
FontTemplateDescriptor {
weight: weight,
stretch: stretch,
italic: italic,
}
}
}
impl PartialEq for FontTemplateDescriptor {
fn eq(&self, other: &FontTemplateDescriptor) -> bool {
self.weight.is_bold() == other.weight.is_bold() &&
self.stretch == other.stretch &&
self.italic == other.italic
}
}
/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
pub struct FontTemplate {
identifier: String,
descriptor: Option<FontTemplateDescriptor>,
weak_ref: Option<Weak<FontTemplateData>>,
// GWTODO: Add code path to unset the strong_ref for web fonts!
strong_ref: Option<Arc<FontTemplateData>>,
is_valid: bool,
}
/// Holds all of the template information for a font that
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
pub fn new(identifier: &str, maybe_bytes: Option<Vec<u8>>) -> FontTemplate {
let maybe_data = match maybe_bytes {
Some(_) => Some(FontTemplateData::new(identifier, maybe_bytes)),
None => None,
};
let maybe_strong_ref = match maybe_data {
Some(data) => Some(Arc::new(data)),
None => None,
};
let maybe_weak_ref = match maybe_strong_ref {
Some(ref strong_ref) => Some(strong_ref.downgrade()),
None => None,
};
FontTemplate {
identifier: identifier.to_owned(),
descriptor: None,
weak_ref: maybe_weak_ref,
strong_ref: maybe_strong_ref,
is_valid: true,
}
}
pub fn identifier<'a>(&'a self) -> &'a str {
&*self.identifier
}
/// Get the data for creating a font if it matches a given descriptor.
pub fn get_if_matches(&mut self,
fctx: &FontContextHandle,
requested_desc: &FontTemplateDescriptor)
-> Option<Arc<FontTemplateData>> {
// The font template data can be unloaded when nothing is referencing
// it (via the Weak reference to the Arc above). However, if we have
// already loaded a font, store the style information about it separately,
// so that we can do font matching against it again in the future
// without having to reload the font (unless it is an actual match).
match self.descriptor {
Some(actual_desc) => {
if *requested_desc == actual_desc {
Some(self.get_data())
} else {
None
}
},
None if self.is_valid => {
let data = self.get_data();
let handle: Result<FontHandle, ()> =
FontHandleMethods::new_from_template(fctx, data.clone(), None);
match handle {
Ok(handle) => {
let actual_desc = FontTemplateDescriptor::new(handle.boldness(),
handle.stretchiness(),
handle.is_italic());
let desc_match = actual_desc == *requested_desc;
self.descriptor = Some(actual_desc);
self.is_valid = true;
if desc_match {
Some(data)
} else {
None
}
}
Err(()) => {
self.is_valid = false;
debug!("Unable to create a font from template {}", self.identifier);
None
}
}
}
None => None,
}
}
/// Get the data for creating a font.
pub fn get(&mut self) -> Option<Arc<FontTemplateData>> {
if self.is_valid {
Some(self.get_data())
} else {
None
}
}
/// Get the font template data. If any strong references still
/// exist, it will return a clone, otherwise it will load the
/// font data and store a weak reference to it internally.
pub fn get_data(&mut self) -> Arc<FontTemplateData> {
let maybe_data = match self.weak_ref {
Some(ref data) => data.upgrade(),
None => None,
};
if let Some(data) = maybe_data {
return data
}
assert!(self.strong_ref.is_none());
let template_data = Arc::new(FontTemplateData::new(self.identifier.as_slice(), None));
self.weak_ref = Some(template_data.downgrade());
template_data
}
}