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 
5 use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult};
6 use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, ExternalEvent, Epoch};
7 use api::{BuiltDisplayList, ColorF, LayoutSize, NotificationRequest, Checkpoint, IdNamespace};
8 use api::{MemoryReport};
9 use api::channel::MsgSender;
10 #[cfg(feature = "capture")]
11 use capture::CaptureConfig;
12 use frame_builder::{FrameBuilderConfig, FrameBuilder};
13 use clip_scroll_tree::ClipScrollTree;
14 use display_list_flattener::DisplayListFlattener;
15 use intern::{Internable, Interner};
16 use intern_types;
17 use internal_types::{FastHashMap, FastHashSet};
18 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
19 use prim_store::{PrimitiveKeyKind};
20 use prim_store::PrimitiveStoreStats;
21 use prim_store::borders::{ImageBorder, NormalBorderPrim};
22 use prim_store::gradient::{LinearGradient, RadialGradient};
23 use prim_store::image::{Image, YuvImage};
24 use prim_store::line_dec::LineDecoration;
25 use prim_store::picture::Picture;
26 use prim_store::text_run::TextRun;
27 use resource_cache::{AsyncBlobImageInfo, FontInstanceMap};
28 use render_backend::DocumentView;
29 use renderer::{PipelineInfo, SceneBuilderHooks};
30 use scene::Scene;
31 use std::sync::mpsc::{channel, Receiver, Sender};
32 use std::mem::replace;
33 use time::precise_time_ns;
34 use util::drain_filter;
35 use std::thread;
36 use std::time::Duration;
37 
38 /// Represents the work associated to a transaction before scene building.
39 pub struct Transaction {
40     pub document_id: DocumentId,
41     pub display_list_updates: Vec<DisplayListUpdate>,
42     pub removed_pipelines: Vec<PipelineId>,
43     pub epoch_updates: Vec<(PipelineId, Epoch)>,
44     pub request_scene_build: Option<SceneRequest>,
45     pub blob_requests: Vec<BlobImageParams>,
46     pub blob_rasterizer: Option<(Box<AsyncBlobImageRasterizer>, AsyncBlobImageInfo)>,
47     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
48     pub resource_updates: Vec<ResourceUpdate>,
49     pub frame_ops: Vec<FrameMsg>,
50     pub notifications: Vec<NotificationRequest>,
51     pub set_root_pipeline: Option<PipelineId>,
52     pub render_frame: bool,
53     pub invalidate_rendered_frame: bool,
54 }
55 
56 impl Transaction {
can_skip_scene_builder(&self) -> bool57     pub fn can_skip_scene_builder(&self) -> bool {
58         self.request_scene_build.is_none() &&
59             self.display_list_updates.is_empty() &&
60             self.epoch_updates.is_empty() &&
61             self.removed_pipelines.is_empty() &&
62             self.blob_requests.is_empty() &&
63             self.set_root_pipeline.is_none()
64     }
65 
should_build_scene(&self) -> bool66     pub fn should_build_scene(&self) -> bool {
67         !self.display_list_updates.is_empty() ||
68             self.set_root_pipeline.is_some()
69     }
70 
rasterize_blobs(&mut self, is_low_priority: bool)71     fn rasterize_blobs(&mut self, is_low_priority: bool) {
72         if let Some((ref mut rasterizer, _)) = self.blob_rasterizer {
73             let mut rasterized_blobs = rasterizer.rasterize(&self.blob_requests, is_low_priority);
74             // try using the existing allocation if our current list is empty
75             if self.rasterized_blobs.is_empty() {
76                 self.rasterized_blobs = rasterized_blobs;
77             } else {
78                 self.rasterized_blobs.append(&mut rasterized_blobs);
79             }
80         }
81     }
82 }
83 
84 /// Represent the remaining work associated to a transaction after the scene building
85 /// phase as well as the result of scene building itself if applicable.
86 pub struct BuiltTransaction {
87     pub document_id: DocumentId,
88     pub built_scene: Option<BuiltScene>,
89     pub resource_updates: Vec<ResourceUpdate>,
90     pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
91     pub blob_rasterizer: Option<(Box<AsyncBlobImageRasterizer>, AsyncBlobImageInfo)>,
92     pub frame_ops: Vec<FrameMsg>,
93     pub removed_pipelines: Vec<PipelineId>,
94     pub notifications: Vec<NotificationRequest>,
95     pub interner_updates: Option<InternerUpdates>,
96     pub scene_build_start_time: u64,
97     pub scene_build_end_time: u64,
98     pub render_frame: bool,
99     pub invalidate_rendered_frame: bool,
100 }
101 
102 pub struct DisplayListUpdate {
103     pub pipeline_id: PipelineId,
104     pub epoch: Epoch,
105     pub built_display_list: BuiltDisplayList,
106     pub background: Option<ColorF>,
107     pub viewport_size: LayoutSize,
108     pub content_size: LayoutSize,
109 }
110 
111 /// Contains the render backend data needed to build a scene.
112 pub struct SceneRequest {
113     pub view: DocumentView,
114     pub font_instances: FontInstanceMap,
115     pub output_pipelines: FastHashSet<PipelineId>,
116 }
117 
118 #[cfg(feature = "replay")]
119 pub struct LoadScene {
120     pub document_id: DocumentId,
121     pub scene: Scene,
122     pub output_pipelines: FastHashSet<PipelineId>,
123     pub font_instances: FontInstanceMap,
124     pub view: DocumentView,
125     pub config: FrameBuilderConfig,
126     pub build_frame: bool,
127     pub interners: Interners,
128 }
129 
130 pub struct BuiltScene {
131     pub scene: Scene,
132     pub frame_builder: FrameBuilder,
133     pub clip_scroll_tree: ClipScrollTree,
134 }
135 
136 // Message from render backend to scene builder.
137 pub enum SceneBuilderRequest {
138     Transaction(Box<Transaction>),
139     ExternalEvent(ExternalEvent),
140     DeleteDocument(DocumentId),
141     WakeUp,
142     Flush(MsgSender<()>),
143     ClearNamespace(IdNamespace),
144     SetFrameBuilderConfig(FrameBuilderConfig),
145     SimulateLongSceneBuild(u32),
146     SimulateLongLowPrioritySceneBuild(u32),
147     Stop,
148     ReportMemory(MemoryReport, MsgSender<MemoryReport>),
149     #[cfg(feature = "capture")]
150     SaveScene(CaptureConfig),
151     #[cfg(feature = "replay")]
152     LoadScenes(Vec<LoadScene>),
153 }
154 
155 // Message from scene builder to render backend.
156 pub enum SceneBuilderResult {
157     Transaction(Box<BuiltTransaction>, Option<Sender<SceneSwapResult>>),
158     ExternalEvent(ExternalEvent),
159     FlushComplete(MsgSender<()>),
160     ClearNamespace(IdNamespace),
161     Stopped,
162 }
163 
164 // Message from render backend to scene builder to indicate the
165 // scene swap was completed. We need a separate channel for this
166 // so that they don't get mixed with SceneBuilderRequest messages.
167 pub enum SceneSwapResult {
168     Complete(Sender<()>),
169     Aborted,
170 }
171 
172 macro_rules! declare_interners {
173     ( $( $name: ident, )+ ) => {
174         /// This struct contains all items that can be shared between
175         /// display lists. We want to intern and share the same clips,
176         /// primitives and other things between display lists so that:
177         /// - GPU cache handles remain valid, reducing GPU cache updates.
178         /// - Comparison of primitives and pictures between two
179         ///   display lists is (a) fast (b) done during scene building.
180         #[cfg_attr(feature = "capture", derive(Serialize))]
181         #[cfg_attr(feature = "replay", derive(Deserialize))]
182         #[derive(Default)]
183         pub struct Interners {
184             $(
185                 pub $name: intern_types::$name::Interner,
186             )+
187         }
188 
189         pub struct InternerUpdates {
190             $(
191                 pub $name: intern_types::$name::UpdateList,
192             )+
193         }
194 
195         impl Interners {
196             /// Reports CPU heap memory used by the interners.
197             fn report_memory(
198                 &self,
199                 ops: &mut MallocSizeOfOps,
200                 r: &mut MemoryReport,
201             ) {
202                 $(
203                     r.interning.interners.$name += self.$name.size_of(ops);
204                 )+
205             }
206 
207             fn end_frame_and_get_pending_updates(&mut self) -> InternerUpdates {
208                 InternerUpdates {
209                     $(
210                         $name: self.$name.end_frame_and_get_pending_updates(),
211                     )+
212                 }
213             }
214         }
215     }
216 }
217 
218 enumerate_interners!(declare_interners);
219 
220 // Access to `Interners` interners by `Internable`
221 pub trait InternerMut<I: Internable>
222 {
interner_mut(&mut self) -> &mut Interner<I::Source, I::InternData, I::Marker>223     fn interner_mut(&mut self) -> &mut Interner<I::Source, I::InternData, I::Marker>;
224 }
225 
226 macro_rules! impl_interner_mut {
227     ($($ty:ident: $mem:ident,)*) => {
228         $(impl InternerMut<$ty> for Interners {
229             fn interner_mut(&mut self) -> &mut Interner<
230                 <$ty as Internable>::Source,
231                 <$ty as Internable>::InternData,
232                 <$ty as Internable>::Marker
233             > {
234                 &mut self.$mem
235             }
236         })*
237     }
238 }
239 
240 impl_interner_mut! {
241     Image: image,
242     ImageBorder: image_border,
243     LineDecoration: line_decoration,
244     LinearGradient: linear_grad,
245     NormalBorderPrim: normal_border,
246     Picture: picture,
247     PrimitiveKeyKind: prim,
248     RadialGradient: radial_grad,
249     TextRun: text_run,
250     YuvImage: yuv_image,
251 }
252 
253 // A document in the scene builder contains the current scene,
254 // as well as a persistent clip interner. This allows clips
255 // to be de-duplicated, and persisted in the GPU cache between
256 // display lists.
257 struct Document {
258     scene: Scene,
259     interners: Interners,
260     prim_store_stats: PrimitiveStoreStats,
261 }
262 
263 impl Document {
new(scene: Scene) -> Self264     fn new(scene: Scene) -> Self {
265         Document {
266             scene,
267             interners: Interners::default(),
268             prim_store_stats: PrimitiveStoreStats::empty(),
269         }
270     }
271 }
272 
273 pub struct SceneBuilder {
274     documents: FastHashMap<DocumentId, Document>,
275     rx: Receiver<SceneBuilderRequest>,
276     tx: Sender<SceneBuilderResult>,
277     api_tx: MsgSender<ApiMsg>,
278     config: FrameBuilderConfig,
279     hooks: Option<Box<SceneBuilderHooks + Send>>,
280     simulate_slow_ms: u32,
281     size_of_ops: Option<MallocSizeOfOps>,
282 }
283 
284 impl SceneBuilder {
new( config: FrameBuilderConfig, api_tx: MsgSender<ApiMsg>, hooks: Option<Box<SceneBuilderHooks + Send>>, size_of_ops: Option<MallocSizeOfOps>, ) -> (Self, Sender<SceneBuilderRequest>, Receiver<SceneBuilderResult>)285     pub fn new(
286         config: FrameBuilderConfig,
287         api_tx: MsgSender<ApiMsg>,
288         hooks: Option<Box<SceneBuilderHooks + Send>>,
289         size_of_ops: Option<MallocSizeOfOps>,
290     ) -> (Self, Sender<SceneBuilderRequest>, Receiver<SceneBuilderResult>) {
291         let (in_tx, in_rx) = channel();
292         let (out_tx, out_rx) = channel();
293         (
294             SceneBuilder {
295                 documents: FastHashMap::default(),
296                 rx: in_rx,
297                 tx: out_tx,
298                 api_tx,
299                 config,
300                 hooks,
301                 size_of_ops,
302                 simulate_slow_ms: 0,
303             },
304             in_tx,
305             out_rx,
306         )
307     }
308 
309     /// Send a message to the render backend thread.
310     ///
311     /// We first put something in the result queue and then send a wake-up
312     /// message to the api queue that the render backend is blocking on.
send(&self, msg: SceneBuilderResult)313     pub fn send(&self, msg: SceneBuilderResult) {
314         self.tx.send(msg).unwrap();
315         let _ = self.api_tx.send(ApiMsg::WakeUp);
316     }
317 
318     /// The scene builder thread's event loop.
run(&mut self)319     pub fn run(&mut self) {
320         if let Some(ref hooks) = self.hooks {
321             hooks.register();
322         }
323 
324         loop {
325             match self.rx.recv() {
326                 Ok(SceneBuilderRequest::WakeUp) => {}
327                 Ok(SceneBuilderRequest::Flush(tx)) => {
328                     self.send(SceneBuilderResult::FlushComplete(tx));
329                 }
330                 Ok(SceneBuilderRequest::Transaction(mut txn)) => {
331                     let built_txn = self.process_transaction(&mut txn);
332                     self.forward_built_transaction(built_txn);
333                 }
334                 Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
335                     self.documents.remove(&document_id);
336                 }
337                 Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => {
338                     self.config = cfg;
339                 }
340                 Ok(SceneBuilderRequest::ClearNamespace(id)) => {
341                     self.documents.retain(|doc_id, _doc| doc_id.0 != id);
342                     self.send(SceneBuilderResult::ClearNamespace(id));
343                 }
344                 #[cfg(feature = "replay")]
345                 Ok(SceneBuilderRequest::LoadScenes(msg)) => {
346                     self.load_scenes(msg);
347                 }
348                 #[cfg(feature = "capture")]
349                 Ok(SceneBuilderRequest::SaveScene(config)) => {
350                     self.save_scene(config);
351                 }
352                 Ok(SceneBuilderRequest::ExternalEvent(evt)) => {
353                     self.send(SceneBuilderResult::ExternalEvent(evt));
354                 }
355                 Ok(SceneBuilderRequest::Stop) => {
356                     self.tx.send(SceneBuilderResult::Stopped).unwrap();
357                     // We don't need to send a WakeUp to api_tx because we only
358                     // get the Stop when the RenderBackend loop is exiting.
359                     break;
360                 }
361                 Ok(SceneBuilderRequest::ReportMemory(mut report, tx)) => {
362                     report += self.report_memory();
363                     tx.send(report).unwrap();
364                 }
365                 Ok(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)) => {
366                     self.simulate_slow_ms = time_ms
367                 }
368                 Ok(SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(_)) => {}
369                 Err(_) => {
370                     break;
371                 }
372             }
373 
374             if let Some(ref hooks) = self.hooks {
375                 hooks.poke();
376             }
377         }
378 
379         if let Some(ref hooks) = self.hooks {
380             hooks.deregister();
381         }
382     }
383 
384     #[cfg(feature = "capture")]
save_scene(&mut self, config: CaptureConfig)385     fn save_scene(&mut self, config: CaptureConfig) {
386         for (id, doc) in &self.documents {
387             let interners_name = format!("interners-{}-{}", (id.0).0, id.1);
388             config.serialize(&doc.interners, interners_name);
389         }
390     }
391 
392     #[cfg(feature = "replay")]
load_scenes(&mut self, scenes: Vec<LoadScene>)393     fn load_scenes(&mut self, scenes: Vec<LoadScene>) {
394         for mut item in scenes {
395             self.config = item.config;
396 
397             let scene_build_start_time = precise_time_ns();
398 
399             let mut built_scene = None;
400             let mut interner_updates = None;
401 
402             if item.scene.has_root_pipeline() {
403                 let mut clip_scroll_tree = ClipScrollTree::new();
404                 let mut new_scene = Scene::new();
405 
406                 let frame_builder = DisplayListFlattener::create_frame_builder(
407                     &item.scene,
408                     &mut clip_scroll_tree,
409                     item.font_instances,
410                     &item.view,
411                     &item.output_pipelines,
412                     &self.config,
413                     &mut new_scene,
414                     &mut item.interners,
415                     &PrimitiveStoreStats::empty(),
416                 );
417 
418                 interner_updates = Some(
419                     item.interners.end_frame_and_get_pending_updates()
420                 );
421 
422                 built_scene = Some(BuiltScene {
423                     scene: new_scene,
424                     frame_builder,
425                     clip_scroll_tree,
426                 });
427             }
428 
429             self.documents.insert(
430                 item.document_id,
431                 Document {
432                     scene: item.scene,
433                     interners: item.interners,
434                     prim_store_stats: PrimitiveStoreStats::empty(),
435                 },
436             );
437 
438             let txn = Box::new(BuiltTransaction {
439                 document_id: item.document_id,
440                 render_frame: item.build_frame,
441                 invalidate_rendered_frame: false,
442                 built_scene,
443                 resource_updates: Vec::new(),
444                 rasterized_blobs: Vec::new(),
445                 blob_rasterizer: None,
446                 frame_ops: Vec::new(),
447                 removed_pipelines: Vec::new(),
448                 notifications: Vec::new(),
449                 scene_build_start_time,
450                 scene_build_end_time: precise_time_ns(),
451                 interner_updates,
452             });
453 
454             self.forward_built_transaction(txn);
455         }
456     }
457 
458     /// Do the bulk of the work of the scene builder thread.
process_transaction(&mut self, txn: &mut Transaction) -> Box<BuiltTransaction>459     fn process_transaction(&mut self, txn: &mut Transaction) -> Box<BuiltTransaction> {
460         if let &Some(ref hooks) = &self.hooks {
461             hooks.pre_scene_build();
462         }
463 
464         let scene_build_start_time = precise_time_ns();
465 
466         let doc = self.documents
467                       .entry(txn.document_id)
468                       .or_insert(Document::new(Scene::new()));
469         let scene = &mut doc.scene;
470 
471         for update in txn.display_list_updates.drain(..) {
472             scene.set_display_list(
473                 update.pipeline_id,
474                 update.epoch,
475                 update.built_display_list,
476                 update.background,
477                 update.viewport_size,
478                 update.content_size,
479             );
480         }
481 
482         for &(pipeline_id, epoch) in &txn.epoch_updates {
483             scene.update_epoch(pipeline_id, epoch);
484         }
485 
486         if let Some(id) = txn.set_root_pipeline {
487             scene.set_root_pipeline_id(id);
488         }
489 
490         for pipeline_id in &txn.removed_pipelines {
491             scene.remove_pipeline(*pipeline_id)
492         }
493 
494         let mut built_scene = None;
495         let mut interner_updates = None;
496         if scene.has_root_pipeline() {
497             if let Some(request) = txn.request_scene_build.take() {
498                 let mut clip_scroll_tree = ClipScrollTree::new();
499                 let mut new_scene = Scene::new();
500 
501                 let frame_builder = DisplayListFlattener::create_frame_builder(
502                     &scene,
503                     &mut clip_scroll_tree,
504                     request.font_instances,
505                     &request.view,
506                     &request.output_pipelines,
507                     &self.config,
508                     &mut new_scene,
509                     &mut doc.interners,
510                     &doc.prim_store_stats,
511                 );
512 
513                 // Update the allocation stats for next scene
514                 doc.prim_store_stats = frame_builder.prim_store.get_stats();
515 
516                 // Retrieve the list of updates from the clip interner.
517                 interner_updates = Some(
518                     doc.interners.end_frame_and_get_pending_updates()
519                 );
520 
521                 built_scene = Some(BuiltScene {
522                     scene: new_scene,
523                     frame_builder,
524                     clip_scroll_tree,
525                 });
526             }
527         }
528 
529         let is_low_priority = false;
530         txn.rasterize_blobs(is_low_priority);
531 
532         drain_filter(
533             &mut txn.notifications,
534             |n| { n.when() == Checkpoint::SceneBuilt },
535             |n| { n.notify(); },
536         );
537 
538         if self.simulate_slow_ms > 0 {
539             thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64));
540         }
541 
542         Box::new(BuiltTransaction {
543             document_id: txn.document_id,
544             render_frame: txn.render_frame,
545             invalidate_rendered_frame: txn.invalidate_rendered_frame,
546             built_scene,
547             rasterized_blobs: replace(&mut txn.rasterized_blobs, Vec::new()),
548             resource_updates: replace(&mut txn.resource_updates, Vec::new()),
549             blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
550             frame_ops: replace(&mut txn.frame_ops, Vec::new()),
551             removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
552             notifications: replace(&mut txn.notifications, Vec::new()),
553             interner_updates,
554             scene_build_start_time,
555             scene_build_end_time: precise_time_ns(),
556         })
557     }
558 
559     /// Send the result of process_transaction back to the render backend.
forward_built_transaction(&mut self, txn: Box<BuiltTransaction>)560     fn forward_built_transaction(&mut self, txn: Box<BuiltTransaction>) {
561         // We only need the pipeline info and the result channel if we
562         // have a hook callback *and* if this transaction actually built
563         // a new scene that is going to get swapped in. In other cases
564         // pipeline_info can be None and we can avoid some overhead from
565         // invoking the hooks and blocking on the channel.
566         let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) {
567             (&Some(ref hooks), &Some(ref built)) => {
568                 let info = PipelineInfo {
569                     epochs: built.scene.pipeline_epochs.clone(),
570                     removed_pipelines: txn.removed_pipelines.clone(),
571                 };
572                 let (tx, rx) = channel();
573 
574                 hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time);
575 
576                 (Some(info), Some(tx), Some(rx))
577             }
578             _ => (None, None, None),
579         };
580 
581         let scene_swap_start_time = precise_time_ns();
582         let has_resources_updates = !txn.resource_updates.is_empty();
583 
584         self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap();
585 
586         let _ = self.api_tx.send(ApiMsg::WakeUp);
587 
588         if let Some(pipeline_info) = pipeline_info {
589             // Block until the swap is done, then invoke the hook.
590             let swap_result = result_rx.unwrap().recv();
591             let scene_swap_time = precise_time_ns() - scene_swap_start_time;
592             self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, scene_swap_time);
593             // Once the hook is done, allow the RB thread to resume
594             match swap_result {
595                 Ok(SceneSwapResult::Complete(resume_tx)) => {
596                     resume_tx.send(()).ok();
597                 },
598                 _ => (),
599             };
600         } else if has_resources_updates {
601             if let &Some(ref hooks) = &self.hooks {
602                 hooks.post_resource_update();
603             }
604         } else {
605             if let &Some(ref hooks) = &self.hooks {
606                 hooks.post_empty_scene_build();
607             }
608         }
609     }
610 
611     /// Reports CPU heap memory used by the SceneBuilder.
report_memory(&mut self) -> MemoryReport612     fn report_memory(&mut self) -> MemoryReport {
613         let ops = self.size_of_ops.as_mut().unwrap();
614         let mut report = MemoryReport::default();
615         for doc in self.documents.values() {
616             doc.interners.report_memory(ops, &mut report);
617         }
618 
619         report
620     }
621 }
622 
623 /// A scene builder thread which executes expensive operations such as blob rasterization
624 /// with a lower priority than the normal scene builder thread.
625 ///
626 /// After rasterizing blobs, the secene building request is forwarded to the normal scene
627 /// builder where the FrameBuilder is generated.
628 pub struct LowPrioritySceneBuilder {
629     pub rx: Receiver<SceneBuilderRequest>,
630     pub tx: Sender<SceneBuilderRequest>,
631     pub simulate_slow_ms: u32,
632 }
633 
634 impl LowPrioritySceneBuilder {
run(&mut self)635     pub fn run(&mut self) {
636         loop {
637             match self.rx.recv() {
638                 Ok(SceneBuilderRequest::Transaction(txn)) => {
639                     let txn = self.process_transaction(txn);
640                     self.tx.send(SceneBuilderRequest::Transaction(txn)).unwrap();
641                 }
642                 Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
643                     self.tx.send(SceneBuilderRequest::DeleteDocument(document_id)).unwrap();
644                 }
645                 Ok(SceneBuilderRequest::Stop) => {
646                     self.tx.send(SceneBuilderRequest::Stop).unwrap();
647                     break;
648                 }
649                 Ok(SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(time_ms)) => {
650                     self.simulate_slow_ms = time_ms;
651                 }
652                 Ok(other) => {
653                     self.tx.send(other).unwrap();
654                 }
655                 Err(_) => {
656                     break;
657                 }
658             }
659         }
660     }
661 
process_transaction(&mut self, mut txn: Box<Transaction>) -> Box<Transaction>662     fn process_transaction(&mut self, mut txn: Box<Transaction>) -> Box<Transaction> {
663         let is_low_priority = true;
664         txn.rasterize_blobs(is_low_priority);
665         txn.blob_requests = Vec::new();
666 
667         if self.simulate_slow_ms > 0 {
668             thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64));
669         }
670 
671         txn
672     }
673 }
674