Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy load fonts in a FontGroup #20021

Merged
merged 2 commits into from Feb 22, 2018
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Prev

Add test for FontContext/FontGroup functionality

Unfortunately, this required quite a bit of changes to the non-test
code. That's because FontContext depends on a FontCacheThread, which in
turn depends on a CoreResourceThread and therefore lots of other data
structures.

It seemed like it would be very difficult to instantiate a FontContext
as it was, and even if we could it seems like overkill to have all these
data structures present for a relatively focused test.

Therefore, I created a FontSource trait which represents the interface
which FontContext uses to talk to FontCacheThread. FontCacheThread then
implements FontSource. Then, in the test, we can create a dummy
implementation of FontSource rather than using FontCacheThread.

This actually has the advantage that we can make our dummy
implementation behave in certain specific way which are useful for
testing, for example it can count the number of times
find_font_template() is called, which helps us verify that
caching/lazy-loading is working as intended.
  • Loading branch information
jonleighton committed Feb 22, 2018
commit e4acb3f77f12e0d42fb8084dafbf5de59f7a1c1b
@@ -4,7 +4,7 @@

use app_units::Au;
use euclid::{Point2D, Rect, Size2D};
use font_context::FontContext;
use font_context::{FontContext, FontSource};
use font_template::FontTemplateDescriptor;
use ordered_float::NotNaN;
use platform::font::{FontHandle, FontTable};
@@ -341,23 +341,33 @@ 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(&mut self, mut font_context: &mut FontContext, codepoint: char) -> Option<FontRef> {
pub fn find_by_codepoint<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>,
codepoint: char
) -> Option<FontRef> {
self.find(&mut font_context, |font| font.borrow().has_glyph_for(codepoint))
.or_else(|| self.first(&mut font_context))
}

pub fn first(&mut self, mut font_context: &mut FontContext) -> Option<FontRef> {
pub fn first<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>
) -> Option<FontRef> {
self.find(&mut font_context, |_| true)
}

/// Find a font which returns true for `predicate`. This method mutates because we may need to
/// load new font data in the process of finding a suitable font.
fn find<P>(
fn find<S, P>(
&mut self,
mut font_context: &mut FontContext,
mut font_context: &mut FontContext<S>,
mut predicate: P
) -> Option<FontRef>
where P: FnMut(&FontRef) -> bool {
where
S: FontSource,
P: FnMut(&FontRef) -> bool
{
self.families.iter_mut()
.filter_map(|family| family.font(&mut font_context))
.find(|f| predicate(f))
@@ -392,7 +402,7 @@ impl FontGroupFamily {
/// Returns the font within this family which matches the style. We'll fetch the data from the
/// `FontContext` the first time this method is called, and return a cached reference on
/// subsequent calls.
fn font(&mut self, font_context: &mut FontContext) -> Option<FontRef> {
fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
if !self.loaded {
self.font = font_context.font(&self.descriptor, &self.family);
self.loaded = true;
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use font_context::FontSource;
use font_template::{FontTemplate, FontTemplateDescriptor};
use fontsan;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
@@ -30,7 +31,7 @@ use style::values::computed::font::{SingleFontFamily, FamilyName};
use webrender_api;

/// A list of font templates that make up a given font family.
struct FontTemplates {
pub struct FontTemplates {
templates: Vec<FontTemplate>,
}

@@ -41,14 +42,14 @@ pub struct FontTemplateInfo {
}

impl FontTemplates {
fn new() -> FontTemplates {
pub fn new() -> FontTemplates {
FontTemplates {
templates: vec!(),
}
}

/// Find a font in this family that matches a given descriptor.
fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
pub fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
-> Option<Arc<FontTemplateData>> {
// TODO(Issue #189): optimize lookup for
// regular/bold/italic/bolditalic with fixed offsets and a
@@ -89,7 +90,7 @@ impl FontTemplates {
None
}

fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
pub fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
for template in &self.templates {
if *template.identifier() == identifier {
return;
@@ -414,8 +415,8 @@ impl FontCache {
}
}

/// The public interface to the font cache thread, used exclusively by
/// the per-thread/thread FontContext structures.
/// 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>,
@@ -453,7 +454,31 @@ impl FontCacheThread {
}
}

pub fn find_font_template(&self, family: SingleFontFamily, desc: FontTemplateDescriptor)
pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap();
}

pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message");
response_port.recv().expect("Couldn't receive FontCacheThread reply");
}
}

impl FontSource for FontCacheThread {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");

let instance_key = response_port.recv()
.expect("failed to receive response to font request");

instance_key
}

fn find_font_template(&mut self, family: SingleFontFamily, desc: FontTemplateDescriptor)
-> Option<FontTemplateInfo> {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
@@ -470,7 +495,7 @@ impl FontCacheThread {
}
}

pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor)
fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor)
-> FontTemplateInfo {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
@@ -486,31 +511,8 @@ impl FontCacheThread {
}
}
}

pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap();
}

pub fn get_font_instance(&self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");

let instance_key = response_port.recv()
.expect("failed to receive response to font request");

instance_key
}

pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message");
response_port.recv().expect("Couldn't receive FontCacheThread reply");
}
}


#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct LowercaseString {
inner: String,
@@ -5,7 +5,8 @@
use app_units::Au;
use fnv::FnvHasher;
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontRef};
use font_cache_thread::{FontCacheThread, FontTemplateInfo};
use font_cache_thread::FontTemplateInfo;
use font_template::FontTemplateDescriptor;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use platform::font::FontHandle;
pub use platform::font_context::FontContextHandle;
@@ -20,6 +21,7 @@ use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use style::computed_values::font_variant_caps::T as FontVariantCaps;
use style::properties::style_structs::Font as FontStyleStruct;
use style::values::computed::font::SingleFontFamily;
use webrender_api;

static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)

@@ -58,14 +60,26 @@ impl FallbackFontCacheEntry {
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;

pub trait FontSource {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey;

fn find_font_template(
&mut self,
family: SingleFontFamily,
desc: FontTemplateDescriptor
) -> Option<FontTemplateInfo>;

fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor) -> FontTemplateInfo;
}

/// 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
/// required.
#[derive(Debug)]
pub struct FontContext {
pub struct FontContext<S: FontSource> {
platform_handle: FontContextHandle,
font_cache_thread: FontCacheThread,
font_source: S,

// TODO: The font context holds a strong ref to the cached fonts
// so they will never be released. Find out a good time to drop them.
@@ -81,12 +95,12 @@ pub struct FontContext {
epoch: usize,
}

impl FontContext {
pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
impl<S: FontSource> FontContext<S> {
pub fn new(font_source: S) -> FontContext<S> {
let handle = FontContextHandle::new();
FontContext {
platform_handle: handle,
font_cache_thread: font_cache_thread,
font_source,
font_cache: vec!(),
fallback_font_cache: vec!(),
font_group_cache: HashMap::with_hasher(Default::default()),
@@ -97,7 +111,7 @@ impl FontContext {
/// Create a `Font` for use in layout calculations, from a `FontTemplateInfo` returned by the
/// cache thread (which contains the underlying font data) and a `FontDescriptor` which
/// contains the styling parameters.
fn create_font(&self, info: FontTemplateInfo, descriptor: FontDescriptor) -> Result<Font, ()> {
fn create_font(&mut self, info: FontTemplateInfo, descriptor: FontDescriptor) -> Result<Font, ()> {
// TODO: (Bug #3463): Currently we only support fake small-caps
// painting. We should also support true small-caps (where the
// font supports it) in the future.
@@ -110,8 +124,7 @@ impl FontContext {
info.font_template,
Some(actual_pt_size))?;

let font_instance_key = self.font_cache_thread
.get_font_instance(info.font_key, actual_pt_size);
let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size);
Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key))
}

@@ -155,9 +168,9 @@ impl FontContext {
}

/// Creates a new font cache entry matching `descriptor` and `family`.
fn create_font_cache_entry(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> FontCacheEntry {
fn create_font_cache_entry(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> FontCacheEntry {
let font =
self.font_cache_thread.find_font_template(family.clone(), descriptor.template_descriptor.clone())
self.font_source.find_font_template(family.clone(), descriptor.template_descriptor.clone())
.and_then(|template_info|
self.create_font(template_info, descriptor.to_owned()).ok()
)
@@ -187,8 +200,8 @@ impl FontContext {
}

/// Creates a new fallback font cache entry matching `descriptor`.
fn create_fallback_font_cache_entry(&self, descriptor: &FontDescriptor) -> Option<FallbackFontCacheEntry> {
let template_info = self.font_cache_thread.last_resort_font_template(descriptor.template_descriptor.clone());
fn create_fallback_font_cache_entry(&mut self, descriptor: &FontDescriptor) -> Option<FallbackFontCacheEntry> {
let template_info = self.font_source.last_resort_font_template(descriptor.template_descriptor.clone());

match self.create_font(template_info, descriptor.to_owned()) {
Ok(font) =>
@@ -220,7 +233,7 @@ impl FontContext {
}
}

impl MallocSizeOf for FontContext {
impl<S: FontSource> MallocSizeOf for FontContext<S> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
// FIXME(njn): Measure other fields eventually.
self.platform_handle.size_of(ops)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.