mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
fonts: Clean up WebRender web fonts when they are no longer used (#32545)
This is the first part of cleaning up unused WebRender resources. Currently this only cleans up web font resources, but a more full-featured implementation in the future could also clean up unused system fonts. Fixes #32345. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
bd15a4fbd8
commit
fef1337da0
6 changed files with 181 additions and 7 deletions
|
@ -845,6 +845,23 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
|||
.send_transaction(self.webrender_document, txn);
|
||||
},
|
||||
|
||||
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts(
|
||||
keys,
|
||||
instance_keys,
|
||||
)) => {
|
||||
let mut transaction = Transaction::new();
|
||||
|
||||
for instance in instance_keys.into_iter() {
|
||||
transaction.delete_font_instance(instance);
|
||||
}
|
||||
for key in keys.into_iter() {
|
||||
transaction.delete_font(key);
|
||||
}
|
||||
|
||||
self.webrender_api
|
||||
.send_transaction(self.webrender_document, transaction);
|
||||
},
|
||||
|
||||
ForwardedToCompositorMsg::Net(NetToCompositorMsg::AddImage(key, desc, data)) => {
|
||||
let mut txn = Transaction::new();
|
||||
txn.add_image(key, desc, data, None);
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
* 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::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::default::Default;
|
||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use app_units::Au;
|
||||
|
@ -26,6 +27,7 @@ use style::shared_lock::SharedRwLockReadGuard;
|
|||
use style::stylesheets::{DocumentStyleSheet, StylesheetInDocument};
|
||||
use style::Atom;
|
||||
use url::Url;
|
||||
use webrender_api::{FontInstanceKey, FontKey};
|
||||
|
||||
use crate::font::{
|
||||
Font, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontGroup, FontRef, FontSearchScope,
|
||||
|
@ -48,6 +50,7 @@ pub struct FontContext<S: FontSource> {
|
|||
cache: CachingFontSource<S>,
|
||||
web_fonts: CrossThreadFontStore,
|
||||
webrender_font_store: CrossThreadWebRenderFontStore,
|
||||
have_removed_web_fonts: AtomicBool,
|
||||
}
|
||||
|
||||
impl<S: FontSource> MallocSizeOf for FontContext<S> {
|
||||
|
@ -65,6 +68,7 @@ impl<S: FontSource> FontContext<S> {
|
|||
cache: CachingFontSource::new(font_source),
|
||||
web_fonts: Arc::new(RwLock::default()),
|
||||
webrender_font_store: Arc::new(RwLock::default()),
|
||||
have_removed_web_fonts: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,6 +248,8 @@ pub trait FontContextWebFontMethods {
|
|||
) -> usize;
|
||||
fn process_next_web_font_source(&self, web_font_download_state: WebFontDownloadState);
|
||||
fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet);
|
||||
fn collect_unused_webrender_resources(&self, all: bool)
|
||||
-> (Vec<FontKey>, Vec<FontInstanceKey>);
|
||||
}
|
||||
|
||||
impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontContext<S>> {
|
||||
|
@ -399,6 +405,44 @@ impl<S: FontSource + Send + 'static> FontContextWebFontMethods for Arc<FontConte
|
|||
// Removing this stylesheet modified the available fonts, so invalidate the cache
|
||||
// of resolved font groups.
|
||||
font_groups.clear();
|
||||
|
||||
// Ensure that we clean up any WebRender resources on the next display list update.
|
||||
self.have_removed_web_fonts.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn collect_unused_webrender_resources(
|
||||
&self,
|
||||
all: bool,
|
||||
) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
|
||||
if all {
|
||||
let mut webrender_font_store = self.webrender_font_store.write();
|
||||
self.have_removed_web_fonts.store(false, Ordering::Relaxed);
|
||||
return webrender_font_store.remove_all_fonts();
|
||||
}
|
||||
|
||||
if !self.have_removed_web_fonts.load(Ordering::Relaxed) {
|
||||
return (Vec::new(), Vec::new());
|
||||
}
|
||||
|
||||
// Lock everything to prevent adding new fonts while we are cleaning up the old ones.
|
||||
let web_fonts = self.web_fonts.write();
|
||||
let _fonts = self.cache.fonts.write();
|
||||
let _font_groups = self.cache.resolved_font_groups.write();
|
||||
let mut webrender_font_store = self.webrender_font_store.write();
|
||||
|
||||
let mut unused_identifiers: HashSet<FontIdentifier> = webrender_font_store
|
||||
.webrender_font_key_map
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect();
|
||||
for templates in web_fonts.families.values() {
|
||||
templates.for_all_identifiers(|identifier| {
|
||||
unused_identifiers.remove(identifier);
|
||||
});
|
||||
}
|
||||
|
||||
self.have_removed_web_fonts.store(false, Ordering::Relaxed);
|
||||
webrender_font_store.remove_all_fonts_for_identifiers(unused_identifiers)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use app_units::Au;
|
||||
|
@ -132,6 +132,50 @@ impl WebRenderFontStore {
|
|||
font_cache_thread.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
|
||||
|
@ -182,6 +226,18 @@ impl SimpleFamily {
|
|||
remove_if_template_matches(&mut self.italic);
|
||||
remove_if_template_matches(&mut self.bold_italic);
|
||||
}
|
||||
|
||||
pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
|
||||
let mut call_if_not_none = |template: &Option<FontTemplateRef>| {
|
||||
if let Some(template) = template {
|
||||
callback(&template.identifier())
|
||||
}
|
||||
};
|
||||
call_if_not_none(&self.regular);
|
||||
call_if_not_none(&self.bold);
|
||||
call_if_not_none(&self.italic);
|
||||
call_if_not_none(&self.bold_italic);
|
||||
}
|
||||
}
|
||||
/// A list of font templates that make up a given font family.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -328,4 +384,13 @@ impl FontTemplates {
|
|||
|
||||
length_before != self.templates.len()
|
||||
}
|
||||
|
||||
pub(crate) fn for_all_identifiers(&self, mut callback: impl FnMut(&FontIdentifier)) {
|
||||
for template in self.templates.iter() {
|
||||
callback(&template.borrow().identifier);
|
||||
}
|
||||
if let Some(ref simple_family) = self.simple_family {
|
||||
simple_family.for_all_identifiers(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,6 +242,16 @@ impl Drop for ScriptReflowResult {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for LayoutThread {
|
||||
fn drop(&mut self) {
|
||||
let (keys, instance_keys) = self
|
||||
.font_context
|
||||
.collect_unused_webrender_resources(true /* all */);
|
||||
self.webrender_api
|
||||
.remove_unused_font_resources(keys, instance_keys)
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for LayoutThread {
|
||||
fn device(&self) -> &Device {
|
||||
self.stylist.device()
|
||||
|
@ -921,6 +931,12 @@ impl LayoutThread {
|
|||
|
||||
self.webrender_api
|
||||
.send_display_list(compositor_info, builder.end().1);
|
||||
|
||||
let (keys, instance_keys) = self
|
||||
.font_context
|
||||
.collect_unused_webrender_resources(false /* all */);
|
||||
self.webrender_api
|
||||
.remove_unused_font_resources(keys, instance_keys)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -219,6 +219,16 @@ impl Drop for ScriptReflowResult {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for LayoutThread {
|
||||
fn drop(&mut self) {
|
||||
let (keys, instance_keys) = self
|
||||
.font_context
|
||||
.collect_unused_webrender_resources(true /* all */);
|
||||
self.webrender_api
|
||||
.remove_unused_font_resources(keys, instance_keys)
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for LayoutThread {
|
||||
fn device(&self) -> &Device {
|
||||
self.stylist.device()
|
||||
|
@ -912,6 +922,12 @@ impl LayoutThread {
|
|||
if reflow_goal.needs_display() {
|
||||
self.webrender_api
|
||||
.send_display_list(display_list.compositor_info, display_list.wr.end().1);
|
||||
|
||||
let (keys, instance_keys) = self
|
||||
.font_context
|
||||
.collect_unused_webrender_resources(false /* all */);
|
||||
self.webrender_api
|
||||
.remove_unused_font_resources(keys, instance_keys)
|
||||
}
|
||||
|
||||
self.update_iframe_sizes(iframe_sizes);
|
||||
|
|
|
@ -191,16 +191,18 @@ pub trait WebRenderFontApi {
|
|||
flags: FontInstanceFlags,
|
||||
) -> FontInstanceKey;
|
||||
fn add_font(&self, data: Arc<Vec<u8>>, index: u32) -> FontKey;
|
||||
/// Forward an already prepared `AddFont` message, sending it on to the compositor. This is used
|
||||
/// to get WebRender [`FontKey`]s for web fonts in the per-layout `FontContext`.
|
||||
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey;
|
||||
|
||||
/// Forward a `AddFont` message, sending it on to the compositor. This is used to get WebRender
|
||||
/// [`FontKey`]s for web fonts in the per-layout `FontContext`.
|
||||
fn forward_add_font_message(
|
||||
&self,
|
||||
bytes_receiver: IpcBytesReceiver,
|
||||
font_index: u32,
|
||||
result_sender: IpcSender<FontKey>,
|
||||
);
|
||||
/// Forward an already prepared `AddFontInstance` message, sending it on to the compositor. This
|
||||
/// is used to get WebRender [`FontInstanceKey`]s for web fonts in the per-layout `FontContext`.
|
||||
/// Forward a `AddFontInstance` message, sending it on to the compositor. This is used to get
|
||||
/// WebRender [`FontInstanceKey`]s for web fonts in the per-layout `FontContext`.
|
||||
fn forward_add_font_instance_message(
|
||||
&self,
|
||||
font_key: FontKey,
|
||||
|
@ -208,7 +210,6 @@ pub trait WebRenderFontApi {
|
|||
flags: FontInstanceFlags,
|
||||
result_receiver: IpcSender<FontInstanceKey>,
|
||||
);
|
||||
fn add_system_font(&self, handle: NativeFontHandle) -> FontKey;
|
||||
}
|
||||
|
||||
pub enum CanvasToCompositorMsg {
|
||||
|
@ -257,6 +258,8 @@ pub enum ScriptToCompositorMsg {
|
|||
GenerateImageKey(IpcSender<ImageKey>),
|
||||
/// Perform a resource update operation.
|
||||
UpdateImages(Vec<SerializedImageUpdate>),
|
||||
/// Remove the given font resources from our WebRender instance.
|
||||
RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
|
||||
}
|
||||
|
||||
/// A mechanism to send messages from networking to the WebRender instance.
|
||||
|
@ -420,6 +423,19 @@ impl WebRenderScriptApi {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn remove_unused_font_resources(
|
||||
&self,
|
||||
keys: Vec<FontKey>,
|
||||
instance_keys: Vec<FontInstanceKey>,
|
||||
) {
|
||||
if keys.is_empty() && instance_keys.is_empty() {
|
||||
return;
|
||||
}
|
||||
let _ = self
|
||||
.0
|
||||
.send(ScriptToCompositorMsg::RemoveFonts(keys, instance_keys));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue