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