diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 0dde3e1ae280..d24bd976e586 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -845,6 +845,23 @@ impl IOCompositor { .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); diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index f5aa46edc34f..124a21f6e370 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -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 { cache: CachingFontSource, web_fonts: CrossThreadFontStore, webrender_font_store: CrossThreadWebRenderFontStore, + have_removed_web_fonts: AtomicBool, } impl MallocSizeOf for FontContext { @@ -65,6 +68,7 @@ impl FontContext { 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, Vec); } impl FontContextWebFontMethods for Arc> { @@ -399,6 +405,44 @@ impl FontContextWebFontMethods for Arc (Vec, Vec) { + 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 = 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) } } diff --git a/components/gfx/font_store.rs b/components/gfx/font_store.rs index c0b52b7707ec..ff6d56abac25 100644 --- a/components/gfx/font_store.rs +++ b/components/gfx/font_store.rs @@ -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, Vec) { + ( + 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, + ) -> (Vec, Vec) { + let mut removed_keys: HashSet = 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 = 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| { + 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) + } + } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 85e665b28b01..803c3f7c8294 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -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) }, ); } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 7def12e673f4..e392a3b6f1bf 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -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); diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index e5a794fccf0c..718d53e44a19 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -191,16 +191,18 @@ pub trait WebRenderFontApi { flags: FontInstanceFlags, ) -> FontInstanceKey; fn add_font(&self, data: Arc>, 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, ); - /// 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, ); - fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; } pub enum CanvasToCompositorMsg { @@ -257,6 +258,8 @@ pub enum ScriptToCompositorMsg { GenerateImageKey(IpcSender), /// Perform a resource update operation. UpdateImages(Vec), + /// Remove the given font resources from our WebRender instance. + RemoveFonts(Vec, Vec), } /// 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, + instance_keys: Vec, + ) { + if keys.is_empty() && instance_keys.is_empty() { + return; + } + let _ = self + .0 + .send(ScriptToCompositorMsg::RemoveFonts(keys, instance_keys)); + } } #[derive(Deserialize, Serialize)]