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
//! The high-level module responsible for managing the pipeline and preparing
6
//! commands to be issued by the `Renderer`.
7
//!
8
//! See the comment at the top of the `renderer` module for a description of
9
//! how these two pieces interact.
10
11
use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand, DebugFlags};
12
use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult};
13
use api::{IdNamespace, MemoryReport, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
14
use api::{ScrollLocation, TransactionMsg, ResourceUpdate, BlobImageKey};
15
use api::{NotificationRequest, Checkpoint, QualitySettings};
16
use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind};
17
use api::units::*;
18
use api::channel::{MsgReceiver, MsgSender, Payload};
19
#[cfg(feature = "capture")]
20
use api::CaptureBits;
21
#[cfg(feature = "replay")]
22
use api::CapturedDocument;
23
use crate::spatial_tree::SpatialNodeIndex;
24
use crate::composite::{CompositorKind, CompositeDescriptor};
25
#[cfg(feature = "debugger")]
26
use crate::debug_server;
27
use crate::frame_builder::{FrameBuilder, FrameBuilderConfig};
28
use crate::glyph_rasterizer::{FontInstance};
29
use crate::gpu_cache::GpuCache;
30
use crate::hit_test::{HitTest, HitTester, SharedHitTester};
31
use crate::intern::DataStore;
32
use crate::internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
33
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
34
use crate::picture::{RetainedTiles, TileCacheLogger};
35
use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance};
36
use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
37
use crate::prim_store::interned::*;
38
use crate::profiler::{BackendProfileCounters, ResourceProfileCounters};
39
use crate::record::ApiRecordingReceiver;
40
use crate::record::LogRecorder;
41
use crate::render_task_graph::RenderTaskGraphCounters;
42
use crate::renderer::{AsyncPropertySampler, PipelineInfo};
43
use crate::resource_cache::ResourceCache;
44
#[cfg(feature = "replay")]
45
use crate::resource_cache::PlainCacheOwn;
46
#[cfg(any(feature = "capture", feature = "replay"))]
47
use crate::resource_cache::PlainResources;
48
#[cfg(feature = "replay")]
49
use crate::scene::Scene;
50
use crate::scene::{BuiltScene, SceneProperties};
51
use crate::scene_builder_thread::*;
52
#[cfg(feature = "serialize")]
53
use serde::{Serialize, Deserialize};
54
#[cfg(feature = "debugger")]
55
use serde_json;
56
use std::path::PathBuf;
57
use std::sync::Arc;
58
use std::sync::atomic::{AtomicUsize, Ordering};
59
use std::sync::mpsc::{channel, Sender, Receiver};
60
use std::time::{UNIX_EPOCH, SystemTime};
61
use std::u32;
62
#[cfg(feature = "replay")]
63
use crate::frame_builder::Frame;
64
use time::precise_time_ns;
65
use crate::util::{Recycler, VecHelper, drain_filter};
66
67
68
#[cfg_attr(feature = "capture", derive(Serialize))]
69
#[cfg_attr(feature = "replay", derive(Deserialize))]
70
#[derive(Clone)]
71
pub struct DocumentView {
72
pub device_rect: DeviceIntRect,
73
pub layer: DocumentLayer,
74
pub pan: DeviceIntPoint,
75
pub device_pixel_ratio: f32,
76
pub page_zoom_factor: f32,
77
pub pinch_zoom_factor: f32,
78
pub quality_settings: QualitySettings,
79
}
80
81
impl DocumentView {
82
pub fn accumulated_scale_factor(&self) -> DevicePixelScale {
83
DevicePixelScale::new(
84
self.device_pixel_ratio *
85
self.page_zoom_factor *
86
self.pinch_zoom_factor
87
)
88
}
89
90
pub fn accumulated_scale_factor_for_snapping(&self) -> DevicePixelScale {
91
DevicePixelScale::new(
92
self.device_pixel_ratio *
93
self.page_zoom_factor
94
)
95
}
96
}
97
98
#[derive(Copy, Clone, Hash, MallocSizeOf, PartialEq, PartialOrd, Debug, Eq, Ord)]
99
#[cfg_attr(feature = "capture", derive(Serialize))]
100
#[cfg_attr(feature = "replay", derive(Deserialize))]
101
pub struct FrameId(usize);
102
103
impl FrameId {
104
/// Returns a FrameId corresponding to the first frame.
105
///
106
/// Note that we use 0 as the internal id here because the current code
107
/// increments the frame id at the beginning of the frame, rather than
108
/// at the end, and we want the first frame to be 1. It would probably
109
/// be sensible to move the advance() call to after frame-building, and
110
/// then make this method return FrameId(1).
111
pub fn first() -> Self {
112
FrameId(0)
113
}
114
115
/// Returns the backing usize for this FrameId.
116
pub fn as_usize(&self) -> usize {
117
self.0
118
}
119
120
/// Advances this FrameId to the next frame.
121
pub fn advance(&mut self) {
122
self.0 += 1;
123
}
124
125
/// An invalid sentinel FrameId, which will always compare less than
126
/// any valid FrameId.
127
pub const INVALID: FrameId = FrameId(0);
128
}
129
130
impl Default for FrameId {
131
fn default() -> Self {
132
FrameId::INVALID
133
}
134
}
135
136
impl ::std::ops::Add<usize> for FrameId {
137
type Output = Self;
138
fn add(self, other: usize) -> FrameId {
139
FrameId(self.0 + other)
140
}
141
}
142
143
impl ::std::ops::Sub<usize> for FrameId {
144
type Output = Self;
145
fn sub(self, other: usize) -> FrameId {
146
assert!(self.0 >= other, "Underflow subtracting FrameIds");
147
FrameId(self.0 - other)
148
}
149
}
150
151
enum RenderBackendStatus {
152
Continue,
153
ShutDown(Option<MsgSender<()>>),
154
}
155
156
/// Identifier to track a sequence of frames.
157
///
158
/// This is effectively a `FrameId` with a ridealong timestamp corresponding
159
/// to when advance() was called, which allows for more nuanced cache eviction
160
/// decisions. As such, we use the `FrameId` for equality and comparison, since
161
/// we should never have two `FrameStamps` with the same id but different
162
/// timestamps.
163
#[derive(Copy, Clone, Debug, MallocSizeOf)]
164
#[cfg_attr(feature = "capture", derive(Serialize))]
165
#[cfg_attr(feature = "replay", derive(Deserialize))]
166
pub struct FrameStamp {
167
id: FrameId,
168
time: SystemTime,
169
document_id: DocumentId,
170
}
171
172
impl Eq for FrameStamp {}
173
174
impl PartialEq for FrameStamp {
175
fn eq(&self, other: &Self) -> bool {
176
// We should not be checking equality unless the documents are the same
177
debug_assert!(self.document_id == other.document_id);
178
self.id == other.id
179
}
180
}
181
182
impl PartialOrd for FrameStamp {
183
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
184
self.id.partial_cmp(&other.id)
185
}
186
}
187
188
impl FrameStamp {
189
/// Gets the FrameId in this stamp.
190
pub fn frame_id(&self) -> FrameId {
191
self.id
192
}
193
194
/// Gets the time associated with this FrameStamp.
195
pub fn time(&self) -> SystemTime {
196
self.time
197
}
198
199
/// Gets the DocumentId in this stamp.
200
pub fn document_id(&self) -> DocumentId {
201
self.document_id
202
}
203
204
pub fn is_valid(&self) -> bool {
205
// If any fields are their default values, the whole struct should equal INVALID
206
debug_assert!((self.time != UNIX_EPOCH && self.id != FrameId(0) && self.document_id != DocumentId::INVALID) ||
207
*self == Self::INVALID);
208
self.document_id != DocumentId::INVALID
209
}
210
211
/// Returns a FrameStamp corresponding to the first frame.
212
pub fn first(document_id: DocumentId) -> Self {
213
FrameStamp {
214
id: FrameId::first(),
215
time: SystemTime::now(),
216
document_id,
217
}
218
}
219
220
/// Advances to a new frame.
221
pub fn advance(&mut self) {
222
self.id.advance();
223
self.time = SystemTime::now();
224
}
225
226
/// An invalid sentinel FrameStamp.
227
pub const INVALID: FrameStamp = FrameStamp {
228
id: FrameId(0),
229
time: UNIX_EPOCH,
230
document_id: DocumentId::INVALID,
231
};
232
}
233
234
macro_rules! declare_data_stores {
235
( $( $name:ident : $ty:ty, )+ ) => {
236
/// A collection of resources that are shared by clips, primitives
237
/// between display lists.
238
#[cfg_attr(feature = "capture", derive(Serialize))]
239
#[cfg_attr(feature = "replay", derive(Deserialize))]
240
#[derive(Default)]
241
pub struct DataStores {
242
$(
243
pub $name: DataStore<$ty>,
244
)+
245
}
246
247
impl DataStores {
248
/// Reports CPU heap usage.
249
fn report_memory(&self, ops: &mut MallocSizeOfOps, r: &mut MemoryReport) {
250
$(
251
r.interning.data_stores.$name += self.$name.size_of(ops);
252
)+
253
}
254
255
fn apply_updates(
256
&mut self,
257
updates: InternerUpdates,
258
profile_counters: &mut BackendProfileCounters,
259
) {
260
$(
261
self.$name.apply_updates(
262
updates.$name,
263
&mut profile_counters.intern.$name,
264
);
265
)+
266
}
267
}
268
}
269
}
270
271
enumerate_interners!(declare_data_stores);
272
273
impl DataStores {
274
pub fn as_common_data(
275
&self,
276
prim_inst: &PrimitiveInstance
277
) -> &PrimTemplateCommonData {
278
match prim_inst.kind {
279
PrimitiveInstanceKind::Rectangle { data_handle, .. } |
280
PrimitiveInstanceKind::Clear { data_handle, .. } => {
281
let prim_data = &self.prim[data_handle];
282
&prim_data.common
283
}
284
PrimitiveInstanceKind::Image { data_handle, .. } => {
285
let prim_data = &self.image[data_handle];
286
&prim_data.common
287
}
288
PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
289
let prim_data = &self.image_border[data_handle];
290
&prim_data.common
291
}
292
PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
293
let prim_data = &self.line_decoration[data_handle];
294
&prim_data.common
295
}
296
PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
297
let prim_data = &self.linear_grad[data_handle];
298
&prim_data.common
299
}
300
PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
301
let prim_data = &self.normal_border[data_handle];
302
&prim_data.common
303
}
304
PrimitiveInstanceKind::Picture { data_handle, .. } => {
305
let prim_data = &self.picture[data_handle];
306
&prim_data.common
307
}
308
PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
309
let prim_data = &self.radial_grad[data_handle];
310
&prim_data.common
311
}
312
PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
313
let prim_data = &self.conic_grad[data_handle];
314
&prim_data.common
315
}
316
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
317
let prim_data = &self.text_run[data_handle];
318
&prim_data.common
319
}
320
PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
321
let prim_data = &self.yuv_image[data_handle];
322
&prim_data.common
323
}
324
PrimitiveInstanceKind::Backdrop { data_handle, .. } => {
325
let prim_data = &self.backdrop[data_handle];
326
&prim_data.common
327
}
328
PrimitiveInstanceKind::PushClipChain |
329
PrimitiveInstanceKind::PopClipChain => {
330
unreachable!();
331
}
332
}
333
}
334
}
335
336
struct Document {
337
/// The id of this document
338
id: DocumentId,
339
340
/// Temporary list of removed pipelines received from the scene builder
341
/// thread and forwarded to the renderer.
342
removed_pipelines: Vec<(PipelineId, DocumentId)>,
343
344
view: DocumentView,
345
346
/// The id and time of the current frame.
347
stamp: FrameStamp,
348
349
/// The latest built scene, usable to build frames.
350
/// received from the scene builder thread.
351
scene: BuiltScene,
352
353
/// The builder object that prodces frames, kept around to preserve some retained state.
354
frame_builder: FrameBuilder,
355
356
/// A set of pipelines that the caller has requested be
357
/// made available as output textures.
358
output_pipelines: FastHashSet<PipelineId>,
359
360
/// A data structure to allow hit testing against rendered frames. This is updated
361
/// every time we produce a fully rendered frame.
362
hit_tester: Option<Arc<HitTester>>,
363
/// To avoid synchronous messaging we update a shared hit-tester that other threads
364
/// can query.
365
shared_hit_tester: Arc<SharedHitTester>,
366
367
/// Properties that are resolved during frame building and can be changed at any time
368
/// without requiring the scene to be re-built.
369
dynamic_properties: SceneProperties,
370
371
/// Track whether the last built frame is up to date or if it will need to be re-built
372
/// before rendering again.
373
frame_is_valid: bool,
374
hit_tester_is_valid: bool,
375
rendered_frame_is_valid: bool,
376
/// We track this information to be able to display debugging information from the
377
/// renderer.
378
has_built_scene: bool,
379
380
data_stores: DataStores,
381
382
/// Contains various vecs of data that is used only during frame building,
383
/// where we want to recycle the memory each new display list, to avoid constantly
384
/// re-allocating and moving memory around.
385
scratch: PrimitiveScratchBuffer,
386
/// Keep track of the size of render task graph to pre-allocate memory up-front
387
/// the next frame.
388
render_task_counters: RenderTaskGraphCounters,
389
390
#[cfg(feature = "replay")]
391
loaded_scene: Scene,
392
393
/// Tracks the state of the picture cache tiles that were composited on the previous frame.
394
prev_composite_descriptor: CompositeDescriptor,
395
}
396
397
impl Document {
398
pub fn new(
399
id: DocumentId,
400
size: DeviceIntSize,
401
layer: DocumentLayer,
402
default_device_pixel_ratio: f32,
403
) -> Self {
404
Document {
405
id,
406
removed_pipelines: Vec::new(),
407
view: DocumentView {
408
device_rect: size.into(),
409
layer,
410
pan: DeviceIntPoint::zero(),
411
page_zoom_factor: 1.0,
412
pinch_zoom_factor: 1.0,
413
device_pixel_ratio: default_device_pixel_ratio,
414
quality_settings: QualitySettings::default(),
415
},
416
stamp: FrameStamp::first(id),
417
scene: BuiltScene::empty(),
418
frame_builder: FrameBuilder::new(),
419
output_pipelines: FastHashSet::default(),
420
hit_tester: None,
421
shared_hit_tester: Arc::new(SharedHitTester::new()),
422
dynamic_properties: SceneProperties::new(),
423
frame_is_valid: false,
424
hit_tester_is_valid: false,
425
rendered_frame_is_valid: false,
426
has_built_scene: false,
427
data_stores: DataStores::default(),
428
scratch: PrimitiveScratchBuffer::new(),
429
render_task_counters: RenderTaskGraphCounters::new(),
430
#[cfg(feature = "replay")]
431
loaded_scene: Scene::new(),
432
prev_composite_descriptor: CompositeDescriptor::empty(),
433
}
434
}
435
436
fn can_render(&self) -> bool {
437
self.scene.has_root_pipeline
438
}
439
440
fn has_pixels(&self) -> bool {
441
!self.view.device_rect.size.is_empty_or_negative()
442
}
443
444
fn process_frame_msg(
445
&mut self,
446
message: FrameMsg,
447
) -> DocumentOps {
448
match message {
449
FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
450
self.scene.pipeline_epochs.insert(pipeline_id, epoch);
451
}
452
FrameMsg::Scroll(delta, cursor) => {
453
profile_scope!("Scroll");
454
455
let node_index = match self.hit_tester {
456
Some(ref hit_tester) => {
457
// Ideally we would call self.scroll_nearest_scrolling_ancestor here, but
458
// we need have to avoid a double-borrow.
459
let test = HitTest::new(None, cursor, HitTestFlags::empty());
460
hit_tester.find_node_under_point(test)
461
}
462
None => {
463
None
464
}
465
};
466
467
if self.hit_tester.is_some()
468
&& self.scroll_nearest_scrolling_ancestor(delta, node_index) {
469
self.hit_tester_is_valid = false;
470
self.frame_is_valid = false;
471
}
472
473
return DocumentOps {
474
// TODO: Does it make sense to track this as a scrolling even if we
475
// ended up not scrolling anything?
476
scroll: true,
477
..DocumentOps::nop()
478
};
479
}
480
FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
481
if !self.hit_tester_is_valid {
482
self.rebuild_hit_tester();
483
}
484
485
let result = match self.hit_tester {
486
Some(ref hit_tester) => {
487
hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
488
}
489
None => HitTestResult { items: Vec::new() },
490
};
491
492
tx.send(result).unwrap();
493
}
494
FrameMsg::RequestHitTester(tx) => {
495
tx.send(self.shared_hit_tester.clone()).unwrap();
496
}
497
FrameMsg::SetPan(pan) => {
498
if self.view.pan != pan {
499
self.view.pan = pan;
500
self.hit_tester_is_valid = false;
501
self.frame_is_valid = false;
502
}
503
}
504
FrameMsg::ScrollNodeWithId(origin, id, clamp) => {
505
profile_scope!("ScrollNodeWithScrollId");
506
507
if self.scroll_node(origin, id, clamp) {
508
self.hit_tester_is_valid = false;
509
self.frame_is_valid = false;
510
}
511
512
return DocumentOps {
513
scroll: true,
514
..DocumentOps::nop()
515
};
516
}
517
FrameMsg::GetScrollNodeState(tx) => {
518
profile_scope!("GetScrollNodeState");
519
tx.send(self.scene.spatial_tree.get_scroll_node_state()).unwrap();
520
}
521
FrameMsg::UpdateDynamicProperties(property_bindings) => {
522
self.dynamic_properties.set_properties(property_bindings);
523
}
524
FrameMsg::AppendDynamicTransformProperties(property_bindings) => {
525
self.dynamic_properties.add_transforms(property_bindings);
526
}
527
FrameMsg::SetPinchZoom(factor) => {
528
if self.view.pinch_zoom_factor != factor.get() {
529
self.view.pinch_zoom_factor = factor.get();
530
self.frame_is_valid = false;
531
}
532
}
533
FrameMsg::SetIsTransformAsyncZooming(is_zooming, animation_id) => {
534
let node = self.scene.spatial_tree.spatial_nodes.iter_mut()
535
.find(|node| node.is_transform_bound_to_property(animation_id));
536
if let Some(node) = node {
537
if node.is_async_zooming != is_zooming {
538
node.is_async_zooming = is_zooming;
539
self.frame_is_valid = false;
540
}
541
}
542
}
543
}
544
545
DocumentOps::nop()
546
}
547
548
fn build_frame(
549
&mut self,
550
resource_cache: &mut ResourceCache,
551
gpu_cache: &mut GpuCache,
552
resource_profile: &mut ResourceProfileCounters,
553
debug_flags: DebugFlags,
554
tile_cache_logger: &mut TileCacheLogger,
555
) -> RenderedDocument {
556
let accumulated_scale_factor = self.view.accumulated_scale_factor();
557
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
558
559
// Advance to the next frame.
560
self.stamp.advance();
561
562
assert!(self.stamp.frame_id() != FrameId::INVALID,
563
"First frame increment must happen before build_frame()");
564
565
let frame = {
566
let frame = self.frame_builder.build(
567
&mut self.scene,
568
resource_cache,
569
gpu_cache,
570
self.stamp,
571
accumulated_scale_factor,
572
self.view.layer,
573
self.view.device_rect.origin,
574
pan,
575
resource_profile,
576
&self.dynamic_properties,
577
&mut self.data_stores,
578
&mut self.scratch,
579
&mut self.render_task_counters,
580
debug_flags,
581
tile_cache_logger,
582
);
583
584
frame
585
};
586
587
self.frame_is_valid = true;
588
589
let is_new_scene = self.has_built_scene;
590
self.has_built_scene = false;
591
592
RenderedDocument {
593
frame,
594
is_new_scene,
595
}
596
}
597
598
fn rebuild_hit_tester(&mut self) {
599
let accumulated_scale_factor = self.view.accumulated_scale_factor();
600
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
601
602
self.scene.spatial_tree.update_tree(
603
pan,
604
accumulated_scale_factor,
605
&self.dynamic_properties,
606
);
607
608
let hit_tester = Arc::new(self.scene.create_hit_tester(&self.data_stores.clip));
609
self.hit_tester = Some(Arc::clone(&hit_tester));
610
self.shared_hit_tester.update(hit_tester);
611
self.hit_tester_is_valid = true;
612
}
613
614
pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
615
let removed_pipelines = self.removed_pipelines.take_and_preallocate();
616
PipelineInfo {
617
epochs: self.scene.pipeline_epochs.iter()
618
.map(|(&pipeline_id, &epoch)| ((pipeline_id, self.id), epoch)).collect(),
619
removed_pipelines,
620
}
621
}
622
623
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
624
self.scene.spatial_tree
625
.discard_frame_state_for_pipeline(pipeline_id);
626
}
627
628
/// Returns true if any nodes actually changed position or false otherwise.
629
pub fn scroll_nearest_scrolling_ancestor(
630
&mut self,
631
scroll_location: ScrollLocation,
632
scroll_node_index: Option<SpatialNodeIndex>,
633
) -> bool {
634
self.scene.spatial_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index)
635
}
636
637
/// Returns true if the node actually changed position or false otherwise.
638
pub fn scroll_node(
639
&mut self,
640
origin: LayoutPoint,
641
id: ExternalScrollId,
642
clamp: ScrollClamping
643
) -> bool {
644
self.scene.spatial_tree.scroll_node(origin, id, clamp)
645
}
646
647
pub fn new_async_scene_ready(
648
&mut self,
649
built_scene: BuiltScene,
650
recycler: &mut Recycler,
651
) {
652
self.frame_is_valid = false;
653
self.hit_tester_is_valid = false;
654
655
// Give the old scene a chance to destroy any resources.
656
// Right now, all this does is build a hash map of any cached
657
// surface tiles, that can be provided to the next scene.
658
// TODO(nical) - It's a bit awkward how these retained tiles live
659
// in the scene's prim store then temporarily in the frame builder
660
// and then presumably back in the prim store during the next frame
661
// build.
662
let mut retained_tiles = RetainedTiles::new();
663
self.scene.prim_store.destroy(&mut retained_tiles);
664
let old_scrolling_states = self.scene.spatial_tree.drain();
665
666
self.scene = built_scene;
667
668
// Provide any cached tiles from the previous scene to
669
// the newly built one.
670
self.frame_builder.set_retained_resources(retained_tiles);
671
672
self.scratch.recycle(recycler);
673
674
self.scene.spatial_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
675
}
676
}
677
678
struct DocumentOps {
679
scroll: bool,
680
}
681
682
impl DocumentOps {
683
fn nop() -> Self {
684
DocumentOps {
685
scroll: false,
686
}
687
}
688
}
689
690
/// The unique id for WR resource identification.
691
/// The namespace_id should start from 1.
692
static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
693
694
#[cfg(any(feature = "capture", feature = "replay"))]
695
#[cfg_attr(feature = "capture", derive(Serialize))]
696
#[cfg_attr(feature = "replay", derive(Deserialize))]
697
struct PlainRenderBackend {
698
default_device_pixel_ratio: f32,
699
frame_config: FrameBuilderConfig,
700
documents: FastHashMap<DocumentId, DocumentView>,
701
resources: PlainResources,
702
}
703
704
/// The render backend is responsible for transforming high level display lists into
705
/// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
706
///
707
/// The render backend operates on its own thread.
708
pub struct RenderBackend {
709
api_rx: MsgReceiver<ApiMsg>,
710
payload_rx: Receiver<Payload>,
711
result_tx: Sender<ResultMsg>,
712
scene_tx: Sender<SceneBuilderRequest>,
713
low_priority_scene_tx: Sender<SceneBuilderRequest>,
714
scene_rx: Receiver<SceneBuilderResult>,
715
716
payload_buffer: Vec<Payload>,
717
718
default_device_pixel_ratio: f32,
719
720
gpu_cache: GpuCache,
721
resource_cache: ResourceCache,
722
723
frame_config: FrameBuilderConfig,
724
default_compositor_kind: CompositorKind,
725
documents: FastHashMap<DocumentId, Document>,
726
727
notifier: Box<dyn RenderNotifier>,
728
recorder: Option<Box<dyn ApiRecordingReceiver>>,
729
logrecorder: Option<Box<LogRecorder>>,
730
tile_cache_logger: TileCacheLogger,
731
sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
732
size_of_ops: Option<MallocSizeOfOps>,
733
debug_flags: DebugFlags,
734
namespace_alloc_by_client: bool,
735
736
recycler: Recycler,
737
}
738
739
impl RenderBackend {
740
pub fn new(
741
api_rx: MsgReceiver<ApiMsg>,
742
payload_rx: Receiver<Payload>,
743
result_tx: Sender<ResultMsg>,
744
scene_tx: Sender<SceneBuilderRequest>,
745
low_priority_scene_tx: Sender<SceneBuilderRequest>,
746
scene_rx: Receiver<SceneBuilderResult>,
747
default_device_pixel_ratio: f32,
748
resource_cache: ResourceCache,
749
notifier: Box<dyn RenderNotifier>,
750
frame_config: FrameBuilderConfig,
751
recorder: Option<Box<dyn ApiRecordingReceiver>>,
752
sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
753
size_of_ops: Option<MallocSizeOfOps>,
754
debug_flags: DebugFlags,
755
namespace_alloc_by_client: bool,
756
) -> RenderBackend {
757
RenderBackend {
758
api_rx,
759
payload_rx,
760
result_tx,
761
scene_tx,
762
low_priority_scene_tx,
763
scene_rx,
764
payload_buffer: Vec::new(),
765
default_device_pixel_ratio,
766
resource_cache,
767
gpu_cache: GpuCache::new(),
768
frame_config,
769
default_compositor_kind : frame_config.compositor_kind,
770
documents: FastHashMap::default(),
771
notifier,
772
recorder,
773
logrecorder: None,
774
tile_cache_logger: TileCacheLogger::new(500usize),
775
sampler,
776
size_of_ops,
777
debug_flags,
778
namespace_alloc_by_client,
779
recycler: Recycler::new(),
780
}
781
}
782
783
fn process_scene_msg(
784
&mut self,
785
document_id: DocumentId,
786
message: SceneMsg,
787
frame_counter: u32,
788
txn: &mut Transaction,
789
) {
790
let doc = self.documents.get_mut(&document_id).expect("No document?");
791
792
match message {
793
SceneMsg::UpdateEpoch(pipeline_id, epoch) => {
794
txn.epoch_updates.push((pipeline_id, epoch));
795
}
796
SceneMsg::SetPageZoom(factor) => {
797
doc.view.page_zoom_factor = factor.get();
798
}
799
SceneMsg::SetQualitySettings { settings } => {
800
doc.view.quality_settings = settings;
801
}
802
SceneMsg::SetDocumentView {
803
device_rect,
804
device_pixel_ratio,
805
} => {
806
doc.view.device_rect = device_rect;
807
doc.view.device_pixel_ratio = device_pixel_ratio;
808
}
809
SceneMsg::SetDisplayList {
810
epoch,
811
pipeline_id,
812
background,
813
viewport_size,
814
content_size,
815
list_descriptor,
816
preserve_frame_state,
817
} => {
818
profile_scope!("SetDisplayList");
819
820
let data = if let Some(idx) = self.payload_buffer.iter().position(|data|
821
data.epoch == epoch && data.pipeline_id == pipeline_id
822
) {
823
self.payload_buffer.swap_remove(idx)
824
} else {
825
loop {
826
let data = self.payload_rx.recv().unwrap();
827
if data.epoch == epoch && data.pipeline_id == pipeline_id {
828
break data;
829
} else {
830
self.payload_buffer.push(data);
831
}
832
}
833
};
834
835
if let Some(ref mut r) = self.recorder {
836
r.write_payload(frame_counter, &data.to_data());
837
}
838
839
let built_display_list =
840
BuiltDisplayList::from_data(data.display_list_data, list_descriptor);
841
842
if !preserve_frame_state {
843
doc.discard_frame_state_for_pipeline(pipeline_id);
844
}
845
846
let display_list_len = built_display_list.data().len();
847
let (builder_start_time_ns, builder_end_time_ns, send_time_ns) =
848
built_display_list.times();
849
850
txn.display_list_updates.push(DisplayListUpdate {
851
built_display_list,
852
pipeline_id,
853
epoch,
854
background,
855
viewport_size,
856
content_size,
857
timings: TransactionTimings {
858
builder_start_time_ns,
859
builder_end_time_ns,
860
send_time_ns,
861
scene_build_start_time_ns: 0,
862
scene_build_end_time_ns: 0,
863
blob_rasterization_end_time_ns: 0,
864
display_list_len,
865
},
866
});
867
}
868
SceneMsg::SetRootPipeline(pipeline_id) => {
869
profile_scope!("SetRootPipeline");
870
txn.set_root_pipeline = Some(pipeline_id);
871
}
872
SceneMsg::RemovePipeline(pipeline_id) => {
873
profile_scope!("RemovePipeline");
874
txn.removed_pipelines.push((pipeline_id, document_id));
875
}
876
SceneMsg::EnableFrameOutput(pipeline_id, enable) => {
877
if enable {
878
doc.output_pipelines.insert(pipeline_id);
879
} else {
880
doc.output_pipelines.remove(&pipeline_id);
881
}
882
}
883
}
884
}
885
886
fn next_namespace_id(&self) -> IdNamespace {
887
IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
888
}
889
890
pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
891
let mut frame_counter: u32 = 0;
892
let mut status = RenderBackendStatus::Continue;
893
894
if let Some(ref sampler) = self.sampler {
895
sampler.register();
896
}
897
898
while let RenderBackendStatus::Continue = status {
899
profile_scope!("handle_msg");
900
901
while let Ok(msg) = self.scene_rx.try_recv() {
902
match msg {
903
SceneBuilderResult::Transactions(mut txns, result_tx) => {
904
self.prepare_for_frames();
905
self.maybe_force_nop_documents(
906
&mut frame_counter,
907
&mut profile_counters,
908
|document_id| txns.iter().any(|txn| txn.document_id == document_id));
909
910
for mut txn in txns.drain(..) {
911
let has_built_scene = txn.built_scene.is_some();
912
913
if let Some(timings) = txn.timings {
914
if has_built_scene {
915
profile_counters.scene_changed = true;
916
}
917
918
profile_counters.txn.set(
919
timings.builder_start_time_ns,
920
timings.builder_end_time_ns,
921
timings.send_time_ns,
922
timings.scene_build_start_time_ns,
923
timings.scene_build_end_time_ns,
924
timings.display_list_len,
925
);
926
}
927
928
if let Some(doc) = self.documents.get_mut(&txn.document_id) {
929
930
doc.removed_pipelines.append(&mut txn.removed_pipelines);
931
932
if let Some(built_scene) = txn.built_scene.take() {
933
doc.new_async_scene_ready(
934
built_scene,
935
&mut self.recycler,
936
);
937
}
938
939
// If there are any additions or removals of clip modes
940
// during the scene build, apply them to the data store now.
941
// This needs to happen before we build the hit tester.
942
if let Some(updates) = txn.interner_updates.take() {
943
#[cfg(feature = "capture")]
944
{
945
if self.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) {
946
self.tile_cache_logger.serialize_updates(&updates);
947
}
948
}
949
doc.data_stores.apply_updates(updates, &mut profile_counters);
950
}
951
952
953
// Build the hit tester while the APZ lock is held so that its content
954
// is in sync with the gecko APZ tree.
955
if !doc.hit_tester_is_valid {
956
doc.rebuild_hit_tester();
957
}
958
if let Some(ref tx) = result_tx {
959
let (resume_tx, resume_rx) = channel();
960
tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
961
// Block until the post-swap hook has completed on
962
// the scene builder thread. We need to do this before
963
// we can sample from the sampler hook which might happen
964
// in the update_document call below.
965
resume_rx.recv().ok();
966
}
967
} else {
968
// The document was removed while we were building it, skip it.
969
// TODO: we might want to just ensure that removed documents are
970
// always forwarded to the scene builder thread to avoid this case.
971
if let Some(ref tx) = result_tx {
972
tx.send(SceneSwapResult::Aborted).unwrap();
973
}
974
continue;
975
}
976
977
self.resource_cache.add_rasterized_blob_images(
978
txn.rasterized_blobs.take(),
979
&mut profile_counters.resources.texture_cache,
980
);
981
if let Some((rasterizer, info)) = txn.blob_rasterizer.take() {
982
self.resource_cache.set_blob_rasterizer(rasterizer, info);
983
}
984
985
self.update_document(
986
txn.document_id,
987
txn.resource_updates.take(),
988
txn.frame_ops.take(),
989
txn.notifications.take(),
990
txn.render_frame,
991
txn.invalidate_rendered_frame,
992
&mut frame_counter,
993
&mut profile_counters,
994
has_built_scene,
995
);
996
}
997
self.bookkeep_after_frames();
998
},
999
SceneBuilderResult::FlushComplete(tx) => {
1000
tx.send(()).ok();
1001
}
1002
SceneBuilderResult::ExternalEvent(evt) => {
1003
self.notifier.external_event(evt);
1004
}
1005
SceneBuilderResult::ClearNamespace(id) => {
1006
self.resource_cache.clear_namespace(id);
1007
self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id);
1008
}
1009
SceneBuilderResult::Stopped => {
1010
panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
1011
}
1012
SceneBuilderResult::DocumentsForDebugger(json) => {
1013
let msg = ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json));
1014
self.result_tx.send(msg).unwrap();
1015
self.notifier.wake_up();
1016
}
1017
}
1018
}
1019
1020
status = match self.api_rx.recv() {
1021
Ok(msg) => {
1022
if let Some(ref mut r) = self.logrecorder {
1023
r.write_msg(frame_counter, &msg);
1024
}
1025
1026
1027
if let Some(ref mut r) = self.recorder {
1028
r.write_msg(frame_counter, &msg);
1029
}
1030
self.process_api_msg(msg, &mut profile_counters, &mut frame_counter)
1031
}
1032
Err(..) => { RenderBackendStatus::ShutDown(None) }
1033
};
1034
}
1035
1036
let _ = self.low_priority_scene_tx.send(SceneBuilderRequest::Stop);
1037
// Ensure we read everything the scene builder is sending us from
1038
// inflight messages, otherwise the scene builder might panic.
1039
while let Ok(msg) = self.scene_rx.recv() {
1040
match msg {
1041
SceneBuilderResult::FlushComplete(tx) => {
1042
// If somebody's blocked waiting for a flush, how did they
1043
// trigger the RB thread to shut down? This shouldn't happen
1044
// but handle it gracefully anyway.
1045
debug_assert!(false);
1046
tx.send(()).ok();
1047
}
1048
SceneBuilderResult::Stopped => break,
1049
_ => continue,
1050
}
1051
}
1052
1053
self.documents.clear();
1054
1055
self.notifier.shut_down();
1056
1057
if let Some(ref sampler) = self.sampler {
1058
sampler.deregister();
1059
}
1060
1061
1062
if let RenderBackendStatus::ShutDown(Some(sender)) = status {
1063
let _ = sender.send(());
1064
}
1065
}
1066
1067
fn process_api_msg(
1068
&mut self,
1069
msg: ApiMsg,
1070
profile_counters: &mut BackendProfileCounters,
1071
frame_counter: &mut u32,
1072
) -> RenderBackendStatus {
1073
match msg {
1074
ApiMsg::WakeUp => {}
1075
ApiMsg::WakeSceneBuilder => {
1076
self.scene_tx.send(SceneBuilderRequest::WakeUp).unwrap();
1077
}
1078
ApiMsg::FlushSceneBuilder(tx) => {
1079
self.low_priority_scene_tx.send(SceneBuilderRequest::Flush(tx)).unwrap();
1080
}
1081
ApiMsg::UpdateResources(mut updates) => {
1082
self.resource_cache.pre_scene_building_update(
1083
&mut updates,
1084
&mut profile_counters.resources
1085
);
1086
self.resource_cache.post_scene_building_update(
1087
updates,
1088
&mut profile_counters.resources
1089
);
1090
}
1091
ApiMsg::GetGlyphDimensions(instance_key, glyph_indices, tx) => {
1092
let mut glyph_dimensions = Vec::with_capacity(glyph_indices.len());
1093
if let Some(base) = self.resource_cache.get_font_instance(instance_key) {
1094
let font = FontInstance::from_base(Arc::clone(&base));
1095
for glyph_index in &glyph_indices {
1096
let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index);
1097
glyph_dimensions.push(glyph_dim);
1098
}
1099
}
1100
tx.send(glyph_dimensions).unwrap();
1101
}
1102
ApiMsg::GetGlyphIndices(font_key, text, tx) => {
1103
let mut glyph_indices = Vec::new();
1104
for ch in text.chars() {
1105
let index = self.resource_cache.get_glyph_index(font_key, ch);
1106
glyph_indices.push(index);
1107
}
1108
tx.send(glyph_indices).unwrap();
1109
}
1110
ApiMsg::CloneApi(sender) => {
1111
assert!(!self.namespace_alloc_by_client);
1112
sender.send(self.next_namespace_id()).unwrap();
1113
}
1114
ApiMsg::CloneApiByClient(namespace_id) => {
1115
assert!(self.namespace_alloc_by_client);
1116
debug_assert!(!self.documents.iter().any(|(did, _doc)| did.namespace_id == namespace_id));
1117
}
1118
ApiMsg::AddDocument(document_id, initial_size, layer) => {
1119
let document = Document::new(
1120
document_id,
1121
initial_size,
1122
layer,
1123
self.default_device_pixel_ratio,
1124
);
1125
let old = self.documents.insert(document_id, document);
1126
debug_assert!(old.is_none());
1127
}
1128
ApiMsg::DeleteDocument(document_id) => {
1129
self.documents.remove(&document_id);
1130
self.low_priority_scene_tx.send(
1131
SceneBuilderRequest::DeleteDocument(document_id)
1132
).unwrap();
1133
}
1134
ApiMsg::ExternalEvent(evt) => {
1135
self.low_priority_scene_tx.send(SceneBuilderRequest::ExternalEvent(evt)).unwrap();
1136
}
1137
ApiMsg::ClearNamespace(id) => {
1138
self.low_priority_scene_tx.send(SceneBuilderRequest::ClearNamespace(id)).unwrap();
1139
}
1140
ApiMsg::MemoryPressure => {
1141
// This is drastic. It will basically flush everything out of the cache,
1142
// and the next frame will have to rebuild all of its resources.
1143
// We may want to look into something less extreme, but on the other hand this
1144
// should only be used in situations where are running low enough on memory
1145
// that we risk crashing if we don't do something about it.
1146
// The advantage of clearing the cache completely is that it gets rid of any
1147
// remaining fragmentation that could have persisted if we kept around the most
1148
// recently used resources.
1149
self.resource_cache.clear(ClearCache::all());
1150
1151
self.gpu_cache.clear();
1152
1153
let resource_updates = self.resource_cache.pending_updates();
1154
let msg = ResultMsg::UpdateResources {
1155
resource_updates,
1156
memory_pressure: true,
1157
};
1158
self.result_tx.send(msg).unwrap();
1159
self.notifier.wake_up();
1160
}
1161
ApiMsg::ReportMemory(tx) => {
1162
self.report_memory(tx);
1163
}
1164
ApiMsg::DebugCommand(option) => {
1165
let msg = match option {
1166
DebugCommand::EnableDualSourceBlending(enable) => {
1167
// Set in the config used for any future documents
1168
// that are created.
1169
self.frame_config
1170
.dual_source_blending_is_enabled = enable;
1171
self.update_frame_builder_config();
1172
1173
// We don't want to forward this message to the renderer.
1174
return RenderBackendStatus::Continue;
1175
}
1176
DebugCommand::SetPictureTileSize(tile_size) => {
1177
self.frame_config.tile_size_override = tile_size;
1178
self.update_frame_builder_config();
1179
1180
return RenderBackendStatus::Continue;
1181
}
1182
DebugCommand::FetchDocuments => {
1183
// Ask SceneBuilderThread to send JSON presentation of the documents,
1184
// that will be forwarded to Renderer.
1185
self.scene_tx.send(SceneBuilderRequest::DocumentsForDebugger).unwrap();
1186
return RenderBackendStatus::Continue;
1187
}
1188
DebugCommand::FetchClipScrollTree => {
1189
let json = self.get_spatial_tree_for_debugger();
1190
ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json))
1191
}
1192
#[cfg(feature = "capture")]
1193
DebugCommand::SaveCapture(root, bits) => {
1194
let output = self.save_capture(root, bits, profile_counters);
1195
ResultMsg::DebugOutput(output)
1196
},
1197
#[cfg(feature = "replay")]
1198
DebugCommand::LoadCapture(root, tx) => {
1199
NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
1200
*frame_counter += 1;
1201
1202
self.load_capture(&root, profile_counters);
1203
1204
for (id, doc) in &self.documents {
1205
let captured = CapturedDocument {
1206
document_id: *id,
1207
root_pipeline_id: doc.loaded_scene.root_pipeline_id,
1208
};
1209
tx.send(captured).unwrap();
1210
1211
// notify the active recorder
1212
if let Some(ref mut r) = self.recorder {
1213
let pipeline_id = doc.loaded_scene.root_pipeline_id.unwrap();
1214
let epoch = doc.loaded_scene.pipeline_epochs[&pipeline_id];
1215
let pipeline = &doc.loaded_scene.pipelines[&pipeline_id];
1216
let scene_msg = SceneMsg::SetDisplayList {
1217
list_descriptor: pipeline.display_list.descriptor().clone(),
1218
epoch,
1219
pipeline_id,
1220
background: pipeline.background_color,
1221
viewport_size: pipeline.viewport_size,
1222
content_size: pipeline.content_size,
1223
preserve_frame_state: false,
1224
};
1225
let txn = TransactionMsg::scene_message(scene_msg);
1226
r.write_msg(*frame_counter, &ApiMsg::UpdateDocuments(vec![*id], vec![txn]));
1227
r.write_payload(*frame_counter, &Payload::construct_data(
1228
epoch,
1229
pipeline_id,
1230
pipeline.display_list.data(),
1231
));
1232
}
1233
}
1234
1235
// Note: we can't pass `LoadCapture` here since it needs to arrive
1236
// before the `PublishDocument` messages sent by `load_capture`.
1237
return RenderBackendStatus::Continue;
1238
}
1239
DebugCommand::SetTransactionLogging(value) => {
1240
match (value, self.logrecorder.as_ref()) {
1241
(true, None) => {
1242
let current_time = time::now_utc().to_local();
1243
let name = format!("wr-log-{}.log",
1244
current_time.strftime("%Y%m%d_%H%M%S").unwrap()
1245
);
1246
self.logrecorder = LogRecorder::new(&PathBuf::from(name));
1247
},
1248
(false, _) => self.logrecorder = None,
1249
_ => (),
1250
};
1251
1252
return RenderBackendStatus::Continue;
1253
}
1254
DebugCommand::ClearCaches(mask) => {
1255
self.resource_cache.clear(mask);
1256
return RenderBackendStatus::Continue;
1257
}
1258
DebugCommand::EnableNativeCompositor(enable) => {
1259
// Default CompositorKind should be Native
1260
if let CompositorKind::Draw { .. } = self.default_compositor_kind {
1261
unreachable!();
1262
}
1263
1264
let compositor_kind = if enable {
1265
self.default_compositor_kind
1266
} else {
1267
CompositorKind::default()
1268
};
1269
1270
for (_, doc) in &mut self.documents {
1271
doc.scene.config.compositor_kind = compositor_kind;
1272
doc.frame_is_valid = false;
1273
}
1274
1275
self.frame_config.compositor_kind = compositor_kind;
1276
self.update_frame_builder_config();
1277
1278
// We don't want to forward this message to the renderer.
1279
return RenderBackendStatus::Continue;
1280
}
1281
DebugCommand::EnableMultithreading(enable) => {
1282
self.resource_cache.enable_multithreading(enable);
1283
return RenderBackendStatus::Continue;
1284
}
1285
DebugCommand::SetBatchingLookback(count) => {
1286
self.frame_config.batch_lookback_count = count as usize;
1287
self.update_frame_builder_config();
1288
1289
return RenderBackendStatus::Continue;
1290
}
1291
DebugCommand::SimulateLongSceneBuild(time_ms) => {
1292
self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)).unwrap();
1293
return RenderBackendStatus::Continue;
1294
}
1295
DebugCommand::SimulateLongLowPrioritySceneBuild(time_ms) => {
1296
self.low_priority_scene_tx.send(
1297
SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(time_ms)
1298
).unwrap();
1299
return RenderBackendStatus::Continue;
1300
}
1301
DebugCommand::SetFlags(flags) => {
1302
self.resource_cache.set_debug_flags(flags);
1303
self.gpu_cache.set_debug_flags(flags);
1304
1305
// If we're toggling on the GPU cache debug display, we
1306
// need to blow away the cache. This is because we only
1307
// send allocation/free notifications to the renderer
1308
// thread when the debug display is enabled, and thus
1309
// enabling it when the cache is partially populated will
1310
// give the renderer an incomplete view of the world.
1311
// And since we might as well drop all the debugging state
1312
// from the renderer when we disable the debug display,
1313
// we just clear the cache on toggle.
1314
let changed = self.debug_flags ^ flags;
1315
if changed.contains(DebugFlags::GPU_CACHE_DBG) {
1316
self.gpu_cache.clear();
1317
}
1318
self.debug_flags = flags;
1319
1320
ResultMsg::DebugCommand(option)
1321
}
1322
_ => ResultMsg::DebugCommand(option),
1323
};
1324
self.result_tx.send(msg).unwrap();
1325
self.notifier.wake_up();
1326
}
1327
ApiMsg::ShutDown(sender) => {
1328
info!("Recycling stats: {:?}", self.recycler);
1329
return RenderBackendStatus::ShutDown(sender);
1330
}
1331
ApiMsg::UpdateDocuments(document_ids, transaction_msgs) => {
1332
self.prepare_transactions(
1333
document_ids,
1334
transaction_msgs,
1335
frame_counter,
1336
profile_counters,
1337
);
1338
}
1339
}
1340
1341
RenderBackendStatus::Continue
1342
}
1343
1344
fn update_frame_builder_config(&self) {
1345
self.low_priority_scene_tx.send(SceneBuilderRequest::SetFrameBuilderConfig(
1346
self.frame_config.clone()
1347
)).unwrap();
1348
}
1349
1350
fn prepare_for_frames(&mut self) {
1351
self.resource_cache.prepare_for_frames(SystemTime::now());
1352
self.gpu_cache.prepare_for_frames();
1353
}
1354
1355
fn bookkeep_after_frames(&mut self) {
1356
self.resource_cache.bookkeep_after_frames();
1357
self.gpu_cache.bookkeep_after_frames();
1358
}
1359
1360
fn requires_frame_build(&mut self) -> bool {
1361
self.resource_cache.requires_frame_build() || self.gpu_cache.requires_frame_build()
1362
}
1363
1364
fn prepare_transactions(
1365
&mut self,
1366
document_ids: Vec<DocumentId>,
1367
mut transaction_msgs: Vec<TransactionMsg>,
1368
frame_counter: &mut u32,
1369
profile_counters: &mut BackendProfileCounters,
1370
) {
1371
let mut use_scene_builder = transaction_msgs.iter()
1372
.any(|transaction_msg| transaction_msg.use_scene_builder_thread);
1373
let use_high_priority = transaction_msgs.iter()
1374
.any(|transaction_msg| !transaction_msg.low_priority);
1375
1376
let mut txns : Vec<Box<Transaction>> = document_ids.iter().zip(transaction_msgs.drain(..))
1377
.map(|(&document_id, mut transaction_msg)| {
1378
let mut txn = Box::new(Transaction {
1379
document_id,
1380
display_list_updates: Vec::new(),
1381
removed_pipelines: Vec::new(),
1382
epoch_updates: Vec::new(),
1383
request_scene_build: None,
1384
blob_rasterizer: None,
1385
blob_requests: Vec::new(),
1386
resource_updates: transaction_msg.resource_updates,
1387
frame_ops: transaction_msg.frame_ops,
1388
rasterized_blobs: Vec::new(),
1389
notifications: transaction_msg.notifications,
1390
set_root_pipeline: None,
1391
render_frame: transaction_msg.generate_frame,
1392
invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame,
1393
});
1394
1395
self.resource_cache.pre_scene_building_update(
1396
&mut txn.resource_updates,
1397
&mut profile_counters.resources,
1398
);
1399
1400
for scene_msg in transaction_msg.scene_ops.drain(..) {
1401
let _timer = profile_counters.total_time.timer();
1402
self.process_scene_msg(
1403
document_id,
1404
scene_msg,
1405
*frame_counter,
1406
&mut txn,
1407
)
1408
}
1409
1410
let blobs_to_rasterize = get_blob_image_updates(&txn.resource_updates);
1411
if !blobs_to_rasterize.is_empty() {
1412
let (blob_rasterizer, blob_requests) = self.resource_cache
1413
.create_blob_scene_builder_requests(&blobs_to_rasterize);
1414
1415
txn.blob_requests = blob_requests;
1416
txn.blob_rasterizer = blob_rasterizer;
1417
}
1418
txn
1419
}).collect();
1420
1421
use_scene_builder = use_scene_builder || txns.iter().any(|txn| {
1422
!txn.can_skip_scene_builder() || txn.blob_rasterizer.is_some()
1423
});
1424
1425
if use_scene_builder {
1426
for txn in txns.iter_mut() {
1427
let doc = self.documents.get_mut(&txn.document_id).unwrap();
1428
1429
if txn.should_build_scene() {
1430
txn.request_scene_build = Some(SceneRequest {
1431
view: doc.view.clone(),
1432
font_instances: self.resource_cache.get_font_instances(),
1433
output_pipelines: doc.output_pipelines.clone(),
1434
});
1435
}
1436
}
1437
} else {
1438
self.prepare_for_frames();
1439
self.maybe_force_nop_documents(
1440
frame_counter,
1441
profile_counters,
1442
|document_id| txns.iter().any(|txn| txn.document_id == document_id));
1443
1444
for mut txn in txns {
1445
self.update_document(
1446
txn.document_id,
1447
txn.resource_updates.take(),
1448
txn.frame_ops.take(),
1449
txn.notifications.take(),
1450
txn.render_frame,
1451
txn.invalidate_rendered_frame,
1452
frame_counter,
1453
profile_counters,
1454
false
1455
);
1456
}
1457
1458
self.bookkeep_after_frames();
1459
return;
1460
}
1461
1462
let tx = if use_high_priority {
1463
&self.scene_tx
1464
} else {
1465
&self.low_priority_scene_tx
1466
};
1467
1468
tx.send(SceneBuilderRequest::Transactions(txns)).unwrap();
1469
}
1470
1471
/// In certain cases, resources shared by multiple documents have to run
1472
/// maintenance operations, like cleaning up unused cache items. In those
1473
/// cases, we are forced to build frames for all documents, however we
1474
/// may not have a transaction ready for every document - this method
1475
/// calls update_document with the details of a fake, nop transaction just
1476
/// to force a frame build.
1477
fn maybe_force_nop_documents<F>(&mut self,
1478
frame_counter: &mut u32,
1479
profile_counters: &mut BackendProfileCounters,
1480
document_already_present: F) where
1481
F: Fn(DocumentId) -> bool {
1482
if self.requires_frame_build() {
1483
let nop_documents : Vec<DocumentId> = self.documents.keys()
1484
.cloned()
1485
.filter(|key| !document_already_present(*key))
1486
.collect();
1487
for &document_id in &nop_documents {
1488
self.update_document(
1489
document_id,
1490
Vec::default(),
1491
Vec::default(),
1492
Vec::default(),
1493
false,
1494
false,
1495
frame_counter,
1496
profile_counters,
1497
false);
1498
}
1499
}
1500
}
1501
1502
fn update_document(
1503
&mut self,
1504
document_id: DocumentId,
1505
resource_updates: Vec<ResourceUpdate>,
1506
mut frame_ops: Vec<FrameMsg>,
1507
mut notifications: Vec<NotificationRequest>,
1508
mut render_frame: bool,
1509
invalidate_rendered_frame: bool,
1510
frame_counter: &mut u32,
1511
profile_counters: &mut BackendProfileCounters,
1512
has_built_scene: bool,
1513
) {
1514
let requested_frame = render_frame;
1515
1516
let requires_frame_build = self.requires_frame_build();
1517
let doc = self.documents.get_mut(&document_id).unwrap();
1518
// If we have a sampler, get more frame ops from it and add them
1519
// to the transaction. This is a hook to allow the WR user code to
1520
// fiddle with things after a potentially long scene build, but just
1521
// before rendering. This is useful for rendering with the latest
1522
// async transforms.
1523
if requested_frame || has_built_scene {
1524
if let Some(ref sampler) = self.sampler {
1525
frame_ops.append(&mut sampler.sample(document_id,
1526
&doc.scene.pipeline_epochs));
1527
}
1528
}
1529
1530
doc.has_built_scene |= has_built_scene;
1531
1532
// TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
1533
// for something wrench specific and we should remove it.
1534
let mut scroll = false;
1535
for frame_msg in frame_ops {
1536
let _timer = profile_counters.total_time.timer();
1537
let op = doc.process_frame_msg(frame_msg);
1538
scroll |= op.scroll;
1539
}
1540
1541
for update in &resource_updates {
1542
if let ResourceUpdate::UpdateImage(..) = update {
1543
doc.frame_is_valid = false;
1544
}
1545
}
1546
1547
self.resource_cache.post_scene_building_update(
1548
resource_updates,
1549
&mut profile_counters.resources,
1550
);
1551
1552
if doc.dynamic_properties.flush_pending_updates() {
1553
doc.frame_is_valid = false;
1554
doc.hit_tester_is_valid = false;
1555
}
1556
1557
if !doc.can_render() {
1558
// TODO: this happens if we are building the first scene asynchronously and
1559
// scroll at the same time. we should keep track of the fact that we skipped
1560
// composition here and do it as soon as we receive the scene.
1561
render_frame = false;
1562
}
1563
1564
// Avoid re-building the frame if the current built frame is still valid.
1565
// However, if the resource_cache requires a frame build, _always_ do that, unless
1566
// doc.can_render() is false, as in that case a frame build can't happen anyway.
1567
// We want to ensure we do this because even if the doc doesn't have pixels it
1568
// can still try to access stale texture cache items.
1569
let build_frame = (render_frame && !doc.frame_is_valid && doc.has_pixels()) ||
1570
(requires_frame_build && doc.can_render());
1571
1572
// Request composite is true when we want to composite frame even when
1573
// there is no frame update. This happens when video frame is updated under
1574
// external image with NativeTexture or when platform requested to composite frame.
1575
if invalidate_rendered_frame {
1576
doc.rendered_frame_is_valid = false;
1577
if let CompositorKind::Draw { max_partial_present_rects } = doc.scene.config.compositor_kind {
1578
// When partial present is enabled, we need to force redraw.
1579
if max_partial_present_rects > 0 {
1580
let msg = ResultMsg::ForceRedraw;
1581
self.result_tx.send(msg).unwrap();
1582
}
1583
}
1584
}
1585
1586
let mut frame_build_time = None;
1587
if build_frame {
1588
profile_scope!("generate frame");
1589
1590
*frame_counter += 1;
1591
doc.rendered_frame_is_valid = false;
1592
1593
// borrow ck hack for profile_counters
1594
let (pending_update, rendered_document) = {
1595
let _timer = profile_counters.total_time.timer();
1596
let frame_build_start_time = precise_time_ns();
1597
1598
let rendered_document = doc.build_frame(
1599
&mut self.resource_cache,
1600
&mut self.gpu_cache,
1601
&mut profile_counters.resources,
1602
self.debug_flags,
1603
&mut self.tile_cache_logger,
1604
);
1605
1606
debug!("generated frame for document {:?} with {} passes",
1607
document_id, rendered_document.frame.passes.len());
1608
1609
let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
1610
self.result_tx.send(msg).unwrap();
1611
1612
frame_build_time = Some(precise_time_ns() - frame_build_start_time);
1613
1614
let pending_update = self.resource_cache.pending_updates();
1615
(pending_update, rendered_document)
1616
};
1617
1618
// Build a small struct that represents the state of the tiles to be composited.
1619
let composite_descriptor = rendered_document
1620
.frame
1621
.composite_state
1622
.descriptor
1623
.clone();
1624
1625
// If there are no texture cache updates to apply, and if the produced
1626
// frame is a no-op, and the compositor state is equal, then we can skip
1627
// compositing this frame completely.
1628
if pending_update.is_nop() &&
1629
rendered_document.frame.is_nop() &&
1630
composite_descriptor == doc.prev_composite_descriptor {
1631
doc.rendered_frame_is_valid = true;
1632
}
1633
doc.prev_composite_descriptor = composite_descriptor;
1634
1635
let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1636
self.result_tx.send(msg).unwrap();
1637
1638
// Publish the frame
1639
let msg = ResultMsg::PublishDocument(
1640
document_id,
1641
rendered_document,
1642
pending_update,
1643
profile_counters.clone()
1644
);
1645
self.result_tx.send(msg).unwrap();
1646
profile_counters.reset();
1647
} else if requested_frame {
1648
// WR-internal optimization to avoid doing a bunch of render work if
1649
// there's no pixels. We still want to pretend to render and request
1650
// a render to make sure that the callbacks (particularly the
1651
// new_frame_ready callback below) has the right flags.
1652
let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
1653
self.result_tx.send(msg).unwrap();
1654
}
1655
1656
drain_filter(
1657
&mut notifications,
1658
|n| { n.when() == Checkpoint::FrameBuilt },
1659
|n| { n.notify(); },
1660
);
1661
1662
if !notifications.is_empty() {
1663
self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap();
1664
}
1665
1666
// Always forward the transaction to the renderer if a frame was requested,
1667
// otherwise gecko can get into a state where it waits (forever) for the
1668
// transaction to complete before sending new work.
1669
if requested_frame {
1670
// If rendered frame is already valid, there is no need to render frame.
1671
if doc.rendered_frame_is_valid {
1672
render_frame = false;
1673
} else if render_frame {
1674
doc.rendered_frame_is_valid = true;
1675
}
1676
self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time);
1677
}
1678
1679
if !doc.hit_tester_is_valid {
1680
doc.rebuild_hit_tester();
1681
}
1682
}
1683
1684
#[cfg(not(feature = "debugger"))]
1685
fn get_spatial_tree_for_debugger(&self) -> String {
1686
String::new()
1687
}
1688
1689
#[cfg(feature = "debugger")]
1690
fn get_spatial_tree_for_debugger(&self) -> String {
1691
use crate::print_tree::PrintableTree;
1692
1693
let mut debug_root = debug_server::SpatialTreeList::new();
1694
1695
for (_, doc) in &self.documents {
1696
let debug_node = debug_server::TreeNode::new("document spatial tree");
1697
let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
1698
1699
doc.scene.spatial_tree.print_with(&mut builder);
1700
1701
debug_root.add(builder.build());
1702
}
1703
1704
serde_json::to_string(&debug_root).unwrap()
1705
}
1706
1707
fn report_memory(&mut self, tx: MsgSender<Box<MemoryReport>>) {
1708
let mut report = Box::new(MemoryReport::default());
1709
let ops = self.size_of_ops.as_mut().unwrap();
1710
let op = ops.size_of_op;
1711
report.gpu_cache_metadata = self.gpu_cache.size_of(ops);
1712
for doc in self.documents.values() {
1713
report.clip_stores += doc.scene.clip_store.size_of(ops);
1714
report.hit_testers += match &doc.hit_tester {
1715
Some(hit_tester) => hit_tester.size_of(ops),
1716
None => 0,
1717
};
1718
1719
doc.data_stores.report_memory(ops, &mut report)
1720
}
1721
1722
(*report) += self.resource_cache.report_memory(op);
1723
1724
// Send a message to report memory on the scene-builder thread, which
1725
// will add its report to this one and send the result back to the original
1726
// thread waiting on the request.
1727
self.scene_tx.send(SceneBuilderRequest::ReportMemory(report, tx)).unwrap();
1728
}
1729
}
1730
1731
fn get_blob_image_updates(updates: &[ResourceUpdate]) -> Vec<BlobImageKey> {
1732
let mut requests = Vec::new();
1733
for update in updates {
1734
match *update {
1735
ResourceUpdate::AddBlobImage(ref img) => {
1736
requests.push(img.key);
1737
}
1738
ResourceUpdate::UpdateBlobImage(ref img) => {
1739
requests.push(img.key);
1740
}
1741
ResourceUpdate::SetBlobImageVisibleArea(key, ..) => {
1742
requests.push(key);
1743
}
1744
_ => {}
1745
}
1746
}
1747
1748
requests
1749
}
1750
1751
impl RenderBackend {
1752
#[cfg(feature = "capture")]
1753
// Note: the mutable `self` is only needed here for resolving blob images
1754
fn save_capture(
1755
&mut self,
1756
root: PathBuf,
1757
bits: CaptureBits,
1758
profile_counters: &mut BackendProfileCounters,
1759
) -> DebugOutput {
1760
use std::fs;
1761
use crate::capture::CaptureConfig;
1762
use crate::render_task_graph::dump_render_tasks_as_svg;
1763
1764
debug!("capture: saving {:?}", root);
1765
if !root.is_dir() {
1766
if let Err(e) = fs::create_dir_all(&root) {
1767
panic!("Unable to create capture dir: {:?}", e);
1768
}
1769
}
1770
let config = CaptureConfig::new(root, bits);
1771
1772
if config.bits.contains(CaptureBits::FRAME) {
1773
self.prepare_for_frames();
1774
}
1775
1776
for (&id, doc) in &mut self.documents {
1777
debug!("\tdocument {:?}", id);
1778
if config.bits.contains(CaptureBits::FRAME) {
1779
let rendered_document = doc.build_frame(
1780
&mut self.resource_cache,
1781
&mut self.gpu_cache,
1782
&mut profile_counters.resources,
1783
self.debug_flags,
1784
&mut self.tile_cache_logger,
1785
);
1786
// After we rendered the frames, there are pending updates to both
1787
// GPU cache and resources. Instead of serializing them, we are going to make sure
1788
// they are applied on the `Renderer` side.
1789