Skip to content

Commit

Permalink
gfx: Remove FontTemplateData (#32034)
Browse files Browse the repository at this point in the history
Now that `FontTemplateData` is more or less the same on all platforms,
it can be removed. This is a preparatory change for a full refactor of
the font system on Servo. The major changes here are:

 - Remove `FontTemplateData` and move its members into `FontTemplate`
 - Make `FontTemplate` have full interior mutability instead of only
   the `FontTemplateData` member. This is preparation for having these
   data types `Send` and `Sync` with locking.
 - Remove the strong/weak reference concept for font data. In practice,
   all font data references were strong, so this was never fully
   complete. Instead of using this approach, the new font system will
   use a central font data cache with references associated to layouts.
 - The `CTFont` cache is now a global cache, so `CTFont`s can be shared
   between threads. The cache is cleared when clearing font caches.

A benefit of this change (apart from `CTFont` sharing) is that font data
loading is platform-independent now.
  • Loading branch information
mrobinson committed Apr 16, 2024
1 parent 689c144 commit 6b2fa91
Show file tree
Hide file tree
Showing 19 changed files with 392 additions and 598 deletions.
3 changes: 2 additions & 1 deletion components/canvas/canvas_data.rs
Expand Up @@ -17,6 +17,7 @@ use font_kit::source::SystemSource;
use gfx::font::FontHandleMethods;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use gfx::font_template::FontTemplateRefMethods;
use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
use log::{debug, error, warn};
use num_traits::ToPrimitive;
Expand Down Expand Up @@ -501,7 +502,7 @@ impl<'a> CanvasData<'a> {
.first(font_context)
.expect("couldn't find font");
let font = font.borrow_mut();
Font::from_bytes(font.handle.template().bytes(), 0)
Font::from_bytes(font.handle.template().data(), 0)
.ok()
.or_else(|| load_system_font_from_style(Some(style)))
})
Expand Down
14 changes: 5 additions & 9 deletions components/gfx/font.rs
Expand Up @@ -26,10 +26,9 @@ use webrender_api::FontInstanceKey;

use crate::font_cache_thread::FontIdentifier;
use crate::font_context::{FontContext, FontSource};
use crate::font_template::FontTemplateDescriptor;
use crate::font_template::{FontTemplateDescriptor, FontTemplateRef};
use crate::platform::font::{FontHandle, FontTable};
pub use crate::platform::font_list::fallback_font_families;
use crate::platform::font_template::FontTemplateData;
use crate::text::glyph::{ByteIndex, GlyphData, GlyphId, GlyphStore};
use crate::text::shaping::ShaperMethods;
use crate::text::Shaper;
Expand All @@ -55,11 +54,11 @@ static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0);

pub trait FontHandleMethods: Sized {
fn new_from_template(
template: Arc<FontTemplateData>,
template: FontTemplateRef,
pt_size: Option<Au>,
) -> Result<Self, &'static str>;

fn template(&self) -> Arc<FontTemplateData>;
fn template(&self) -> FontTemplateRef;
fn family_name(&self) -> Option<String>;
fn face_name(&self) -> Option<String>;

Expand All @@ -75,9 +74,6 @@ pub trait FontHandleMethods: Sized {
fn can_do_fast_shaping(&self) -> bool;
fn metrics(&self) -> FontMetrics;
fn table_for_tag(&self, _: FontTableTag) -> Option<FontTable>;

/// A unique identifier for the font, allowing comparison.
fn identifier(&self) -> &FontIdentifier;
}

// Used to abstract over the shaper's choice of fixed int representation.
Expand Down Expand Up @@ -201,8 +197,8 @@ impl Font {
}

/// A unique identifier for the font, allowing comparison.
pub fn identifier(&self) -> &FontIdentifier {
self.handle.identifier()
pub fn identifier(&self) -> FontIdentifier {
self.handle.template().borrow().identifier.clone()
}
}

Expand Down
145 changes: 75 additions & 70 deletions components/gfx/font_cache_thread.rs
Expand Up @@ -3,8 +3,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{f32, fmt, mem, thread};

Expand All @@ -25,22 +27,23 @@ use webrender_api::{FontInstanceKey, FontKey};

use crate::font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope};
use crate::font_context::FontSource;
use crate::font_template::{FontTemplate, FontTemplateDescriptor};
use crate::font_template::{
FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods,
};
use crate::platform::font_list::{
for_each_available_family, for_each_variation, system_default_family, LocalFontIdentifier,
SANS_SERIF_FONT_FAMILY,
};
use crate::platform::font_template::FontTemplateData;

/// A list of font templates that make up a given font family.
#[derive(Default)]
pub struct FontTemplates {
templates: Vec<FontTemplate>,
templates: Vec<FontTemplateRef>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug)]
pub struct FontTemplateInfo {
pub font_template: Arc<FontTemplateData>,
pub font_template: FontTemplateRef,
pub font_key: FontKey,
}

Expand All @@ -59,13 +62,19 @@ pub enum FontIdentifier {
#[derive(Debug, Deserialize, Serialize)]
pub struct SerializedFontTemplate {
identifier: FontIdentifier,
descriptor: Option<FontTemplateDescriptor>,
bytes_receiver: ipc_channel::ipc::IpcBytesReceiver,
}

impl SerializedFontTemplate {
pub fn to_font_template_data(&self) -> FontTemplateData {
pub fn to_font_template(&self) -> FontTemplate {
let font_data = self.bytes_receiver.recv().ok();
FontTemplateData::new(self.identifier.clone(), font_data).unwrap()
FontTemplate {
identifier: self.identifier.clone(),
descriptor: self.descriptor.clone(),
data: font_data.map(Arc::new),
is_valid: true,
}
}
}

Expand All @@ -74,40 +83,37 @@ impl FontTemplates {
pub fn find_font_for_style(
&mut self,
desc: &FontTemplateDescriptor,
) -> Option<Arc<FontTemplateData>> {
) -> Option<FontTemplateRef> {
// TODO(Issue #189): optimize lookup for
// regular/bold/italic/bolditalic with fixed offsets and a
// static decision table for fallback between these values.
for template in &mut self.templates {
let maybe_template = template.data_for_descriptor(desc);
if maybe_template.is_some() {
return maybe_template;
if template.descriptor_matches(desc) {
return Some(template.clone());
}
}

// We didn't find an exact match. Do more expensive fuzzy matching.
// TODO(#190): Do a better job.
let (mut best_template_data, mut best_distance) = (None, f32::MAX);
for template in &mut self.templates {
if let Some((template_data, distance)) = template.data_for_approximate_descriptor(desc)
{
let (mut best_template, mut best_distance) = (None, f32::MAX);
for template in self.templates.iter() {
if let Some(distance) = template.descriptor_distance(desc) {
if distance < best_distance {
best_template_data = Some(template_data);
best_template = Some(template);
best_distance = distance
}
}
}
if best_template_data.is_some() {
return best_template_data;
if best_template.is_some() {
return best_template.cloned();
}

// If a request is made for a font family that exists,
// pick the first valid font in the family if we failed
// to find an exact match for the descriptor.
for template in &mut self.templates {
let maybe_template = template.get();
if maybe_template.is_some() {
return maybe_template;
for template in &mut self.templates.iter() {
if template.is_valid() {
return Some(template.clone());
}
}

Expand All @@ -116,13 +122,13 @@ impl FontTemplates {

pub fn add_template(&mut self, identifier: FontIdentifier, maybe_data: Option<Vec<u8>>) {
for template in &self.templates {
if *template.identifier() == identifier {
if *template.borrow().identifier() == identifier {
return;
}
}

if let Ok(template) = FontTemplate::new(identifier, maybe_data) {
self.templates.push(template);
self.templates.push(Rc::new(RefCell::new(template)));
}
}
}
Expand Down Expand Up @@ -196,29 +202,30 @@ impl FontCache {

match msg {
Command::GetFontTemplate(template_descriptor, family_descriptor, result) => {
let maybe_font_template =
self.find_font_template(&template_descriptor, &family_descriptor);
match maybe_font_template {
None => {
let _ = result.send(Reply::GetFontTemplateReply(None));
},
Some(font_template_info) => {
let (bytes_sender, bytes_receiver) =
ipc::bytes_channel().expect("failed to create IPC channel");
let serialized_font_template = SerializedFontTemplate {
identifier: font_template_info.font_template.identifier.clone(),
bytes_receiver,
};
let Some(font_template_info) =
self.find_font_template(&template_descriptor, &family_descriptor)
else {
let _ = result.send(Reply::GetFontTemplateReply(None));
continue;
};

let _ = result.send(Reply::GetFontTemplateReply(Some(
SerializedFontTemplateInfo {
serialized_font_template,
font_key: font_template_info.font_key,
},
)));
let _ = bytes_sender.send(&font_template_info.font_template.bytes());
},
let (bytes_sender, bytes_receiver) =
ipc::bytes_channel().expect("failed to create IPC channel");
let serialized_font_template = SerializedFontTemplate {
identifier: font_template_info.font_template.borrow().identifier.clone(),
descriptor: font_template_info.font_template.borrow().descriptor.clone(),
bytes_receiver,
};

let _ = result.send(Reply::GetFontTemplateReply(Some(
SerializedFontTemplateInfo {
serialized_font_template,
font_key: font_template_info.font_key,
},
)));

// NB: This will load the font into memory if it hasn't been loaded already.
let _ = bytes_sender.send(&font_template_info.font_template.data());
},
Command::GetFontInstance(font_key, size, result) => {
let webrender_api = &self.webrender_api;
Expand Down Expand Up @@ -382,7 +389,7 @@ impl FontCache {
&mut self,
template_descriptor: &FontTemplateDescriptor,
family_name: &FontFamilyName,
) -> Option<Arc<FontTemplateData>> {
) -> Option<FontTemplateRef> {
let family_name = self.transform_family(family_name);

// TODO(Issue #188): look up localized font family names if canonical name not found
Expand Down Expand Up @@ -414,7 +421,7 @@ impl FontCache {
&mut self,
template_descriptor: &FontTemplateDescriptor,
family_name: &FontFamilyName,
) -> Option<Arc<FontTemplateData>> {
) -> Option<FontTemplateRef> {
let family_name = LowercaseString::from(family_name);

if self.web_families.contains_key(&family_name) {
Expand All @@ -425,25 +432,20 @@ impl FontCache {
}
}

fn get_font_template_info(&mut self, template: Arc<FontTemplateData>) -> FontTemplateInfo {
fn get_font_key_for_template(&mut self, template: &FontTemplateRef) -> FontKey {
let webrender_api = &self.webrender_api;
let webrender_fonts = &mut self.webrender_fonts;

let font_key = *webrender_fonts
.entry(template.identifier.clone())
*webrender_fonts
.entry(template.borrow().identifier.clone())
.or_insert_with(|| {
let font = match (template.bytes_if_in_memory(), template.native_font()) {
let template = template.borrow();
let font = match (template.data_if_in_memory(), template.native_font_handle()) {
(Some(bytes), _) => FontData::Raw((*bytes).clone()),
(None, Some(native_font)) => FontData::Native(native_font),
(None, None) => FontData::Raw((*template.bytes()).clone()),
(None, None) => unreachable!("Font should either be local or a web font."),
};
webrender_api.add_font(font)
});

FontTemplateInfo {
font_template: template,
font_key,
}
})
}

fn find_font_template(
Expand All @@ -462,7 +464,10 @@ impl FontCache {
self.find_font_in_local_family(template_descriptor, &family_descriptor.name)
},
}
.map(|t| self.get_font_template_info(t))
.map(|font_template| FontTemplateInfo {
font_key: self.get_font_key_for_template(&font_template),
font_template,
})
}
}

Expand Down Expand Up @@ -618,17 +623,17 @@ impl FontSource for FontCacheThread {

match reply.unwrap() {
Reply::GetFontTemplateReply(maybe_serialized_font_template_info) => {
match maybe_serialized_font_template_info {
None => None,
Some(serialized_font_template_info) => Some(FontTemplateInfo {
font_template: Arc::new(
serialized_font_template_info
.serialized_font_template
.to_font_template_data(),
),
maybe_serialized_font_template_info.map(|serialized_font_template_info| {
let font_template = Rc::new(RefCell::new(
serialized_font_template_info
.serialized_font_template
.to_font_template(),
));
FontTemplateInfo {
font_template,
font_key: serialized_font_template_info.font_key,
}),
}
}
})
},
}
}
Expand Down
5 changes: 5 additions & 0 deletions components/gfx/font_context.rs
Expand Up @@ -21,6 +21,8 @@ use crate::font::{
Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontHandleMethods, FontRef,
};
use crate::font_cache_thread::FontTemplateInfo;
#[cfg(target_os = "macos")]
use crate::font_template::FontTemplate;
use crate::font_template::FontTemplateDescriptor;
use crate::platform::font::FontHandle;

Expand Down Expand Up @@ -264,4 +266,7 @@ impl Hash for FontGroupCacheKey {
#[inline]
pub fn invalidate_font_caches() {
FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);

#[cfg(target_os = "macos")]
FontTemplate::clear_core_text_font_cache();
}

0 comments on commit 6b2fa91

Please sign in to comment.