1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #![deny(missing_docs)]
5 
6 //! Provides the webrender-side implementation of gecko blob images.
7 //!
8 //! Pretty much this is just a shim that calls back into Moz2DImageRenderer, but
9 //! it also handles merging "partial" blob images (see `merge_blob_images`) and
10 //! registering fonts found in the blob (see `prepare_request`).
11 
12 use bindings::{
13     gecko_profiler_end_marker, gecko_profiler_start_marker, wr_moz2d_render_cb, ArcVecU8, ByteSlice, MutByteSlice,
14 };
15 use rayon::prelude::*;
16 use rayon::ThreadPool;
17 use webrender::api::units::{BlobDirtyRect, BlobToDeviceTranslation, DeviceIntRect};
18 use webrender::api::*;
19 
20 use euclid::{Box2D, Rect};
21 use std;
22 use std::collections::btree_map::BTreeMap;
23 use std::collections::hash_map;
24 use std::collections::hash_map::HashMap;
25 use std::collections::Bound::Included;
26 use std::i32;
27 use std::mem;
28 use std::os::raw::{c_char, c_void};
29 use std::ptr;
30 use std::sync::Arc;
31 
32 #[cfg(target_os = "windows")]
33 use dwrote;
34 
35 #[cfg(target_os = "macos")]
36 use foreign_types::ForeignType;
37 
38 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
39 use std::ffi::CString;
40 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
41 use std::os::unix::ffi::OsStrExt;
42 
43 /// Local print-debugging utility
44 macro_rules! dlog {
45     ($($e:expr),*) => { {$(let _ = $e;)*} }
46     //($($t:tt)*) => { println!($($t)*) }
47 }
48 
49 /// Debug prints a blob's item bounds, indicating whether the bounds are dirty or not.
dump_bounds(blob: &[u8], dirty_rect: Box2d)50 fn dump_bounds(blob: &[u8], dirty_rect: Box2d) {
51     let mut index = BlobReader::new(blob);
52     while index.reader.has_more() {
53         let e = index.read_entry();
54         dlog!(
55             "  {:?} {}",
56             e.bounds,
57             if e.bounds.contained_by(&dirty_rect) { "*" } else { "" }
58         );
59     }
60 }
61 
62 /// Debug prints a blob's metadata.
dump_index(blob: &[u8])63 fn dump_index(blob: &[u8]) {
64     let mut index = BlobReader::new(blob);
65     // we might get an empty result here because sub groups are not tightly bound
66     // and we'll sometimes have display items that end up with empty bounds in
67     // the blob image.
68     while index.reader.has_more() {
69         let e = index.read_entry();
70         dlog!("result bounds: {} {} {:?}", e.end, e.extra_end, e.bounds);
71     }
72 }
73 
74 /// Handles the interpretation and rasterization of gecko-based (moz2d) WR blob images.
75 pub struct Moz2dBlobImageHandler {
76     workers: Arc<ThreadPool>,
77     workers_low_priority: Arc<ThreadPool>,
78     blob_commands: HashMap<BlobImageKey, BlobCommand>,
79     enable_multithreading: bool,
80 }
81 
82 /// Transmute some bytes into a value.
83 ///
84 /// FIXME: kill this with fire and/or do a super robust security audit
convert_from_bytes<T: Copy>(slice: &[u8]) -> T85 unsafe fn convert_from_bytes<T: Copy>(slice: &[u8]) -> T {
86     assert!(mem::size_of::<T>() <= slice.len());
87     ptr::read_unaligned(slice.as_ptr() as *const T)
88 }
89 
90 /// Transmute a value into some bytes.
convert_to_bytes<T>(x: &T) -> &[u8]91 fn convert_to_bytes<T>(x: &T) -> &[u8] {
92     unsafe {
93         let ip: *const T = x;
94         let bp: *const u8 = ip as *const _;
95         ::std::slice::from_raw_parts(bp, mem::size_of::<T>())
96     }
97 }
98 
99 /// A simple helper for deserializing a bunch of transmuted POD data from bytes.
100 struct BufReader<'a> {
101     /// The buffer to read from.
102     buf: &'a [u8],
103     /// Where we currently are reading from.
104     pos: usize,
105 }
106 
107 impl<'a> BufReader<'a> {
108     /// Creates a reader over the given input.
new(buf: &'a [u8]) -> BufReader<'a>109     fn new(buf: &'a [u8]) -> BufReader<'a> {
110         BufReader { buf, pos: 0 }
111     }
112 
113     /// Transmute-deserializes a value of type T from the stream.
114     ///
115     /// !!! SUPER DANGEROUS !!!
116     ///
117     /// To limit the scope of this unsafety, please don't call this directly.
118     /// Make a helper method for each whitelisted type.
read<T: Copy>(&mut self) -> T119     unsafe fn read<T: Copy>(&mut self) -> T {
120         let ret = convert_from_bytes(&self.buf[self.pos..]);
121         self.pos += mem::size_of::<T>();
122         ret
123     }
124 
125     /// Deserializes a BlobFont.
read_blob_font(&mut self) -> BlobFont126     fn read_blob_font(&mut self) -> BlobFont {
127         unsafe { self.read::<BlobFont>() }
128     }
129 
130     /// Deserializes a usize.
read_usize(&mut self) -> usize131     fn read_usize(&mut self) -> usize {
132         unsafe { self.read::<usize>() }
133     }
134 
135     /// Deserializes a Box2d.
read_box(&mut self) -> Box2d136     fn read_box(&mut self) -> Box2d {
137         unsafe { self.read::<Box2d>() }
138     }
139 
140     /// Returns whether the buffer has more data to deserialize.
has_more(&self) -> bool141     fn has_more(&self) -> bool {
142         self.pos < self.buf.len()
143     }
144 }
145 
146 /// Reads the metadata of a blob image.
147 ///
148 /// Blob stream format:
149 /// { data[..], index[..], offset in the stream of the index array }
150 ///
151 /// An 'item' has 'data' and 'extra_data'
152 ///  - In our case the 'data' is the stream produced by DrawTargetRecording
153 ///    and the 'extra_data' includes things like webrender font keys
154 ///
155 /// The index is an array of entries of the following form:
156 /// { end, extra_end, bounds }
157 ///
158 /// - end is the offset of the end of an item's data
159 ///   an item's data goes from the begining of the stream or
160 ///   the begining of the last item til end
161 /// - extra_end is the offset of the end of an item's extra data
162 ///   an item's extra data goes from 'end' until 'extra_end'
163 /// - bounds is a set of 4 ints {x1, y1, x2, y2 }
164 ///
165 /// The offsets in the index should be monotonically increasing.
166 ///
167 /// Design rationale:
168 ///  - the index is smaller so we append it to the end of the data array
169 ///  during construction. This makes it more likely that we'll fit inside
170 ///  the data Vec
171 ///  - we use indices/offsets instead of sizes to avoid having to deal with any
172 ///  arithmetic that might overflow.
173 struct BlobReader<'a> {
174     /// The buffer of the blob.
175     reader: BufReader<'a>,
176     /// Where the buffer head is.
177     begin: usize,
178 }
179 
180 #[derive(PartialEq, Debug, Eq, Clone, Copy)]
181 struct IntPoint {
182     x: i32,
183     y: i32,
184 }
185 
186 /// The metadata for each display item in a blob image (doesn't match the serialized layout).
187 ///
188 /// See BlobReader above for detailed docs of the blob image format.
189 struct Entry {
190     /// The bounds of the display item.
191     bounds: Box2d,
192     /// Where the item's recorded drawing commands start.
193     begin: usize,
194     /// Where the item's recorded drawing commands end, and its extra data starts.
195     end: usize,
196     /// Where the item's extra data ends, and the next item's `begin`.
197     extra_end: usize,
198 }
199 
200 impl<'a> BlobReader<'a> {
201     /// Creates a new BlobReader for the given buffer.
new(buf: &'a [u8]) -> BlobReader<'a>202     fn new(buf: &'a [u8]) -> BlobReader<'a> {
203         // The offset of the index is at the end of the buffer.
204         let index_offset_pos = buf.len() - mem::size_of::<usize>();
205         assert!(index_offset_pos < buf.len());
206         let index_offset = unsafe { convert_from_bytes::<usize>(&buf[index_offset_pos..]) };
207 
208         BlobReader {
209             reader: BufReader::new(&buf[index_offset..index_offset_pos]),
210             begin: 0,
211         }
212     }
213 
214     /// Reads the next display item's metadata.
read_entry(&mut self) -> Entry215     fn read_entry(&mut self) -> Entry {
216         let end = self.reader.read_usize();
217         let extra_end = self.reader.read_usize();
218         let bounds = self.reader.read_box();
219         let ret = Entry {
220             begin: self.begin,
221             end,
222             extra_end,
223             bounds,
224         };
225         self.begin = extra_end;
226         ret
227     }
228 }
229 
230 /// Writes new blob images.
231 ///
232 /// In our case this is the result of merging an old one and a new one
233 struct BlobWriter {
234     /// The buffer that the data and extra data for the items is accumulated.
235     data: Vec<u8>,
236     /// The buffer that the metadata for the items is accumulated.
237     index: Vec<u8>,
238 }
239 
240 impl BlobWriter {
241     /// Creates an empty BlobWriter.
new() -> BlobWriter242     fn new() -> BlobWriter {
243         BlobWriter {
244             data: Vec::new(),
245             index: Vec::new(),
246         }
247     }
248 
249     /// Writes a display item to the blob.
new_entry(&mut self, extra_size: usize, bounds: Box2d, data: &[u8])250     fn new_entry(&mut self, extra_size: usize, bounds: Box2d, data: &[u8]) {
251         self.data.extend_from_slice(data);
252         // Write 'end' to the index: the offset where the regular data ends and the extra data starts.
253         self.index
254             .extend_from_slice(convert_to_bytes(&(self.data.len() - extra_size)));
255         // Write 'extra_end' to the index: the offset where the extra data ends.
256         self.index.extend_from_slice(convert_to_bytes(&self.data.len()));
257         // XXX: we can aggregate these writes
258         // Write the bounds to the index.
259         self.index.extend_from_slice(convert_to_bytes(&bounds.x1));
260         self.index.extend_from_slice(convert_to_bytes(&bounds.y1));
261         self.index.extend_from_slice(convert_to_bytes(&bounds.x2));
262         self.index.extend_from_slice(convert_to_bytes(&bounds.y2));
263     }
264 
265     /// Completes the blob image, producing a single buffer containing it.
finish(mut self) -> Vec<u8>266     fn finish(mut self) -> Vec<u8> {
267         // Append the index to the end of the buffer
268         // and then append the offset to the beginning of the index.
269         let index_begin = self.data.len();
270         self.data.extend_from_slice(&self.index);
271         self.data.extend_from_slice(convert_to_bytes(&index_begin));
272         self.data
273     }
274 }
275 
276 // TODO: replace with euclid::Box2D
277 #[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
278 #[repr(C)]
279 /// A two-points representation of a rectangle.
280 struct Box2d {
281     /// Top-left x
282     x1: i32,
283     /// Top-left y
284     y1: i32,
285     /// Bottom-right x
286     x2: i32,
287     /// Bottom-right y
288     y2: i32,
289 }
290 
291 impl Box2d {
292     /// Returns whether `self` is contained by `other` (inclusive).
293     /// an empty self is contained in every rect
contained_by(&self, other: &Box2d) -> bool294     fn contained_by(&self, other: &Box2d) -> bool {
295         self.is_empty() || (self.x1 >= other.x1 && self.x2 <= other.x2 && self.y1 >= other.y1 && self.y2 <= other.y2)
296     }
297 
intersection(&self, other: &Box2d) -> Box2d298     fn intersection(&self, other: &Box2d) -> Box2d {
299         let result = Box2d {
300             x1: self.x1.max(other.x1),
301             y1: self.y1.max(other.y1),
302             x2: self.x2.min(other.x2),
303             y2: self.y2.min(other.y2),
304         };
305         if self.is_empty() || other.is_empty() {
306             assert!(result.is_empty());
307         }
308         result
309     }
310 
is_empty(&self) -> bool311     fn is_empty(&self) -> bool {
312         self.x2 <= self.x1 || self.y2 <= self.y1
313     }
314 }
315 
316 impl<T> From<Rect<i32, T>> for Box2d {
from(rect: Rect<i32, T>) -> Box2d317     fn from(rect: Rect<i32, T>) -> Box2d {
318         (&rect).into()
319     }
320 }
321 
322 impl<T> From<&Rect<i32, T>> for Box2d {
from(rect: &Rect<i32, T>) -> Box2d323     fn from(rect: &Rect<i32, T>) -> Box2d {
324         Box2d {
325             x1: rect.min_x(),
326             y1: rect.min_y(),
327             x2: rect.max_x(),
328             y2: rect.max_y(),
329         }
330     }
331 }
332 
333 impl<T> From<Box2D<i32, T>> for Box2d {
from(rect: Box2D<i32, T>) -> Box2d334     fn from(rect: Box2D<i32, T>) -> Box2d {
335         (&rect).into()
336     }
337 }
338 
339 impl<T> From<&Box2D<i32, T>> for Box2d {
from(rect: &Box2D<i32, T>) -> Box2d340     fn from(rect: &Box2D<i32, T>) -> Box2d {
341         Box2d {
342             x1: rect.min.x,
343             y1: rect.min.y,
344             x2: rect.max.x,
345             y2: rect.max.y,
346         }
347     }
348 }
349 
350 /// Provides an API for looking up the display items in a blob image by bounds, yielding items
351 /// with equal bounds in their original relative ordering.
352 ///
353 /// This is used to implement `merge_blobs_images`.
354 ///
355 /// We use a BTree as a kind of multi-map, by appending an integer "cache_order" to the key.
356 /// This lets us use multiple items with matching bounds in the map and allows
357 /// us to fetch and remove them while retaining the ordering of the original list.
358 struct CachedReader<'a> {
359     /// Wrapped reader.
360     reader: BlobReader<'a>,
361     /// Cached entries that have been read but not yet requested by our consumer.
362     cache: BTreeMap<(Box2d, u32), Entry>,
363     /// The current number of internally read display items, used to preserve list order.
364     cache_index_counter: u32,
365 }
366 
367 impl<'a> CachedReader<'a> {
368     /// Creates a new CachedReader.
new(buf: &'a [u8]) -> CachedReader369     pub fn new(buf: &'a [u8]) -> CachedReader {
370         CachedReader {
371             reader: BlobReader::new(buf),
372             cache: BTreeMap::new(),
373             cache_index_counter: 0,
374         }
375     }
376 
377     /// Tries to find the given bounds in the cache of internally read items, removing it if found.
take_entry_with_bounds_from_cache(&mut self, bounds: &Box2d) -> Option<Entry>378     fn take_entry_with_bounds_from_cache(&mut self, bounds: &Box2d) -> Option<Entry> {
379         if self.cache.is_empty() {
380             return None;
381         }
382 
383         let key_to_delete = match self
384             .cache
385             .range((Included((*bounds, 0u32)), Included((*bounds, std::u32::MAX))))
386             .next()
387         {
388             Some((&key, _)) => key,
389             None => return None,
390         };
391 
392         Some(
393             self.cache
394                 .remove(&key_to_delete)
395                 .expect("We just got this key from range, it needs to be present"),
396         )
397     }
398 
399     /// Yields the next item in the blob image with the given bounds.
400     ///
401     /// If the given bounds aren't found in the blob, this panics. `merge_blob_images` should
402     /// avoid this by construction if the blob images are well-formed.
next_entry_with_bounds(&mut self, bounds: &Box2d, ignore_rect: &Box2d) -> Entry403     pub fn next_entry_with_bounds(&mut self, bounds: &Box2d, ignore_rect: &Box2d) -> Entry {
404         if let Some(entry) = self.take_entry_with_bounds_from_cache(bounds) {
405             return entry;
406         }
407 
408         loop {
409             // This will panic if we run through the whole list without finding our bounds.
410             let old = self.reader.read_entry();
411             if old.bounds == *bounds {
412                 return old;
413             } else if !old.bounds.contained_by(&ignore_rect) {
414                 self.cache.insert((old.bounds, self.cache_index_counter), old);
415                 self.cache_index_counter += 1;
416             }
417         }
418     }
419 }
420 
421 /// Merges a new partial blob image into an existing complete one.
422 ///
423 /// A blob image represents a recording of the drawing commands needed to render
424 /// (part of) a display list. A partial blob image is a diff between the old display
425 /// list and a new one. It contains an entry for every display item in the new list, but
426 /// the actual drawing commands are missing for any item that isn't strictly contained
427 /// in the dirty rect. This is possible because not being contained in the dirty
428 /// rect implies that the item is unchanged between the old and new list, so we can
429 /// just grab the drawing commands from the old list.
430 ///
431 /// The dirty rect strictly contains the bounds of every item that has been inserted
432 /// into or deleted from the old list to create the new list. (For simplicity
433 /// you may think of any other update as deleting and reinserting the item).
434 ///
435 /// Partial blobs are based on gecko's "retained display list" system, and
436 /// in particular rely on one key property: if two items have overlapping bounds
437 /// and *aren't* contained in the dirty rect, then their relative order in both
438 /// the old and new list will not change. This lets us uniquely identify a display
439 /// item using only its bounds and relative order in the list.
440 ///
441 /// That is, the first non-dirty item in the new list with bounds (10, 15, 100, 100)
442 /// is *also* the first non-dirty item in the old list with those bounds.
443 ///
444 /// Note that *every* item contained inside the dirty rect will be fully recorded in
445 /// the new list, even if it is actually unchanged from the old list.
446 ///
447 /// All of this together gives us a fairly simple merging algorithm: all we need
448 /// to do is walk through the new (partial) list, determine which of the two lists
449 /// has the recording for that item, and copy the recording into the result.
450 ///
451 /// If an item is contained in the dirty rect, then the new list contains the
452 /// correct recording for that item, so we always copy it from there. Otherwise, we find
453 /// the first not-yet-copied item with those bounds in the old list and copy that.
454 /// Any items found in the old list but not the new one can be safely assumed to
455 /// have been deleted.
merge_blob_images( old_buf: &[u8], new_buf: &[u8], dirty_rect: Box2d, old_visible_rect: Box2d, new_visible_rect: Box2d, ) -> Vec<u8>456 fn merge_blob_images(
457     old_buf: &[u8],
458     new_buf: &[u8],
459     dirty_rect: Box2d,
460     old_visible_rect: Box2d,
461     new_visible_rect: Box2d,
462 ) -> Vec<u8> {
463     let mut result = BlobWriter::new();
464     dlog!("dirty rect: {:?}", dirty_rect);
465     dlog!("old:");
466     dump_bounds(old_buf, dirty_rect);
467     dlog!("new:");
468     dump_bounds(new_buf, dirty_rect);
469     dlog!("old visibile rect: {:?}", old_visible_rect);
470     dlog!("new visibile rect: {:?}", new_visible_rect);
471 
472     let mut old_reader = CachedReader::new(old_buf);
473     let mut new_reader = BlobReader::new(new_buf);
474     let preserved_rect = old_visible_rect.intersection(&new_visible_rect);
475 
476     // Loop over both new and old entries merging them.
477     // Both new and old must have the same number of entries that
478     // overlap but are not contained by the dirty rect, and they
479     // must be in the same order.
480     while new_reader.reader.has_more() {
481         let new = new_reader.read_entry();
482         dlog!("bounds: {} {} {:?}", new.end, new.extra_end, new.bounds);
483         let preserved_bounds = new.bounds.intersection(&preserved_rect);
484         if preserved_bounds.contained_by(&dirty_rect) {
485             result.new_entry(new.extra_end - new.end, new.bounds, &new_buf[new.begin..new.extra_end]);
486         } else {
487             let old = old_reader.next_entry_with_bounds(&new.bounds, &dirty_rect);
488             result.new_entry(old.extra_end - old.end, new.bounds, &old_buf[old.begin..old.extra_end])
489         }
490     }
491 
492     // XXX: future work: ensure that items that have been deleted but aren't in the blob's visible
493     // rect don't affect the dirty rect -- this allows us to scroll content out of view while only
494     // updating the areas where items have been scrolled *into* view. This is very important for
495     // the performance of blobs that are larger than the viewport. When this is done this
496     // assertion will need to be modified to factor in the visible rect, or removed.
497 
498     // Ensure all remaining items will be discarded
499     while old_reader.reader.reader.has_more() {
500         let old = old_reader.reader.read_entry();
501         dlog!("new bounds: {} {} {:?}", old.end, old.extra_end, old.bounds);
502         //assert!(old.bounds.contained_by(&dirty_rect));
503     }
504 
505     //assert!(old_reader.cache.is_empty());
506 
507     let result = result.finish();
508     dump_index(&result);
509     result
510 }
511 
512 /// A font used by a blob image.
513 #[repr(C)]
514 #[derive(Copy, Clone)]
515 struct BlobFont {
516     /// The font key.
517     font_instance_key: FontInstanceKey,
518     /// A pointer to the scaled font.
519     scaled_font_ptr: u64,
520 }
521 
522 /// A blob image and extra data provided by webrender on how to rasterize it.
523 #[derive(Clone)]
524 struct BlobCommand {
525     /// The blob.
526     data: Arc<BlobImageData>,
527     /// What part of the blob should be rasterized (visible_rect's top-left corresponds to
528     /// (0,0) in the blob's rasterization)
529     visible_rect: DeviceIntRect,
530     /// The size of the tiles to use in rasterization.
531     tile_size: TileSize,
532 }
533 
534 struct Job {
535     request: BlobImageRequest,
536     descriptor: BlobImageDescriptor,
537     commands: Arc<BlobImageData>,
538     dirty_rect: BlobDirtyRect,
539     visible_rect: DeviceIntRect,
540     tile_size: TileSize,
541 }
542 
543 /// Rasterizes gecko blob images.
544 struct Moz2dBlobRasterizer {
545     /// Pool of rasterizers.
546     workers: Arc<ThreadPool>,
547     /// Pool of low priority rasterizers.
548     workers_low_priority: Arc<ThreadPool>,
549     /// Blobs to rasterize.
550     blob_commands: HashMap<BlobImageKey, BlobCommand>,
551     ///
552     enable_multithreading: bool,
553 }
554 
555 struct GeckoProfilerMarker {
556     name: &'static [u8],
557 }
558 
559 impl GeckoProfilerMarker {
new(name: &'static [u8]) -> GeckoProfilerMarker560     pub fn new(name: &'static [u8]) -> GeckoProfilerMarker {
561         unsafe {
562             gecko_profiler_start_marker(name.as_ptr() as *const c_char);
563         }
564         GeckoProfilerMarker { name }
565     }
566 }
567 
568 impl Drop for GeckoProfilerMarker {
drop(&mut self)569     fn drop(&mut self) {
570         unsafe {
571             gecko_profiler_end_marker(self.name.as_ptr() as *const c_char);
572         }
573     }
574 }
575 
576 impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
rasterize( &mut self, requests: &[BlobImageParams], low_priority: bool, ) -> Vec<(BlobImageRequest, BlobImageResult)>577     fn rasterize(
578         &mut self,
579         requests: &[BlobImageParams],
580         low_priority: bool,
581     ) -> Vec<(BlobImageRequest, BlobImageResult)> {
582         // All we do here is spin up our workers to callback into gecko to replay the drawing commands.
583         let _marker = GeckoProfilerMarker::new(b"BlobRasterization\0");
584 
585         let requests: Vec<Job> = requests
586             .iter()
587             .map(|params| {
588                 let command = &self.blob_commands[&params.request.key];
589                 let blob = Arc::clone(&command.data);
590                 assert!(!params.descriptor.rect.is_empty());
591 
592                 Job {
593                     request: params.request,
594                     descriptor: params.descriptor,
595                     commands: blob,
596                     visible_rect: command.visible_rect,
597                     dirty_rect: params.dirty_rect,
598                     tile_size: command.tile_size,
599                 }
600             })
601             .collect();
602 
603         // If we don't have a lot of blobs it is probably not worth the initial cost
604         // of installing work on rayon's thread pool so we do it serially on this thread.
605         let should_parallelize = if !self.enable_multithreading {
606             false
607         } else if low_priority {
608             requests.len() > 2
609         } else {
610             // For high priority requests we don't "risk" the potential priority inversion of
611             // dispatching to a thread pool full of low priority jobs unless it is really
612             // appealing.
613             requests.len() > 4
614         };
615 
616         if should_parallelize {
617             // Parallel version synchronously installs a job on the thread pool which will
618             // try to do the work in parallel.
619             // This thread is blocked until the thread pool is done doing the work.
620             let lambda = || requests.into_par_iter().map(rasterize_blob).collect();
621             if low_priority {
622                 //TODO --bpe runtime flag to A/B test these two
623                 self.workers_low_priority.install(lambda)
624             //self.workers.install(lambda)
625             } else {
626                 self.workers.install(lambda)
627             }
628         } else {
629             requests.into_iter().map(rasterize_blob).collect()
630         }
631     }
632 }
633 
634 // a cross platform wrapper that creates an autorelease pool
635 // on macOS
autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T636 fn autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T {
637     #[cfg(target_os = "macos")]
638     {
639         objc::rc::autoreleasepool(f)
640     }
641     #[cfg(not(target_os = "macos"))]
642     {
643         f()
644     }
645 }
646 
rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult)647 fn rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult) {
648     let descriptor = job.descriptor;
649     let buf_size = (descriptor.rect.area() * descriptor.format.bytes_per_pixel()) as usize;
650 
651     let mut output = vec![0u8; buf_size];
652 
653     let dirty_rect = match job.dirty_rect {
654         DirtyRect::Partial(rect) => Some(rect),
655         DirtyRect::All => None,
656     };
657     assert!(!descriptor.rect.is_empty());
658 
659     let result = autoreleasepool(|| {
660         unsafe {
661             if wr_moz2d_render_cb(
662                 ByteSlice::new(&job.commands[..]),
663                 descriptor.format,
664                 &descriptor.rect,
665                 &job.visible_rect,
666                 job.tile_size,
667                 &job.request.tile,
668                 dirty_rect.as_ref(),
669                 MutByteSlice::new(output.as_mut_slice()),
670             ) {
671                 // We want the dirty rect local to the tile rather than the whole image.
672                 // TODO(nical): move that up and avoid recomupting the tile bounds in the callback
673                 let dirty_rect = job.dirty_rect.to_subrect_of(&descriptor.rect);
674                 let tx: BlobToDeviceTranslation = (-descriptor.rect.min.to_vector()).into();
675                 let rasterized_rect = tx.transform_box(&dirty_rect);
676 
677                 Ok(RasterizedBlobImage {
678                     rasterized_rect,
679                     data: Arc::new(output),
680                 })
681             } else {
682                 panic!("Moz2D replay problem");
683             }
684         }
685     });
686 
687     (job.request, result)
688 }
689 
690 impl BlobImageHandler for Moz2dBlobImageHandler {
create_similar(&self) -> Box<dyn BlobImageHandler>691     fn create_similar(&self) -> Box<dyn BlobImageHandler> {
692         Box::new(Self::new(
693             Arc::clone(&self.workers),
694             Arc::clone(&self.workers_low_priority),
695         ))
696     }
697 
add(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect, tile_size: TileSize)698     fn add(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect, tile_size: TileSize) {
699         {
700             let index = BlobReader::new(&data);
701             assert!(index.reader.has_more());
702         }
703         self.blob_commands.insert(
704             key,
705             BlobCommand {
706                 data: Arc::clone(&data),
707                 visible_rect: *visible_rect,
708                 tile_size,
709             },
710         );
711     }
712 
update( &mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect, dirty_rect: &BlobDirtyRect, )713     fn update(
714         &mut self,
715         key: BlobImageKey,
716         data: Arc<BlobImageData>,
717         visible_rect: &DeviceIntRect,
718         dirty_rect: &BlobDirtyRect,
719     ) {
720         match self.blob_commands.entry(key) {
721             hash_map::Entry::Occupied(mut e) => {
722                 let command = e.get_mut();
723                 let dirty_rect = if let DirtyRect::Partial(rect) = *dirty_rect {
724                     Box2d {
725                         x1: rect.min.x,
726                         y1: rect.min.y,
727                         x2: rect.max.x,
728                         y2: rect.max.y,
729                     }
730                 } else {
731                     Box2d {
732                         x1: i32::MIN,
733                         y1: i32::MIN,
734                         x2: i32::MAX,
735                         y2: i32::MAX,
736                     }
737                 };
738                 command.data = Arc::new(merge_blob_images(
739                     &command.data,
740                     &data,
741                     dirty_rect,
742                     command.visible_rect.into(),
743                     visible_rect.into(),
744                 ));
745                 command.visible_rect = *visible_rect;
746             }
747             _ => {
748                 panic!("missing image key");
749             }
750         }
751     }
752 
delete(&mut self, key: BlobImageKey)753     fn delete(&mut self, key: BlobImageKey) {
754         self.blob_commands.remove(&key);
755     }
756 
create_blob_rasterizer(&mut self) -> Box<dyn AsyncBlobImageRasterizer>757     fn create_blob_rasterizer(&mut self) -> Box<dyn AsyncBlobImageRasterizer> {
758         Box::new(Moz2dBlobRasterizer {
759             workers: Arc::clone(&self.workers),
760             workers_low_priority: Arc::clone(&self.workers_low_priority),
761             blob_commands: self.blob_commands.clone(),
762             enable_multithreading: self.enable_multithreading,
763         })
764     }
765 
delete_font(&mut self, font: FontKey)766     fn delete_font(&mut self, font: FontKey) {
767         unsafe {
768             DeleteFontData(font);
769         }
770     }
771 
delete_font_instance(&mut self, key: FontInstanceKey)772     fn delete_font_instance(&mut self, key: FontInstanceKey) {
773         unsafe {
774             DeleteBlobFont(key);
775         }
776     }
777 
clear_namespace(&mut self, namespace: IdNamespace)778     fn clear_namespace(&mut self, namespace: IdNamespace) {
779         unsafe {
780             ClearBlobImageResources(namespace);
781         }
782     }
783 
prepare_resources(&mut self, resources: &dyn BlobImageResources, requests: &[BlobImageParams])784     fn prepare_resources(&mut self, resources: &dyn BlobImageResources, requests: &[BlobImageParams]) {
785         for params in requests {
786             let commands = &self.blob_commands[&params.request.key];
787             let blob = Arc::clone(&commands.data);
788             self.prepare_request(&blob, resources);
789         }
790     }
791 
enable_multithreading(&mut self, enable: bool)792     fn enable_multithreading(&mut self, enable: bool) {
793         self.enable_multithreading = enable;
794     }
795 }
796 
797 use bindings::{WrFontInstanceKey, WrFontKey, WrIdNamespace};
798 
799 #[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &Arc<Vec> to an extern function
800 extern "C" {
HasFontData(key: WrFontKey) -> bool801     fn HasFontData(key: WrFontKey) -> bool;
AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &ArcVecU8)802     fn AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &ArcVecU8);
AddNativeFontHandle(key: WrFontKey, handle: *mut c_void, index: u32)803     fn AddNativeFontHandle(key: WrFontKey, handle: *mut c_void, index: u32);
DeleteFontData(key: WrFontKey)804     fn DeleteFontData(key: WrFontKey);
AddBlobFont( instance_key: WrFontInstanceKey, font_key: WrFontKey, size: f32, options: Option<&FontInstanceOptions>, platform_options: Option<&FontInstancePlatformOptions>, variations: *const FontVariation, num_variations: usize, )805     fn AddBlobFont(
806         instance_key: WrFontInstanceKey,
807         font_key: WrFontKey,
808         size: f32,
809         options: Option<&FontInstanceOptions>,
810         platform_options: Option<&FontInstancePlatformOptions>,
811         variations: *const FontVariation,
812         num_variations: usize,
813     );
DeleteBlobFont(key: WrFontInstanceKey)814     fn DeleteBlobFont(key: WrFontInstanceKey);
ClearBlobImageResources(namespace: WrIdNamespace)815     fn ClearBlobImageResources(namespace: WrIdNamespace);
816 
817 }
818 
819 impl Moz2dBlobImageHandler {
820     /// Create a new BlobImageHandler with the given thread pool.
new(workers: Arc<ThreadPool>, workers_low_priority: Arc<ThreadPool>) -> Self821     pub fn new(workers: Arc<ThreadPool>, workers_low_priority: Arc<ThreadPool>) -> Self {
822         Moz2dBlobImageHandler {
823             blob_commands: HashMap::new(),
824             workers,
825             workers_low_priority,
826             enable_multithreading: true,
827         }
828     }
829 
830     /// Does early preprocessing of a blob's resources.
831     ///
832     /// Currently just sets up fonts found in the blob.
prepare_request(&self, blob: &[u8], resources: &dyn BlobImageResources)833     fn prepare_request(&self, blob: &[u8], resources: &dyn BlobImageResources) {
834         #[cfg(target_os = "windows")]
835         fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
836             let file = dwrote::FontFile::new_from_path(&handle.path).unwrap();
837             let face = file
838                 .create_face(handle.index, dwrote::DWRITE_FONT_SIMULATIONS_NONE)
839                 .unwrap();
840             unsafe { AddNativeFontHandle(key, face.as_ptr() as *mut c_void, 0) };
841         }
842 
843         #[cfg(target_os = "macos")]
844         fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
845             unsafe { AddNativeFontHandle(key, handle.0.as_ptr() as *mut c_void, 0) };
846         }
847 
848         #[cfg(not(any(target_os = "macos", target_os = "windows")))]
849         fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
850             let cstr = CString::new(handle.path.as_os_str().as_bytes()).unwrap();
851             unsafe { AddNativeFontHandle(key, cstr.as_ptr() as *mut c_void, handle.index) };
852         }
853 
854         fn process_fonts(
855             mut extra_data: BufReader,
856             resources: &dyn BlobImageResources,
857             unscaled_fonts: &mut Vec<FontKey>,
858             scaled_fonts: &mut Vec<FontInstanceKey>,
859         ) {
860             let font_count = extra_data.read_usize();
861             for _ in 0..font_count {
862                 let font = extra_data.read_blob_font();
863                 if scaled_fonts.contains(&font.font_instance_key) {
864                     continue;
865                 }
866                 scaled_fonts.push(font.font_instance_key);
867                 if let Some(instance) = resources.get_font_instance_data(font.font_instance_key) {
868                     if !unscaled_fonts.contains(&instance.font_key) {
869                         unscaled_fonts.push(instance.font_key);
870                         if !unsafe { HasFontData(instance.font_key) } {
871                             let template = resources.get_font_data(instance.font_key);
872                             match template {
873                                 FontTemplate::Raw(ref data, ref index) => unsafe {
874                                     AddFontData(instance.font_key, data.as_ptr(), data.len(), *index, data);
875                                 },
876                                 FontTemplate::Native(ref handle) => {
877                                     process_native_font_handle(instance.font_key, handle);
878                                 }
879                             }
880                         }
881                     }
882                     unsafe {
883                         AddBlobFont(
884                             font.font_instance_key,
885                             instance.font_key,
886                             instance.size,
887                             instance.options.as_ref(),
888                             instance.platform_options.as_ref(),
889                             instance.variations.as_ptr(),
890                             instance.variations.len(),
891                         );
892                     }
893                 }
894             }
895         }
896 
897         {
898             let mut index = BlobReader::new(blob);
899             let mut unscaled_fonts = Vec::new();
900             let mut scaled_fonts = Vec::new();
901             while index.reader.pos < index.reader.buf.len() {
902                 let e = index.read_entry();
903                 process_fonts(
904                     BufReader::new(&blob[e.end..e.extra_end]),
905                     resources,
906                     &mut unscaled_fonts,
907                     &mut scaled_fonts,
908                 );
909             }
910         }
911     }
912 }
913