1use std::borrow::ToOwned;
6use std::cell::OnceCell;
7use std::collections::HashMap;
8use std::thread;
9
10use app_units::Au;
11use base::generic_channel::{self, GenericReceiver};
12use base::id::PainterId;
13use fonts_traits::{
14 FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, LowercaseFontFamilyName,
15 SystemFontServiceMessage, SystemFontServiceProxySender,
16};
17use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
18use malloc_size_of_derive::MallocSizeOf;
19use paint_api::CrossProcessPaintApi;
20use profile_traits::mem::{
21 ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report,
22};
23use profile_traits::path;
24use rustc_hash::FxHashMap;
25use servo_config::pref;
26use style::values::computed::font::{GenericFontFamily, SingleFontFamily};
27use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey, FontVariation};
28
29use crate::font_store::FontStore;
30use crate::platform::font_list::{
31 default_system_generic_font_family, for_each_available_family, for_each_variation,
32};
33
34#[derive(Default, MallocSizeOf)]
35struct ResolvedGenericFontFamilies {
36 default: OnceCell<LowercaseFontFamilyName>,
37 serif: OnceCell<LowercaseFontFamilyName>,
38 sans_serif: OnceCell<LowercaseFontFamilyName>,
39 monospace: OnceCell<LowercaseFontFamilyName>,
40 fantasy: OnceCell<LowercaseFontFamilyName>,
41 cursive: OnceCell<LowercaseFontFamilyName>,
42 system_ui: OnceCell<LowercaseFontFamilyName>,
43}
44
45#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
46struct FontInstancesMapKey {
47 font_key: FontKey,
48 pt_size: Au,
49 variations: Vec<FontVariation>,
50 painter_id: PainterId,
51 flags: FontInstanceFlags,
52}
53
54#[derive(MallocSizeOf)]
58pub struct SystemFontService {
59 port: GenericReceiver<SystemFontServiceMessage>,
60 local_families: FontStore,
61 paint_api: CrossProcessPaintApi,
62 webrender_fonts: HashMap<(FontIdentifier, PainterId), FontKey>,
64 font_instances: HashMap<FontInstancesMapKey, FontInstanceKey>,
65 generic_fonts: ResolvedGenericFontFamilies,
66
67 free_font_keys: FxHashMap<PainterId, Vec<FontKey>>,
73
74 free_font_instance_keys: FxHashMap<PainterId, Vec<FontInstanceKey>>,
79}
80
81impl SystemFontService {
82 pub fn spawn(
83 paint_api: CrossProcessPaintApi,
84 memory_profiler_sender: ProfilerChan,
85 ) -> SystemFontServiceProxySender {
86 let (sender, receiver) = generic_channel::channel().unwrap();
87 let memory_reporter_sender = sender.clone();
88
89 thread::Builder::new()
90 .name("SystemFontService".to_owned())
91 .spawn(move || {
92 let mut cache = SystemFontService {
93 port: receiver,
94 local_families: Default::default(),
95 paint_api,
96 webrender_fonts: HashMap::new(),
97 font_instances: HashMap::new(),
98 generic_fonts: Default::default(),
99 free_font_keys: Default::default(),
100 free_font_instance_keys: Default::default(),
101 };
102
103 cache.refresh_local_families();
104
105 memory_profiler_sender.run_with_memory_reporting(
106 || cache.run(),
107 "system-fonts".to_owned(),
108 memory_reporter_sender,
109 SystemFontServiceMessage::CollectMemoryReport,
110 );
111 })
112 .expect("Thread spawning failed");
113
114 SystemFontServiceProxySender(sender)
115 }
116
117 fn run(&mut self) {
118 loop {
119 let msg = self.port.recv().unwrap();
120
121 let _span = profile_traits::trace_span!("SystemFontServiceMessage").entered();
122 match msg {
123 SystemFontServiceMessage::GetFontTemplates(
124 font_descriptor,
125 font_family,
126 result_sender,
127 ) => {
128 let _ =
129 result_sender.send(self.get_font_templates(font_descriptor, font_family));
130 },
131 SystemFontServiceMessage::GetFontInstance(
132 painter_id,
133 identifier,
134 pt_size,
135 flags,
136 variations,
137 result,
138 ) => {
139 let _ = result.send(
140 self.get_font_instance(painter_id, identifier, pt_size, flags, variations),
141 );
142 },
143 SystemFontServiceMessage::GetFontKey(painter_id, result_sender) => {
144 self.fetch_font_keys_if_needed(painter_id);
145
146 let _ = result_sender.send(
147 self.free_font_keys
148 .get_mut(&painter_id)
149 .expect("We just filled the keys")
150 .pop()
151 .unwrap(),
152 );
153 },
154 SystemFontServiceMessage::GetFontInstanceKey(painter_id, result_sender) => {
155 self.fetch_font_keys_if_needed(painter_id);
156 let _ = result_sender.send(
157 self.free_font_instance_keys
158 .get_mut(&painter_id)
159 .expect("We just filled the keys")
160 .pop()
161 .unwrap(),
162 );
163 },
164 SystemFontServiceMessage::PrefetchFontKeys(painter_id) => {
165 self.fetch_font_keys_if_needed(painter_id);
166 },
167 SystemFontServiceMessage::CollectMemoryReport(report_sender) => {
168 self.collect_memory_report(report_sender);
169 },
170 SystemFontServiceMessage::Ping => (),
171 SystemFontServiceMessage::Exit(result) => {
172 let _ = result.send(());
173 break;
174 },
175 }
176 }
177 }
178
179 fn collect_memory_report(&self, report_sender: ReportsChan) {
180 perform_memory_report(|ops| {
181 let reports = vec![Report {
182 path: path!["system-fonts"],
183 kind: ReportKind::ExplicitSystemHeapSize,
184 size: self.size_of(ops),
185 }];
186 report_sender.send(ProcessReports::new(reports));
187 });
188 }
189
190 #[servo_tracing::instrument(skip_all)]
191 fn fetch_font_keys_if_needed(&mut self, painter_id: PainterId) {
192 let free_font_keys = self.free_font_keys.entry(painter_id).or_default();
193 let free_font_instance_keys = self.free_font_instance_keys.entry(painter_id).or_default();
194 if !free_font_keys.is_empty() && !free_font_instance_keys.is_empty() {
195 return;
196 }
197
198 const FREE_FONT_KEYS_BATCH_SIZE: usize = 40;
199 const FREE_FONT_INSTANCE_KEYS_BATCH_SIZE: usize = 40;
200 let (mut new_font_keys, mut new_font_instance_keys) = self.paint_api.fetch_font_keys(
201 FREE_FONT_KEYS_BATCH_SIZE - free_font_keys.len(),
202 FREE_FONT_INSTANCE_KEYS_BATCH_SIZE - free_font_instance_keys.len(),
203 painter_id,
204 );
205
206 free_font_keys.append(&mut new_font_keys);
207 free_font_instance_keys.append(&mut new_font_instance_keys);
208 }
209
210 #[servo_tracing::instrument(skip_all)]
211 fn get_font_templates(
212 &mut self,
213 font_descriptor: Option<FontDescriptor>,
214 font_family: SingleFontFamily,
215 ) -> Vec<FontTemplate> {
216 let templates = self.find_font_templates(font_descriptor.as_ref(), &font_family);
217 templates
218 .into_iter()
219 .map(|template| template.borrow().clone())
220 .collect()
221 }
222
223 #[servo_tracing::instrument(skip_all)]
224 fn refresh_local_families(&mut self) {
225 self.local_families.clear();
226 for_each_available_family(|family_name| {
227 self.local_families
228 .families
229 .entry(family_name.as_str().into())
230 .or_default();
231 });
232 }
233
234 #[servo_tracing::instrument(skip_all)]
235 fn find_font_templates(
236 &mut self,
237 descriptor_to_match: Option<&FontDescriptor>,
238 family: &SingleFontFamily,
239 ) -> Vec<FontTemplateRef> {
240 let family_name = self.family_name_for_single_font_family(family);
243 self.local_families
244 .families
245 .get_mut(&family_name)
246 .map(|font_templates| {
247 if font_templates.templates.is_empty() {
248 for_each_variation(&family_name, |font_template| {
249 font_templates.add_template(font_template);
250 });
251 }
252
253 font_templates.find_for_descriptor(descriptor_to_match)
254 })
255 .unwrap_or_default()
256 }
257
258 #[servo_tracing::instrument(skip_all)]
259 fn get_font_instance(
260 &mut self,
261 painter_id: PainterId,
262 identifier: FontIdentifier,
263 pt_size: Au,
264 flags: FontInstanceFlags,
265 variations: Vec<FontVariation>,
266 ) -> FontInstanceKey {
267 self.fetch_font_keys_if_needed(painter_id);
268
269 let paint_api = &self.paint_api;
270 let webrender_fonts = &mut self.webrender_fonts;
271
272 let font_key = *webrender_fonts
273 .entry((identifier.clone(), painter_id))
274 .or_insert_with(|| {
275 let font_key = self
276 .free_font_keys
277 .get_mut(&painter_id)
278 .expect("We just filled the keys")
279 .pop()
280 .unwrap();
281 let FontIdentifier::Local(local_font_identifier) = identifier else {
282 unreachable!("Should never have a web font in the system font service");
283 };
284 paint_api.add_system_font(font_key, local_font_identifier.native_font_handle());
285 font_key
286 });
287
288 let entry_key = FontInstancesMapKey {
289 font_key,
290 pt_size,
291 variations: variations.clone(),
292 painter_id,
293 flags,
294 };
295 *self.font_instances.entry(entry_key).or_insert_with(|| {
296 let font_instance_key = self
297 .free_font_instance_keys
298 .get_mut(&painter_id)
299 .expect("We just filled the keys")
300 .pop()
301 .unwrap();
302 paint_api.add_font_instance(
303 font_instance_key,
304 font_key,
305 pt_size.to_f32_px(),
306 flags,
307 variations,
308 );
309 font_instance_key
310 })
311 }
312
313 pub(crate) fn family_name_for_single_font_family(
314 &mut self,
315 family: &SingleFontFamily,
316 ) -> LowercaseFontFamilyName {
317 let generic = match family {
318 SingleFontFamily::FamilyName(family_name) => return family_name.name.clone().into(),
319 SingleFontFamily::Generic(generic) => generic,
320 };
321
322 let resolved_font = match generic {
323 GenericFontFamily::None => &self.generic_fonts.default,
324 GenericFontFamily::Serif => &self.generic_fonts.serif,
325 GenericFontFamily::SansSerif => &self.generic_fonts.sans_serif,
326 GenericFontFamily::Monospace => &self.generic_fonts.monospace,
327 GenericFontFamily::Cursive => &self.generic_fonts.cursive,
328 GenericFontFamily::Fantasy => &self.generic_fonts.fantasy,
329 GenericFontFamily::SystemUi => &self.generic_fonts.system_ui,
330 };
331
332 resolved_font
333 .get_or_init(|| {
334 let family_name = match generic {
336 GenericFontFamily::None => pref!(fonts_default),
337 GenericFontFamily::Serif => pref!(fonts_serif),
338 GenericFontFamily::SansSerif => pref!(fonts_sans_serif),
339 GenericFontFamily::Monospace => pref!(fonts_monospace),
340 _ => String::new(),
341 };
342
343 if !family_name.is_empty() {
344 return family_name.into();
345 }
346
347 default_system_generic_font_family(*generic)
349 })
350 .clone()
351 }
352}