Source code

Revision control

Other Tools

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