From 259a7474cba94cccda589ee7503e57d3536a8617 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 29 Aug 2018 19:21:50 +0200 Subject: [PATCH 1/7] Move the pending scene data structure to the scene builder thread. With this change the render backend only sees the state of the scene that it can use for frame building and the scene builder thread manages its own scene. This will allow us to perform low priority scene building and rasterization asynchronously without having to involve the render backend thread. --- webrender/src/display_list_flattener.rs | 26 -- webrender/src/render_backend.rs | 431 ++++++++++-------------- webrender/src/scene.rs | 8 + webrender/src/scene_builder.rs | 408 +++++++++++++++------- 4 files changed, 472 insertions(+), 401 deletions(-) diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 33a15a4dfb..e623844c3f 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -31,7 +31,6 @@ use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive}; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; -use scene_builder::{BuiltScene, SceneRequest}; use spatial_node::{SpatialNodeType, StickyFrameInfo}; use std::{f32, iter, mem}; use tiling::{CompositeOps, ScrollbarPrimitive}; @@ -2000,31 +1999,6 @@ impl<'a> DisplayListFlattener<'a> { } } -pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene { - - let mut clip_scroll_tree = ClipScrollTree::new(); - let mut new_scene = Scene::new(); - - let frame_builder = DisplayListFlattener::create_frame_builder( - FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up. - &request.scene, - &mut clip_scroll_tree, - request.font_instances, - &request.view, - &request.output_pipelines, - config, - &mut new_scene, - request.scene_id, - ); - - BuiltScene { - scene: new_scene, - frame_builder, - clip_scroll_tree, - removed_pipelines: request.removed_pipelines, - } -} - /// Properties of a stacking context that are maintained /// during creation of the scene. These structures are /// not persisted after the initial scene build. diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 2d6e1ce615..d121dddf55 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -17,8 +17,6 @@ use api::CapturedDocument; use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree}; #[cfg(feature = "debugger")] use debug_server; -#[cfg(feature = "replay")] -use display_list_flattener::build_scene; use frame_builder::{FrameBuilder, FrameBuilderConfig}; use gpu_cache::GpuCache; use hit_test::{HitTest, HitTester}; @@ -70,11 +68,6 @@ impl DocumentView { } } -struct SceneData { - scene: Scene, - removed_pipelines: Vec, -} - #[derive(Copy, Clone, Hash, PartialEq, PartialOrd, Debug, Eq, Ord)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -83,10 +76,11 @@ pub struct FrameId(pub u32); struct Document { // The latest built scene, usable to build frames. // received from the scene builder thread. - current: SceneData, - // The scene with the latest transactions applied, not necessarily built yet. - // what we will send to the scene builder. - pending: SceneData, + scene: Scene, + + // Temporary list of removed pipelines received from the scene builder + // thread and forwarded to the renderer. + removed_pipelines: Vec, view: DocumentView, @@ -136,14 +130,8 @@ impl Document { None }; Document { - current: SceneData { - scene: Scene::new(), - removed_pipelines: Vec::new(), - }, - pending: SceneData { - scene: Scene::new(), - removed_pipelines: Vec::new(), - }, + scene: Scene::new(), + removed_pipelines: Vec::new(), view: DocumentView { window_size, inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size), @@ -176,9 +164,7 @@ impl Document { ) -> DocumentOps { match message { FrameMsg::UpdateEpoch(pipeline_id, epoch) => { - self.current.scene.update_epoch(pipeline_id, epoch); - - DocumentOps::nop() + self.scene.update_epoch(pipeline_id, epoch); } FrameMsg::EnableFrameOutput(pipeline_id, enable) => { if enable { @@ -186,7 +172,6 @@ impl Document { } else { self.output_pipelines.remove(&pipeline_id); } - DocumentOps::nop() } FrameMsg::Scroll(delta, cursor) => { profile_scope!("Scroll"); @@ -209,12 +194,13 @@ impl Document { should_render && self.scroll_nearest_scrolling_ancestor(delta, node_index) && self.render_on_scroll == Some(true); - DocumentOps { + + return DocumentOps { scroll: true, render: should_render, composite: should_render, ..DocumentOps::nop() - } + }; } FrameMsg::HitTest(pipeline_id, point, flags, tx) => { @@ -226,11 +212,9 @@ impl Document { }; tx.send(result).unwrap(); - DocumentOps::nop() } FrameMsg::SetPan(pan) => { self.view.pan = pan; - DocumentOps::nop() } FrameMsg::ScrollNodeWithId(origin, id, clamp) => { profile_scope!("ScrollNodeWithScrollId"); @@ -238,74 +222,29 @@ impl Document { let should_render = self.scroll_node(origin, id, clamp) && self.render_on_scroll == Some(true); - DocumentOps { + return DocumentOps { scroll: true, render: should_render, composite: should_render, ..DocumentOps::nop() - } + }; } FrameMsg::GetScrollNodeState(tx) => { profile_scope!("GetScrollNodeState"); tx.send(self.get_scroll_node_state()).unwrap(); - DocumentOps::nop() } FrameMsg::UpdateDynamicProperties(property_bindings) => { self.dynamic_properties.set_properties(property_bindings); - DocumentOps::nop() } FrameMsg::AppendDynamicProperties(property_bindings) => { self.dynamic_properties.add_properties(property_bindings); - DocumentOps::nop() } } - } - - fn forward_transaction_to_scene_builder( - &mut self, - transaction_msg: TransactionMsg, - blobs_to_rasterize: &[ImageKey], - document_ops: &DocumentOps, - document_id: DocumentId, - scene_id: u64, - resource_cache: &mut ResourceCache, - scene_tx: &Sender, - ) { - // Do as much of the error handling as possible here before dispatching to - // the scene builder thread. - let build_scene: bool = document_ops.build - && self.pending.scene.root_pipeline_id.map( - |id| { self.pending.scene.pipelines.contains_key(&id) } - ).unwrap_or(false); - - let scene_request = if build_scene { - Some(SceneRequest { - scene: self.pending.scene.clone(), - removed_pipelines: replace(&mut self.pending.removed_pipelines, Vec::new()), - view: self.view.clone(), - font_instances: resource_cache.get_font_instances(), - output_pipelines: self.output_pipelines.clone(), - scene_id, - }) - } else { - None - }; - let (blob_rasterizer, blob_requests) = resource_cache.create_blob_scene_builder_requests( - blobs_to_rasterize - ); - - scene_tx.send(SceneBuilderRequest::Transaction { - scene: scene_request, - blob_requests, - blob_rasterizer, - resource_updates: transaction_msg.resource_updates, - frame_ops: transaction_msg.frame_ops, - render: transaction_msg.generate_frame, - document_id, - }).unwrap(); + DocumentOps::nop() } + fn render( &mut self, resource_cache: &mut ResourceCache, @@ -323,7 +262,7 @@ impl Document { gpu_cache, self.frame_id, &mut self.clip_scroll_tree, - &self.current.scene.pipelines, + &self.scene.pipelines, accumulated_scale_factor, self.view.layer, pan, @@ -342,9 +281,9 @@ impl Document { } pub fn updated_pipeline_info(&mut self) -> PipelineInfo { - let removed_pipelines = replace(&mut self.current.removed_pipelines, Vec::new()); + let removed_pipelines = replace(&mut self.removed_pipelines, Vec::new()); PipelineInfo { - epochs: self.current.scene.pipeline_epochs.clone(), + epochs: self.scene.pipeline_epochs.clone(), removed_pipelines, } } @@ -377,11 +316,10 @@ impl Document { self.clip_scroll_tree.get_scroll_node_state() } - pub fn new_async_scene_ready(&mut self, mut built_scene: BuiltScene) { - self.current.scene = built_scene.scene; + pub fn new_async_scene_ready(&mut self, built_scene: BuiltScene) { + self.scene = built_scene.scene; self.frame_builder = Some(built_scene.frame_builder); - self.current.removed_pipelines.extend(built_scene.removed_pipelines.drain(..)); let old_scrolling_states = self.clip_scroll_tree.drain(); self.clip_scroll_tree = built_scene.clip_scroll_tree; @@ -394,7 +332,6 @@ impl Document { struct DocumentOps { scroll: bool, - build: bool, render: bool, composite: bool, } @@ -403,29 +340,13 @@ impl DocumentOps { fn nop() -> Self { DocumentOps { scroll: false, - build: false, render: false, composite: false, } } - fn build() -> Self { - DocumentOps { - build: true, - ..DocumentOps::nop() - } - } - - fn render() -> Self { - DocumentOps { - render: true, - ..DocumentOps::nop() - } - } - fn combine(&mut self, other: Self) { self.scroll = self.scroll || other.scroll; - self.build = self.build || other.build; self.render = self.render || other.render; self.composite = self.composite || other.composite; } @@ -518,23 +439,20 @@ impl RenderBackend { document_id: DocumentId, message: SceneMsg, frame_counter: u32, + txn: &mut Transaction, ipc_profile_counters: &mut IpcProfileCounters, - ) -> DocumentOps { + ) { let doc = self.documents.get_mut(&document_id).expect("No document?"); match message { SceneMsg::UpdateEpoch(pipeline_id, epoch) => { - doc.pending.scene.update_epoch(pipeline_id, epoch); - - DocumentOps::nop() + txn.epoch_updates.push((pipeline_id, epoch)); } SceneMsg::SetPageZoom(factor) => { doc.view.page_zoom_factor = factor.get(); - DocumentOps::nop() } SceneMsg::SetPinchZoom(factor) => { doc.view.pinch_zoom_factor = factor.get(); - DocumentOps::nop() } SceneMsg::SetWindowParameters { window_size, @@ -544,7 +462,6 @@ impl RenderBackend { doc.view.window_size = window_size; doc.view.inner_rect = inner_rect; doc.view.device_pixel_ratio = device_pixel_ratio; - DocumentOps::nop() } SceneMsg::SetDisplayList { epoch, @@ -588,16 +505,14 @@ impl RenderBackend { built_display_list.times(); let display_list_received_time = precise_time_ns(); - { - doc.pending.scene.set_display_list( - pipeline_id, - epoch, - built_display_list, - background, - viewport_size, - content_size, - ); - } + txn.display_list_updates.push(DisplayListUpdate { + built_display_list, + pipeline_id, + epoch, + background, + viewport_size, + content_size, + }); if let Some(ref mut ros) = doc.render_on_scroll { *ros = false; //wait for `GenerateFrame` @@ -616,25 +531,16 @@ impl RenderBackend { display_list_consumed_time, display_list_len, ); - - DocumentOps::build() } SceneMsg::SetRootPipeline(pipeline_id) => { profile_scope!("SetRootPipeline"); - doc.pending.scene.set_root_pipeline_id(pipeline_id); - if doc.pending.scene.pipelines.get(&pipeline_id).is_some() { - DocumentOps::build() - } else { - DocumentOps::nop() - } + txn.set_root_pipeline = Some(pipeline_id); } SceneMsg::RemovePipeline(pipeline_id) => { profile_scope!("RemovePipeline"); - doc.pending.scene.remove_pipeline(pipeline_id); - doc.pending.removed_pipelines.push(pipeline_id); - DocumentOps::nop() + txn.removed_pipelines.push(pipeline_id); } } } @@ -662,25 +568,15 @@ impl RenderBackend { while let Ok(msg) = self.scene_rx.try_recv() { match msg { - SceneBuilderResult::Transaction { - document_id, - mut built_scene, - resource_updates, - frame_ops, - render, - result_tx, - rasterized_blobs, - blob_rasterizer, - } => { - let mut ops = DocumentOps::nop(); - if let Some(doc) = self.documents.get_mut(&document_id) { - if let Some(mut built_scene) = built_scene.take() { + SceneBuilderResult::Transaction(mut txn, result_tx) => { + if let Some(doc) = self.documents.get_mut(&txn.document_id) { + + doc.removed_pipelines.append(&mut txn.removed_pipelines); + + if let Some(mut built_scene) = txn.built_scene.take() { doc.new_async_scene_ready(built_scene); - // After applying the new scene we need to - // rebuild the hit-tester, so we trigger a render - // step. - ops = DocumentOps::render(); } + if let Some(tx) = result_tx { let (resume_tx, resume_rx) = channel(); tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); @@ -700,27 +596,21 @@ impl RenderBackend { continue; } - let transaction_msg = TransactionMsg { - scene_ops: Vec::new(), - frame_ops, - resource_updates, - generate_frame: render, - use_scene_builder_thread: false, - }; - - self.resource_cache.add_rasterized_blob_images(rasterized_blobs); - if let Some(rasterizer) = blob_rasterizer { + self.resource_cache.add_rasterized_blob_images( + replace(&mut txn.rasterized_blobs, Vec::new()) + ); + if let Some(rasterizer) = txn.blob_rasterizer.take() { self.resource_cache.set_blob_rasterizer(rasterizer); } - if !transaction_msg.is_empty() || ops.render { + if txn.render || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() { self.update_document( - document_id, - transaction_msg, - &[], + txn.document_id, + replace(&mut txn.resource_updates, Vec::new()), + replace(&mut txn.frame_ops, Vec::new()), + txn.render, &mut frame_counter, &mut profile_counters, - ops, true, ); } @@ -827,6 +717,9 @@ impl RenderBackend { } ApiMsg::DeleteDocument(document_id) => { self.documents.remove(&document_id); + self.scene_tx.send( + SceneBuilderRequest::DeleteDocument(document_id) + ).unwrap(); } ApiMsg::ExternalEvent(evt) => { self.notifier.external_event(evt); @@ -904,7 +797,7 @@ impl RenderBackend { for (id, doc) in &self.documents { let captured = CapturedDocument { document_id: *id, - root_pipeline_id: doc.current.scene.root_pipeline_id, + root_pipeline_id: doc.scene.root_pipeline_id, window_size: doc.view.window_size, }; tx.send(captured).unwrap(); @@ -925,76 +818,108 @@ impl RenderBackend { ApiMsg::ShutDown => { return false; } - ApiMsg::UpdateDocument(document_id, mut doc_msgs) => { - let blob_requests = get_blob_image_updates(&doc_msgs.resource_updates); - - self.resource_cache.pre_scene_building_update( - &mut doc_msgs.resource_updates, - &mut profile_counters.resources, - ); - - self.update_document( + ApiMsg::UpdateDocument(document_id, transaction_msg) => { + self.prepare_transaction( document_id, - doc_msgs, - &blob_requests, + transaction_msg, frame_counter, profile_counters, - DocumentOps::nop(), - false, - ) + ); } } true } - fn update_document( + fn prepare_transaction( &mut self, document_id: DocumentId, mut transaction_msg: TransactionMsg, - blob_requests: &[ImageKey], frame_counter: &mut u32, profile_counters: &mut BackendProfileCounters, - initial_op: DocumentOps, - has_built_scene: bool, ) { - let mut op = initial_op; + let mut txn = Box::new(Transaction { + document_id, + display_list_updates: Vec::new(), + removed_pipelines: Vec::new(), + epoch_updates: Vec::new(), + request_scene_build: None, + blob_rasterizer: None, + blob_requests: Vec::new(), + resource_updates: transaction_msg.resource_updates, + frame_ops: transaction_msg.frame_ops, + set_root_pipeline: None, + render: transaction_msg.generate_frame, + }); - if !blob_requests.is_empty() { - transaction_msg.use_scene_builder_thread = true; - } + self.resource_cache.pre_scene_building_update( + &mut txn.resource_updates, + &mut profile_counters.resources, + ); for scene_msg in transaction_msg.scene_ops.drain(..) { let _timer = profile_counters.total_time.timer(); - op.combine( - self.process_scene_msg( - document_id, - scene_msg, - *frame_counter, - &mut profile_counters.ipc, - ) - ); + self.process_scene_msg( + document_id, + scene_msg, + *frame_counter, + &mut txn, + &mut profile_counters.ipc, + ) } - if !has_built_scene && (op.build || transaction_msg.use_scene_builder_thread) { - let scene_id = self.make_unique_scene_id(); - let doc = self.documents.get_mut(&document_id).unwrap(); + let blobs_to_rasterize = get_blob_image_updates(&txn.resource_updates); + if !blobs_to_rasterize.is_empty() { + let (blob_rasterizer, blob_requests) = self.resource_cache + .create_blob_scene_builder_requests(&blobs_to_rasterize); - doc.forward_transaction_to_scene_builder( - transaction_msg, - blob_requests, - &op, - document_id, - scene_id, - &mut self.resource_cache, - &self.scene_tx, + txn.blob_requests = blob_requests; + txn.blob_rasterizer = blob_rasterizer; + } + + if !transaction_msg.use_scene_builder_thread && txn.can_skip_scene_builder() { + + self.update_document( + txn.document_id, + replace(&mut txn.resource_updates, Vec::new()), + replace(&mut txn.frame_ops, Vec::new()), + txn.render, + frame_counter, + profile_counters, + false ); return; } + let scene_id = self.make_unique_scene_id(); + let doc = self.documents.get_mut(&document_id).unwrap(); + + if txn.should_build_scene() { + txn.request_scene_build = Some(SceneRequest { + view: doc.view.clone(), + font_instances: self.resource_cache.get_font_instances(), + output_pipelines: doc.output_pipelines.clone(), + scene_id, + }); + } + + self.scene_tx.send(SceneBuilderRequest::Transaction(txn)).unwrap(); + } + + fn update_document( + &mut self, + document_id: DocumentId, + resource_updates: Vec, + mut frame_ops: Vec, + generate_frame: bool, + frame_counter: &mut u32, + profile_counters: &mut BackendProfileCounters, + has_built_scene: bool, + ) { + self.resource_cache.post_scene_building_update( - transaction_msg.resource_updates, + resource_updates, &mut profile_counters.resources, ); @@ -1003,15 +928,20 @@ impl RenderBackend { // fiddle with things after a potentially long scene build, but just // before rendering. This is useful for rendering with the latest // async transforms. - if op.render || transaction_msg.generate_frame { + if generate_frame { if let Some(ref sampler) = self.sampler { - transaction_msg.frame_ops.append(&mut sampler.sample()); + frame_ops.append(&mut sampler.sample()); } } + let mut op = DocumentOps { + render: generate_frame, + ..DocumentOps::nop() + }; + let doc = self.documents.get_mut(&document_id).unwrap(); - for frame_msg in transaction_msg.frame_ops { + for frame_msg in frame_ops { let _timer = profile_counters.total_time.timer(); op.combine(doc.process_frame_msg(frame_msg)); } @@ -1020,12 +950,12 @@ impl RenderBackend { op.render = true; } - if transaction_msg.generate_frame { + if op.render { if let Some(ref mut ros) = doc.render_on_scroll { *ros = true; } - if doc.current.scene.root_pipeline_id.is_some() { + if doc.scene.root_pipeline_id.is_some() { op.render = true; op.composite = true; } @@ -1092,7 +1022,7 @@ impl RenderBackend { self.result_tx.send(msg).unwrap(); } - if transaction_msg.generate_frame { + if op.render { self.notifier.new_frame_ready(document_id, op.scroll, op.composite, render_time); } } @@ -1149,7 +1079,7 @@ impl RenderBackend { for (_, doc) in &self.documents { let mut debug_doc = debug_server::TreeNode::new("document"); - for (_, pipeline) in &doc.current.scene.pipelines { + for (_, pipeline) in &doc.scene.pipelines { let mut debug_dl = debug_server::TreeNode::new("display-list"); self.traverse_items(&mut pipeline.display_list.iter(), &mut debug_dl); debug_doc.add_child(debug_dl); @@ -1267,7 +1197,7 @@ impl RenderBackend { debug!("\tdocument {:?}", id); if config.bits.contains(CaptureBits::SCENE) { let file_name = format!("scene-{}-{}", (id.0).0, id.1); - config.serialize(&doc.current.scene, file_name); + config.serialize(&doc.scene, file_name); } if config.bits.contains(CaptureBits::FRAME) { let rendered_document = doc.render( @@ -1357,6 +1287,8 @@ impl RenderBackend { self.frame_config = backend.frame_config; self.enable_render_on_scroll = backend.enable_render_on_scroll; + let mut scenes_to_build = Vec::new(); + let mut last_scene_id = backend.last_scene_id; for (id, view) in backend.documents { debug!("\tdocument {:?}", id); @@ -1365,14 +1297,8 @@ impl RenderBackend { .expect(&format!("Unable to open {}.ron", scene_name)); let mut doc = Document { - current: SceneData { - scene: scene.clone(), - removed_pipelines: Vec::new(), - }, - pending: SceneData { - scene, - removed_pipelines: Vec::new(), - }, + scene: scene.clone(), + removed_pipelines: Vec::new(), view: view.clone(), clip_scroll_tree: ClipScrollTree::new(), frame_id: FrameId(0), @@ -1385,46 +1311,53 @@ impl RenderBackend { }; let frame_name = format!("frame-{}-{}", (id.0).0, id.1); - let render_doc = match CaptureConfig::deserialize::(root, frame_name) { + let frame = CaptureConfig::deserialize::(root, frame_name); + let generate_frame = match frame { Some(frame) => { info!("\tloaded a built frame with {} passes", frame.passes.len()); - RenderedDocument { frame, is_new_scene: true } - } - None => { - last_scene_id += 1; - let built_scene = build_scene(&self.frame_config, SceneRequest { - scene: doc.pending.scene.clone(), - view, - font_instances: self.resource_cache.get_font_instances(), - output_pipelines: doc.output_pipelines.clone(), - removed_pipelines: Vec::new(), - scene_id: last_scene_id, - }); - doc.new_async_scene_ready(built_scene); - doc.render( - &mut self.resource_cache, - &mut self.gpu_cache, - &mut profile_counters.resources, - true, - ) + + let msg_update = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates()); + self.result_tx.send(msg_update).unwrap(); + + let msg_publish = ResultMsg::PublishDocument( + id, + RenderedDocument { frame, is_new_scene: true }, + self.resource_cache.pending_updates(), + profile_counters.clone(), + ); + self.result_tx.send(msg_publish).unwrap(); + profile_counters.reset(); + + self.notifier.new_frame_ready(id, false, true, None); + + // We deserialized the state of the frame so we don't want to build + // it (but we do want to update the scene builder's state) + false } + None => true, }; - let msg_update = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates()); - self.result_tx.send(msg_update).unwrap(); + last_scene_id += 1; - let msg_publish = ResultMsg::PublishDocument( - id, - render_doc, - self.resource_cache.pending_updates(), - profile_counters.clone(), - ); - self.result_tx.send(msg_publish).unwrap(); - profile_counters.reset(); + scenes_to_build.push(LoadScene { + document_id: id, + scene: doc.scene.clone(), + view: view.clone(), + config: self.frame_config.clone(), + output_pipelines: doc.output_pipelines.clone(), + font_instances: self.resource_cache.get_font_instances(), + scene_id: last_scene_id, + generate_frame, + }); - self.notifier.new_frame_ready(id, false, true, None); self.documents.insert(id, doc); } + + if !scenes_to_build.is_empty() { + self.scene_tx.send( + SceneBuilderRequest::LoadScenes(scenes_to_build) + ).unwrap(); + } } } diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index ce0891917d..b77d452cac 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -188,6 +188,14 @@ impl Scene { pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) { self.pipeline_epochs.insert(pipeline_id, epoch); } + + pub fn has_root_pipeline(&self) -> bool { + if let Some(ref root_id) = self.root_pipeline_id { + return self.pipelines.contains_key(root_id); + } + + false + } } /// An arbitrary number which we assume opacity is invisible below. diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index 642b05ca11..16a62f49f3 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -3,30 +3,107 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult}; -use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate}; +use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, Epoch}; +use api::{BuiltDisplayList, ColorF, LayoutSize}; use api::channel::MsgSender; -use display_list_flattener::build_scene; use frame_builder::{FrameBuilderConfig, FrameBuilder}; use clip_scroll_tree::ClipScrollTree; -use internal_types::FastHashSet; +use display_list_flattener::DisplayListFlattener; +use internal_types::{FastHashMap, FastHashSet}; use resource_cache::FontInstanceMap; use render_backend::DocumentView; use renderer::{PipelineInfo, SceneBuilderHooks}; use scene::Scene; use std::sync::mpsc::{channel, Receiver, Sender}; +use std::mem::replace; use time::precise_time_ns; +/// Represents the work associated to a transaction before scene building. +pub struct Transaction { + pub document_id: DocumentId, + pub display_list_updates: Vec, + pub removed_pipelines: Vec, + pub epoch_updates: Vec<(PipelineId, Epoch)>, + pub request_scene_build: Option, + pub blob_requests: Vec, + pub blob_rasterizer: Option>, + pub resource_updates: Vec, + pub frame_ops: Vec, + pub set_root_pipeline: Option, + pub render: bool, +} + +impl Transaction { + pub fn can_skip_scene_builder(&self) -> bool { + self.request_scene_build.is_none() && + self.display_list_updates.is_empty() && + self.epoch_updates.is_empty() && + self.removed_pipelines.is_empty() && + self.blob_requests.is_empty() && + self.set_root_pipeline.is_none() + } + + pub fn should_build_scene(&self) -> bool { + !self.display_list_updates.is_empty() || + self.set_root_pipeline.is_some() + } +} + +/// Represent the remaining work associated to a transaction after the scene building +/// phase as well as the result of scene building itself if applicable. +pub struct BuiltTransaction { + pub document_id: DocumentId, + pub built_scene: Option, + pub resource_updates: Vec, + pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>, + pub blob_rasterizer: Option>, + pub frame_ops: Vec, + pub removed_pipelines: Vec, + pub scene_build_start_time: u64, + pub scene_build_end_time: u64, + pub render: bool, +} + +pub struct DisplayListUpdate { + pub pipeline_id: PipelineId, + pub epoch: Epoch, + pub built_display_list: BuiltDisplayList, + pub background: Option, + pub viewport_size: LayoutSize, + pub content_size: LayoutSize, +} + +/// Contains the render backend data needed to build a scene. +pub struct SceneRequest { + pub view: DocumentView, + pub font_instances: FontInstanceMap, + pub output_pipelines: FastHashSet, + pub scene_id: u64, +} + +/// For the replay feature. +pub struct LoadScene { + pub document_id: DocumentId, + pub scene: Scene, + pub scene_id: u64, + pub output_pipelines: FastHashSet, + pub font_instances: FontInstanceMap, + pub view: DocumentView, + pub config: FrameBuilderConfig, + pub generate_frame: bool, +} + +pub struct BuiltScene { + pub scene: Scene, + pub frame_builder: FrameBuilder, + pub clip_scroll_tree: ClipScrollTree, +} + // Message from render backend to scene builder. pub enum SceneBuilderRequest { - Transaction { - document_id: DocumentId, - scene: Option, - blob_requests: Vec, - blob_rasterizer: Option>, - resource_updates: Vec, - frame_ops: Vec, - render: bool, - }, + Transaction(Box), + LoadScenes(Vec), + DeleteDocument(DocumentId), WakeUp, Flush(MsgSender<()>), SetFrameBuilderConfig(FrameBuilderConfig), @@ -35,16 +112,7 @@ pub enum SceneBuilderRequest { // Message from scene builder to render backend. pub enum SceneBuilderResult { - Transaction { - document_id: DocumentId, - built_scene: Option, - resource_updates: Vec, - rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>, - blob_rasterizer: Option>, - frame_ops: Vec, - render: bool, - result_tx: Option>, - }, + Transaction(Box, Option>), FlushComplete(MsgSender<()>), Stopped, } @@ -57,24 +125,8 @@ pub enum SceneSwapResult { Aborted, } -/// Contains the render backend data needed to build a scene. -pub struct SceneRequest { - pub scene: Scene, - pub view: DocumentView, - pub font_instances: FontInstanceMap, - pub output_pipelines: FastHashSet, - pub removed_pipelines: Vec, - pub scene_id: u64, -} - -pub struct BuiltScene { - pub scene: Scene, - pub frame_builder: FrameBuilder, - pub clip_scroll_tree: ClipScrollTree, - pub removed_pipelines: Vec, -} - pub struct SceneBuilder { + documents: FastHashMap, rx: Receiver, tx: Sender, api_tx: MsgSender, @@ -92,6 +144,7 @@ impl SceneBuilder { let (out_tx, out_rx) = channel(); ( SceneBuilder { + documents: FastHashMap::default(), rx: in_rx, tx: out_tx, api_tx, @@ -103,6 +156,7 @@ impl SceneBuilder { ) } + /// The scene builder thread's event loop. pub fn run(&mut self) { if let Some(ref hooks) = self.hooks { hooks.register(); @@ -110,10 +164,29 @@ impl SceneBuilder { loop { match self.rx.recv() { - Ok(msg) => { - if !self.process_message(msg) { - break; - } + Ok(SceneBuilderRequest::WakeUp) => {} + Ok(SceneBuilderRequest::Flush(tx)) => { + self.tx.send(SceneBuilderResult::FlushComplete(tx)).unwrap(); + let _ = self.api_tx.send(ApiMsg::WakeUp); + } + Ok(SceneBuilderRequest::Transaction(mut txn)) => { + let built_txn = self.process_transaction(&mut txn); + self.forward_built_transaction(built_txn); + } + Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { + self.documents.remove(&document_id); + } + Ok(SceneBuilderRequest::LoadScenes(msg)) => { + self.load_scenes(msg); + } + Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => { + self.config = cfg; + } + Ok(SceneBuilderRequest::Stop) => { + self.tx.send(SceneBuilderResult::Stopped).unwrap(); + // We don't need to send a WakeUp to api_tx because we only + // get the Stop when the RenderBackend loop is exiting. + break; } Err(_) => { break; @@ -130,97 +203,180 @@ impl SceneBuilder { } } - fn process_message(&mut self, msg: SceneBuilderRequest) -> bool { - match msg { - SceneBuilderRequest::WakeUp => {} - SceneBuilderRequest::Flush(tx) => { - self.tx.send(SceneBuilderResult::FlushComplete(tx)).unwrap(); - let _ = self.api_tx.send(ApiMsg::WakeUp); - } - SceneBuilderRequest::Transaction { - document_id, - scene, - blob_requests, - mut blob_rasterizer, - resource_updates, - frame_ops, - render, - } => { - let scenebuild_start_time = precise_time_ns(); - let built_scene = scene.map(|request|{ - build_scene(&self.config, request) + // Specific to the replay functionality. + fn load_scenes(&mut self, scenes: Vec) { + for item in scenes { + self.config = item.config; + + let scene_build_start_time = precise_time_ns(); + + let mut built_scene = None; + if item.scene.has_root_pipeline() { + let mut clip_scroll_tree = ClipScrollTree::new(); + let mut new_scene = Scene::new(); + + let frame_builder = DisplayListFlattener::create_frame_builder( + FrameBuilder::empty(), + &item.scene, + &mut clip_scroll_tree, + item.font_instances, + &item.view, + &item.output_pipelines, + &self.config, + &mut new_scene, + item.scene_id, + ); + + built_scene = Some(BuiltScene { + scene: new_scene, + frame_builder, + clip_scroll_tree, }); + } + + self.documents.insert(item.document_id, item.scene); + + let txn = Box::new(BuiltTransaction { + document_id: item.document_id, + render: item.generate_frame, + built_scene, + resource_updates: Vec::new(), + rasterized_blobs: Vec::new(), + blob_rasterizer: None, + frame_ops: Vec::new(), + removed_pipelines: Vec::new(), + scene_build_start_time, + scene_build_end_time: precise_time_ns(), + }); + + self.forward_built_transaction(txn); + } + } + + /// Do the bulk of the work of the scene builder thread. + fn process_transaction(&mut self, txn: &mut Transaction) -> Box { + let scene_build_start_time = precise_time_ns(); + + let scene = self.documents.entry(txn.document_id).or_insert(Scene::new()); + + for update in txn.display_list_updates.drain(..) { + scene.set_display_list( + update.pipeline_id, + update.epoch, + update.built_display_list, + update.background, + update.viewport_size, + update.content_size, + ); + } + + for &(pipeline_id, epoch) in &txn.epoch_updates { + scene.update_epoch(pipeline_id, epoch); + } + + if let Some(id) = txn.set_root_pipeline { + scene.set_root_pipeline_id(id); + } + + for pipeline_id in &txn.removed_pipelines { + scene.remove_pipeline(*pipeline_id) + } + + let mut built_scene = None; + if scene.has_root_pipeline() { + if let Some(request) = txn.request_scene_build.take() { + let mut clip_scroll_tree = ClipScrollTree::new(); + let mut new_scene = Scene::new(); - let rasterized_blobs = blob_rasterizer.as_mut().map_or( - Vec::new(), - |rasterizer| rasterizer.rasterize(&blob_requests), + let frame_builder = DisplayListFlattener::create_frame_builder( + FrameBuilder::empty(), + &scene, + &mut clip_scroll_tree, + request.font_instances, + &request.view, + &request.output_pipelines, + &self.config, + &mut new_scene, + request.scene_id, ); - // We only need the pipeline info and the result channel if we - // have a hook callback *and* if this transaction actually built - // a new scene that is going to get swapped in. In other cases - // pipeline_info can be None and we can avoid some overhead from - // invoking the hooks and blocking on the channel. - let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &built_scene) { - (&Some(ref hooks), &Some(ref built)) => { - let info = PipelineInfo { - epochs: built.scene.pipeline_epochs.clone(), - removed_pipelines: built.removed_pipelines.clone(), - }; - let (tx, rx) = channel(); - - let scenebuild_time = precise_time_ns() - scenebuild_start_time; - hooks.pre_scene_swap(scenebuild_time); - - (Some(info), Some(tx), Some(rx)) - } - _ => (None, None, None), + built_scene = Some(BuiltScene { + scene: new_scene, + frame_builder, + clip_scroll_tree, + }); + } + } + + let blob_requests = replace(&mut txn.blob_requests, Vec::new()); + let rasterized_blobs = txn.blob_rasterizer.as_mut().map_or( + Vec::new(), + |rasterizer| rasterizer.rasterize(&blob_requests), + ); + + // After applying the new scene we need to + // rebuild the hit-tester, so we trigger a render + // step. + + Box::new(BuiltTransaction { + document_id: txn.document_id, + render: txn.render || built_scene.is_some(), + built_scene, + resource_updates: replace(&mut txn.resource_updates, Vec::new()), + rasterized_blobs: rasterized_blobs, + blob_rasterizer: replace(&mut txn.blob_rasterizer, None), + frame_ops: replace(&mut txn.frame_ops, Vec::new()), + removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()), + scene_build_start_time, + scene_build_end_time: precise_time_ns(), + }) + } + + /// Send the result of process_transaction back to the render backend. + fn forward_built_transaction(&mut self, txn: Box) { + // We only need the pipeline info and the result channel if we + // have a hook callback *and* if this transaction actually built + // a new scene that is going to get swapped in. In other cases + // pipeline_info can be None and we can avoid some overhead from + // invoking the hooks and blocking on the channel. + let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) { + (&Some(ref hooks), &Some(ref built)) => { + let info = PipelineInfo { + epochs: built.scene.pipeline_epochs.clone(), + removed_pipelines: txn.removed_pipelines.clone(), }; + let (tx, rx) = channel(); - let sceneswap_start_time = precise_time_ns(); - let has_resources_updates = !resource_updates.is_empty(); - self.tx.send(SceneBuilderResult::Transaction { - document_id, - built_scene, - resource_updates, - rasterized_blobs, - blob_rasterizer, - frame_ops, - render, - result_tx, - }).unwrap(); - - let _ = self.api_tx.send(ApiMsg::WakeUp); - - if let Some(pipeline_info) = pipeline_info { - // Block until the swap is done, then invoke the hook. - let swap_result = result_rx.unwrap().recv(); - let sceneswap_time = precise_time_ns() - sceneswap_start_time; - self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, sceneswap_time); - // Once the hook is done, allow the RB thread to resume - match swap_result { - Ok(SceneSwapResult::Complete(resume_tx)) => { - resume_tx.send(()).ok(); - }, - _ => (), - }; - } else if has_resources_updates { - if let &Some(ref hooks) = &self.hooks { - hooks.post_resource_update(); - } - } - } - SceneBuilderRequest::Stop => { - self.tx.send(SceneBuilderResult::Stopped).unwrap(); - // We don't need to send a WakeUp to api_tx because we only - // get the Stop when the RenderBackend loop is exiting. - return false; + hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time); + + (Some(info), Some(tx), Some(rx)) } - SceneBuilderRequest::SetFrameBuilderConfig(cfg) => { - self.config = cfg; + _ => (None, None, None), + }; + + let scene_swap_start_time = precise_time_ns(); + let has_resources_updates = !txn.resource_updates.is_empty(); + + self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap(); + + let _ = self.api_tx.send(ApiMsg::WakeUp); + + if let Some(pipeline_info) = pipeline_info { + // Block until the swap is done, then invoke the hook. + let swap_result = result_rx.unwrap().recv(); + let scene_swap_time = precise_time_ns() - scene_swap_start_time; + self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, scene_swap_time); + // Once the hook is done, allow the RB thread to resume + match swap_result { + Ok(SceneSwapResult::Complete(resume_tx)) => { + resume_tx.send(()).ok(); + }, + _ => (), + }; + } else if has_resources_updates { + if let &Some(ref hooks) = &self.hooks { + hooks.post_resource_update(); } } - - true } } From 810b3138e493d427c9e044c87aa056c1e2153667 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 30 Aug 2018 17:45:49 +0200 Subject: [PATCH 2/7] Remove the per-document FrameBuilderConfig. All documents currently always have the same config. --- webrender/src/render_backend.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index d121dddf55..a107d22016 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -92,9 +92,6 @@ struct Document { /// The id of the current frame. frame_id: FrameId, - /// A configuration object for the FrameBuilder that we produce. - frame_builder_config: FrameBuilderConfig, - // the `Option` here is only to deal with borrow checker frame_builder: Option, // A set of pipelines that the caller has requested be @@ -118,7 +115,6 @@ struct Document { impl Document { pub fn new( - frame_builder_config: FrameBuilderConfig, window_size: DeviceUintSize, layer: DocumentLayer, enable_render_on_scroll: bool, @@ -143,7 +139,6 @@ impl Document { }, clip_scroll_tree: ClipScrollTree::new(), frame_id: FrameId(0), - frame_builder_config, frame_builder: None, output_pipelines: FastHashSet::default(), render_on_scroll, @@ -707,7 +702,6 @@ impl RenderBackend { } ApiMsg::AddDocument(document_id, initial_size, layer) => { let document = Document::new( - self.frame_config.clone(), initial_size, layer, self.enable_render_on_scroll, @@ -762,11 +756,6 @@ impl RenderBackend { self.frame_config .dual_source_blending_is_enabled = enable; - // Set for any existing documents. - for (_, doc) in &mut self.documents { - doc.frame_builder_config.dual_source_blending_is_enabled = enable; - } - self.scene_tx.send(SceneBuilderRequest::SetFrameBuilderConfig( self.frame_config.clone() )).unwrap(); @@ -1302,7 +1291,6 @@ impl RenderBackend { view: view.clone(), clip_scroll_tree: ClipScrollTree::new(), frame_id: FrameId(0), - frame_builder_config: self.frame_config.clone(), frame_builder: Some(FrameBuilder::empty()), output_pipelines: FastHashSet::default(), render_on_scroll: None, From 171249fea70056c18c035271c47133526bb7c611 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 30 Aug 2018 18:41:09 +0200 Subject: [PATCH 3/7] Move scene loading code behind the "replay" feature flag. --- webrender/src/scene_builder.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index 16a62f49f3..f0533e65d5 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -81,7 +81,7 @@ pub struct SceneRequest { pub scene_id: u64, } -/// For the replay feature. +#[cfg(feature = "replay")] pub struct LoadScene { pub document_id: DocumentId, pub scene: Scene, @@ -102,12 +102,13 @@ pub struct BuiltScene { // Message from render backend to scene builder. pub enum SceneBuilderRequest { Transaction(Box), - LoadScenes(Vec), DeleteDocument(DocumentId), WakeUp, Flush(MsgSender<()>), SetFrameBuilderConfig(FrameBuilderConfig), - Stop + Stop, + #[cfg(feature = "replay")] + LoadScenes(Vec), } // Message from scene builder to render backend. @@ -176,12 +177,13 @@ impl SceneBuilder { Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { self.documents.remove(&document_id); } - Ok(SceneBuilderRequest::LoadScenes(msg)) => { - self.load_scenes(msg); - } Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => { self.config = cfg; } + #[cfg(feature = "replay")] + Ok(SceneBuilderRequest::LoadScenes(msg)) => { + self.load_scenes(msg); + } Ok(SceneBuilderRequest::Stop) => { self.tx.send(SceneBuilderResult::Stopped).unwrap(); // We don't need to send a WakeUp to api_tx because we only @@ -203,7 +205,7 @@ impl SceneBuilder { } } - // Specific to the replay functionality. + #[cfg(feature = "replay")] fn load_scenes(&mut self, scenes: Vec) { for item in scenes { self.config = item.config; From 38b0eb375618f17bcbf5830503234df23071a071 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Fri, 31 Aug 2018 16:32:53 +0200 Subject: [PATCH 4/7] Fix confusion with generate frame/render/composite terminology. From now on, generate frame means having the frame builder generate a frame (but not necessarily render it), and render means asking the renderer to render a frame. --- webrender/src/render_backend.rs | 72 ++++++++++++++++----------------- webrender/src/scene_builder.rs | 11 ++--- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index a107d22016..d13500bc91 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -147,7 +147,9 @@ impl Document { } } - fn can_render(&self) -> bool { self.frame_builder.is_some() } + fn can_render(&self) -> bool { + self.frame_builder.is_some() && self.scene.has_root_pipeline() + } fn has_pixels(&self) -> bool { !self.view.window_size.is_empty_or_negative() @@ -192,8 +194,8 @@ impl Document { return DocumentOps { scroll: true, + generate_frame: should_render, render: should_render, - composite: should_render, ..DocumentOps::nop() }; } @@ -219,8 +221,8 @@ impl Document { return DocumentOps { scroll: true, + generate_frame: should_render, render: should_render, - composite: should_render, ..DocumentOps::nop() }; } @@ -327,24 +329,18 @@ impl Document { struct DocumentOps { scroll: bool, + generate_frame: bool, render: bool, - composite: bool, } impl DocumentOps { fn nop() -> Self { DocumentOps { scroll: false, + generate_frame: false, render: false, - composite: false, } } - - fn combine(&mut self, other: Self) { - self.scroll = self.scroll || other.scroll; - self.render = self.render || other.render; - self.composite = self.composite || other.composite; - } } /// The unique id for WR resource identification. @@ -564,6 +560,7 @@ impl RenderBackend { while let Ok(msg) = self.scene_rx.try_recv() { match msg { SceneBuilderResult::Transaction(mut txn, result_tx) => { + let has_built_scene = txn.built_scene.is_some(); if let Some(doc) = self.documents.get_mut(&txn.document_id) { doc.removed_pipelines.append(&mut txn.removed_pipelines); @@ -598,15 +595,16 @@ impl RenderBackend { self.resource_cache.set_blob_rasterizer(rasterizer); } - if txn.render || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() { + if txn.generate_frame || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() { self.update_document( txn.document_id, replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), + txn.generate_frame, txn.render, &mut frame_counter, &mut profile_counters, - true, + has_built_scene, ); } }, @@ -838,6 +836,7 @@ impl RenderBackend { resource_updates: transaction_msg.resource_updates, frame_ops: transaction_msg.frame_ops, set_root_pipeline: None, + generate_frame: transaction_msg.generate_frame, render: transaction_msg.generate_frame, }); @@ -867,11 +866,11 @@ impl RenderBackend { } if !transaction_msg.use_scene_builder_thread && txn.can_skip_scene_builder() { - self.update_document( txn.document_id, replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), + txn.generate_frame, txn.render, frame_counter, profile_counters, @@ -901,12 +900,12 @@ impl RenderBackend { document_id: DocumentId, resource_updates: Vec, mut frame_ops: Vec, - generate_frame: bool, + mut generate_frame: bool, + mut render: bool, frame_counter: &mut u32, profile_counters: &mut BackendProfileCounters, has_built_scene: bool, ) { - self.resource_cache.post_scene_building_update( resource_updates, &mut profile_counters.resources, @@ -923,45 +922,46 @@ impl RenderBackend { } } - let mut op = DocumentOps { - render: generate_frame, - ..DocumentOps::nop() - }; let doc = self.documents.get_mut(&document_id).unwrap(); + let mut scroll = false; for frame_msg in frame_ops { let _timer = profile_counters.total_time.timer(); - op.combine(doc.process_frame_msg(frame_msg)); + let op = doc.process_frame_msg(frame_msg); + generate_frame |= op.generate_frame; + render |= op.render; + scroll |= op.scroll; } + // After applying the new scene we need to + // rebuild the hit-tester, so we trigger a frame generation + // step. + generate_frame |= has_built_scene; + if doc.dynamic_properties.flush_pending_updates() { - op.render = true; + generate_frame = true; } - if op.render { + if render { if let Some(ref mut ros) = doc.render_on_scroll { *ros = true; } - - if doc.scene.root_pipeline_id.is_some() { - op.render = true; - op.composite = true; - } } if !doc.can_render() { // TODO: this happens if we are building the first scene asynchronously and // scroll at the same time. we should keep track of the fact that we skipped // composition here and do it as soon as we receive the scene. - op.render = false; - op.composite = false; + generate_frame = false; + render = false; } - debug_assert!(op.render || !op.composite); + // If we don't generate a frame it makes no sense to render. + debug_assert!(generate_frame || !render); let mut render_time = None; - if op.render && doc.has_pixels() { + if generate_frame && doc.has_pixels() { profile_scope!("generate frame"); *frame_counter += 1; @@ -1002,17 +1002,17 @@ impl RenderBackend { ); self.result_tx.send(msg).unwrap(); profile_counters.reset(); - } else if op.render { + } else if generate_frame { // WR-internal optimization to avoid doing a bunch of render work if // there's no pixels. We still want to pretend to render and request - // a composite to make sure that the callbacks (particularly the + // a render to make sure that the callbacks (particularly the // new_frame_ready callback below) has the right flags. let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info()); self.result_tx.send(msg).unwrap(); } - if op.render { - self.notifier.new_frame_ready(document_id, op.scroll, op.composite, render_time); + if render { + self.notifier.new_frame_ready(document_id, scroll, render, render_time); } } diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index f0533e65d5..340f83f3a3 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -30,6 +30,7 @@ pub struct Transaction { pub resource_updates: Vec, pub frame_ops: Vec, pub set_root_pipeline: Option, + pub generate_frame: bool, pub render: bool, } @@ -61,6 +62,7 @@ pub struct BuiltTransaction { pub removed_pipelines: Vec, pub scene_build_start_time: u64, pub scene_build_end_time: u64, + pub generate_frame: bool, pub render: bool, } @@ -240,6 +242,7 @@ impl SceneBuilder { let txn = Box::new(BuiltTransaction { document_id: item.document_id, + generate_frame: true, render: item.generate_frame, built_scene, resource_updates: Vec::new(), @@ -257,6 +260,7 @@ impl SceneBuilder { /// Do the bulk of the work of the scene builder thread. fn process_transaction(&mut self, txn: &mut Transaction) -> Box { + let scene_build_start_time = precise_time_ns(); let scene = self.documents.entry(txn.document_id).or_insert(Scene::new()); @@ -316,13 +320,10 @@ impl SceneBuilder { |rasterizer| rasterizer.rasterize(&blob_requests), ); - // After applying the new scene we need to - // rebuild the hit-tester, so we trigger a render - // step. - Box::new(BuiltTransaction { document_id: txn.document_id, - render: txn.render || built_scene.is_some(), + generate_frame: txn.generate_frame || built_scene.is_some(), + render: txn.render, built_scene, resource_updates: replace(&mut txn.resource_updates, Vec::new()), rasterized_blobs: rasterized_blobs, From 7c448fbc06b7c421ce64b0520cf5d71482b03eb4 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 4 Sep 2018 11:23:03 +0200 Subject: [PATCH 5/7] Rename generate_frame into build_frame. --- webrender/src/render_backend.rs | 41 ++++++++++++++++++--------------- webrender/src/scene_builder.rs | 12 +++++----- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index d13500bc91..7fbda15a7d 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -194,7 +194,7 @@ impl Document { return DocumentOps { scroll: true, - generate_frame: should_render, + build_frame: should_render, render: should_render, ..DocumentOps::nop() }; @@ -221,7 +221,7 @@ impl Document { return DocumentOps { scroll: true, - generate_frame: should_render, + build_frame: should_render, render: should_render, ..DocumentOps::nop() }; @@ -329,7 +329,7 @@ impl Document { struct DocumentOps { scroll: bool, - generate_frame: bool, + build_frame: bool, render: bool, } @@ -337,7 +337,7 @@ impl DocumentOps { fn nop() -> Self { DocumentOps { scroll: false, - generate_frame: false, + build_frame: false, render: false, } } @@ -595,12 +595,12 @@ impl RenderBackend { self.resource_cache.set_blob_rasterizer(rasterizer); } - if txn.generate_frame || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() { + if txn.build_frame || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() { self.update_document( txn.document_id, replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), - txn.generate_frame, + txn.build_frame, txn.render, &mut frame_counter, &mut profile_counters, @@ -836,7 +836,7 @@ impl RenderBackend { resource_updates: transaction_msg.resource_updates, frame_ops: transaction_msg.frame_ops, set_root_pipeline: None, - generate_frame: transaction_msg.generate_frame, + build_frame: transaction_msg.generate_frame, render: transaction_msg.generate_frame, }); @@ -870,7 +870,7 @@ impl RenderBackend { txn.document_id, replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), - txn.generate_frame, + txn.build_frame, txn.render, frame_counter, profile_counters, @@ -900,7 +900,7 @@ impl RenderBackend { document_id: DocumentId, resource_updates: Vec, mut frame_ops: Vec, - mut generate_frame: bool, + mut build_frame: bool, mut render: bool, frame_counter: &mut u32, profile_counters: &mut BackendProfileCounters, @@ -916,7 +916,7 @@ impl RenderBackend { // fiddle with things after a potentially long scene build, but just // before rendering. This is useful for rendering with the latest // async transforms. - if generate_frame { + if build_frame { if let Some(ref sampler) = self.sampler { frame_ops.append(&mut sampler.sample()); } @@ -929,7 +929,7 @@ impl RenderBackend { for frame_msg in frame_ops { let _timer = profile_counters.total_time.timer(); let op = doc.process_frame_msg(frame_msg); - generate_frame |= op.generate_frame; + build_frame |= op.build_frame; render |= op.render; scroll |= op.scroll; } @@ -937,10 +937,13 @@ impl RenderBackend { // After applying the new scene we need to // rebuild the hit-tester, so we trigger a frame generation // step. - generate_frame |= has_built_scene; + // + // TODO: We could avoid some the cost of building the frame by only + // building the information required for hit-testing (See #2807). + build_frame |= has_built_scene; if doc.dynamic_properties.flush_pending_updates() { - generate_frame = true; + build_frame = true; } if render { @@ -953,15 +956,15 @@ impl RenderBackend { // TODO: this happens if we are building the first scene asynchronously and // scroll at the same time. we should keep track of the fact that we skipped // composition here and do it as soon as we receive the scene. - generate_frame = false; + build_frame = false; render = false; } // If we don't generate a frame it makes no sense to render. - debug_assert!(generate_frame || !render); + debug_assert!(build_frame || !render); let mut render_time = None; - if generate_frame && doc.has_pixels() { + if build_frame && doc.has_pixels() { profile_scope!("generate frame"); *frame_counter += 1; @@ -1002,7 +1005,7 @@ impl RenderBackend { ); self.result_tx.send(msg).unwrap(); profile_counters.reset(); - } else if generate_frame { + } else if build_frame { // WR-internal optimization to avoid doing a bunch of render work if // there's no pixels. We still want to pretend to render and request // a render to make sure that the callbacks (particularly the @@ -1300,7 +1303,7 @@ impl RenderBackend { let frame_name = format!("frame-{}-{}", (id.0).0, id.1); let frame = CaptureConfig::deserialize::(root, frame_name); - let generate_frame = match frame { + let build_frame = match frame { Some(frame) => { info!("\tloaded a built frame with {} passes", frame.passes.len()); @@ -1335,7 +1338,7 @@ impl RenderBackend { output_pipelines: doc.output_pipelines.clone(), font_instances: self.resource_cache.get_font_instances(), scene_id: last_scene_id, - generate_frame, + build_frame, }); self.documents.insert(id, doc); diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index 340f83f3a3..2e78bb93b4 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -30,7 +30,7 @@ pub struct Transaction { pub resource_updates: Vec, pub frame_ops: Vec, pub set_root_pipeline: Option, - pub generate_frame: bool, + pub build_frame: bool, pub render: bool, } @@ -62,7 +62,7 @@ pub struct BuiltTransaction { pub removed_pipelines: Vec, pub scene_build_start_time: u64, pub scene_build_end_time: u64, - pub generate_frame: bool, + pub build_frame: bool, pub render: bool, } @@ -92,7 +92,7 @@ pub struct LoadScene { pub font_instances: FontInstanceMap, pub view: DocumentView, pub config: FrameBuilderConfig, - pub generate_frame: bool, + pub build_frame: bool, } pub struct BuiltScene { @@ -242,8 +242,8 @@ impl SceneBuilder { let txn = Box::new(BuiltTransaction { document_id: item.document_id, - generate_frame: true, - render: item.generate_frame, + build_frame: true, + render: item.build_frame, built_scene, resource_updates: Vec::new(), rasterized_blobs: Vec::new(), @@ -322,7 +322,7 @@ impl SceneBuilder { Box::new(BuiltTransaction { document_id: txn.document_id, - generate_frame: txn.generate_frame || built_scene.is_some(), + build_frame: txn.build_frame || built_scene.is_some(), render: txn.render, built_scene, resource_updates: replace(&mut txn.resource_updates, Vec::new()), From ede7c8c4bfde741ac6007cb310a3a8efcb44c3dc Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 4 Sep 2018 18:22:26 +0200 Subject: [PATCH 6/7] Rename Transaction's render into render_frame. This is to avoid confusion with the previous meaning of render which was about building the frame rather than rendering it. --- webrender/src/render_backend.rs | 34 ++++++++++++++++----------------- webrender/src/scene_builder.rs | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 7fbda15a7d..544a5a3e1c 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -195,7 +195,7 @@ impl Document { return DocumentOps { scroll: true, build_frame: should_render, - render: should_render, + render_frame: should_render, ..DocumentOps::nop() }; } @@ -222,7 +222,7 @@ impl Document { return DocumentOps { scroll: true, build_frame: should_render, - render: should_render, + render_frame: should_render, ..DocumentOps::nop() }; } @@ -330,7 +330,7 @@ impl Document { struct DocumentOps { scroll: bool, build_frame: bool, - render: bool, + render_frame: bool, } impl DocumentOps { @@ -338,7 +338,7 @@ impl DocumentOps { DocumentOps { scroll: false, build_frame: false, - render: false, + render_frame: false, } } } @@ -601,7 +601,7 @@ impl RenderBackend { replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), txn.build_frame, - txn.render, + txn.render_frame, &mut frame_counter, &mut profile_counters, has_built_scene, @@ -837,7 +837,7 @@ impl RenderBackend { frame_ops: transaction_msg.frame_ops, set_root_pipeline: None, build_frame: transaction_msg.generate_frame, - render: transaction_msg.generate_frame, + render_frame: transaction_msg.generate_frame, }); self.resource_cache.pre_scene_building_update( @@ -871,7 +871,7 @@ impl RenderBackend { replace(&mut txn.resource_updates, Vec::new()), replace(&mut txn.frame_ops, Vec::new()), txn.build_frame, - txn.render, + txn.render_frame, frame_counter, profile_counters, false @@ -901,7 +901,7 @@ impl RenderBackend { resource_updates: Vec, mut frame_ops: Vec, mut build_frame: bool, - mut render: bool, + mut render_frame: bool, frame_counter: &mut u32, profile_counters: &mut BackendProfileCounters, has_built_scene: bool, @@ -930,7 +930,7 @@ impl RenderBackend { let _timer = profile_counters.total_time.timer(); let op = doc.process_frame_msg(frame_msg); build_frame |= op.build_frame; - render |= op.render; + render_frame |= op.render_frame; scroll |= op.scroll; } @@ -946,7 +946,7 @@ impl RenderBackend { build_frame = true; } - if render { + if render_frame { if let Some(ref mut ros) = doc.render_on_scroll { *ros = true; } @@ -957,13 +957,13 @@ impl RenderBackend { // scroll at the same time. we should keep track of the fact that we skipped // composition here and do it as soon as we receive the scene. build_frame = false; - render = false; + render_frame = false; } // If we don't generate a frame it makes no sense to render. - debug_assert!(build_frame || !render); + debug_assert!(build_frame || !render_frame); - let mut render_time = None; + let mut frame_build_time = None; if build_frame && doc.has_pixels() { profile_scope!("generate frame"); @@ -972,7 +972,7 @@ impl RenderBackend { // borrow ck hack for profile_counters let (pending_update, rendered_document) = { let _timer = profile_counters.total_time.timer(); - let render_start_time = precise_time_ns(); + let frame_build_start_time = precise_time_ns(); let rendered_document = doc.render( &mut self.resource_cache, @@ -987,7 +987,7 @@ impl RenderBackend { let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates()); self.result_tx.send(msg).unwrap(); - render_time = Some(precise_time_ns() - render_start_time); + frame_build_time = Some(precise_time_ns() - frame_build_start_time); let pending_update = self.resource_cache.pending_updates(); (pending_update, rendered_document) @@ -1014,8 +1014,8 @@ impl RenderBackend { self.result_tx.send(msg).unwrap(); } - if render { - self.notifier.new_frame_ready(document_id, scroll, render, render_time); + if render_frame { + self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time); } } diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index 2e78bb93b4..4ecec13963 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -31,7 +31,7 @@ pub struct Transaction { pub frame_ops: Vec, pub set_root_pipeline: Option, pub build_frame: bool, - pub render: bool, + pub render_frame: bool, } impl Transaction { @@ -63,7 +63,7 @@ pub struct BuiltTransaction { pub scene_build_start_time: u64, pub scene_build_end_time: u64, pub build_frame: bool, - pub render: bool, + pub render_frame: bool, } pub struct DisplayListUpdate { @@ -243,7 +243,7 @@ impl SceneBuilder { let txn = Box::new(BuiltTransaction { document_id: item.document_id, build_frame: true, - render: item.build_frame, + render_frame: item.build_frame, built_scene, resource_updates: Vec::new(), rasterized_blobs: Vec::new(), @@ -323,7 +323,7 @@ impl SceneBuilder { Box::new(BuiltTransaction { document_id: txn.document_id, build_frame: txn.build_frame || built_scene.is_some(), - render: txn.render, + render_frame: txn.render_frame, built_scene, resource_updates: replace(&mut txn.resource_updates, Vec::new()), rasterized_blobs: rasterized_blobs, From 32bbbe8448a7353948c93c64018460b1aa1fbfa4 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 4 Sep 2018 18:43:28 +0200 Subject: [PATCH 7/7] Rename Document::render into Document::build_frame. --- webrender/src/render_backend.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 544a5a3e1c..6363fdf75f 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -242,7 +242,7 @@ impl Document { } - fn render( + fn build_frame( &mut self, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, @@ -974,7 +974,7 @@ impl RenderBackend { let _timer = profile_counters.total_time.timer(); let frame_build_start_time = precise_time_ns(); - let rendered_document = doc.render( + let rendered_document = doc.build_frame( &mut self.resource_cache, &mut self.gpu_cache, &mut profile_counters.resources, @@ -1192,7 +1192,7 @@ impl RenderBackend { config.serialize(&doc.scene, file_name); } if config.bits.contains(CaptureBits::FRAME) { - let rendered_document = doc.render( + let rendered_document = doc.build_frame( &mut self.resource_cache, &mut self.gpu_cache, &mut profile_counters.resources,