Bug 1462611 - Update webrender to commit bb354abbf84602d3d8357c63c4f0b1139ec4deb1. r?Gankro draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 22 May 2018 09:38:19 -0400
changeset 798100 013cc78726297d638b2076c177f14169f73c7d8d
parent 798084 b75acf9652937ce79a9bf02de843c100db0e5ec7
push id110684
push userkgupta@mozilla.com
push dateTue, 22 May 2018 13:38:41 +0000
reviewersGankro
bugs1462611
milestone62.0a1
Bug 1462611 - Update webrender to commit bb354abbf84602d3d8357c63c4f0b1139ec4deb1. r?Gankro MozReview-Commit-ID: KiJVmU52MMd
gfx/webrender/src/internal_types.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender_bindings/revision.txt
gfx/wrench/src/rawtest.rs
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -124,35 +124,26 @@ impl TextureUpdateList {
     }
 
     #[inline]
     pub fn push(&mut self, update: TextureUpdate) {
         self.updates.push(update);
     }
 }
 
-/// Mostly wraps a tiling::Frame, adding a bit of extra information.
+/// Wraps a tiling::Frame, but conceptually could hold more information
 pub struct RenderedDocument {
-    /// The pipeline info contains:
-    /// - The last rendered epoch for each pipeline present in the frame.
-    /// This information is used to know if a certain transformation on the layout has
-    /// been rendered, which is necessary for reftests.
-    /// - Pipelines that were removed from the scene.
-    pub pipeline_info: PipelineInfo,
-
     pub frame: tiling::Frame,
 }
 
 impl RenderedDocument {
     pub fn new(
-        pipeline_info: PipelineInfo,
         frame: tiling::Frame,
     ) -> Self {
         RenderedDocument {
-            pipeline_info,
             frame,
         }
     }
 }
 
 pub enum DebugOutput {
     FetchDocuments(String),
     FetchClipScrollTree(String),
@@ -166,16 +157,17 @@ pub enum ResultMsg {
     DebugCommand(DebugCommand),
     DebugOutput(DebugOutput),
     RefreshShader(PathBuf),
     UpdateGpuCache(GpuCacheUpdateList),
     UpdateResources {
         updates: TextureUpdateList,
         cancel_rendering: bool,
     },
+    PublishPipelineInfo(PipelineInfo),
     PublishDocument(
         DocumentId,
         RenderedDocument,
         TextureUpdateList,
         BackendProfileCounters,
     ),
 }
 
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -37,16 +37,17 @@ use serde::{Serialize, Deserialize};
 #[cfg(feature = "debugger")]
 use serde_json;
 #[cfg(any(feature = "capture", feature = "replay"))]
 use std::path::PathBuf;
 use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
 use std::mem::replace;
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::u32;
+#[cfg(feature = "replay")]
 use tiling::Frame;
 use time::precise_time_ns;
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Clone)]
 pub struct DocumentView {
     pub window_size: DeviceUintSize,
@@ -165,23 +166,25 @@ impl Document {
             render_on_hittest: false,
             hit_tester: None,
             dynamic_properties: SceneProperties::new(),
         }
     }
 
     fn can_render(&self) -> bool { self.frame_builder.is_some() }
 
+    fn has_pixels(&self) -> bool {
+        !self.view.window_size.is_empty_or_negative()
+    }
+
     // TODO: We will probably get rid of this soon and always forward to the scene building thread.
     fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
         let max_texture_size = resource_cache.max_texture_size();
 
-        if self.view.window_size.width == 0 ||
-           self.view.window_size.height == 0 ||
-           self.view.window_size.width > max_texture_size ||
+        if self.view.window_size.width > max_texture_size ||
            self.view.window_size.height > max_texture_size {
             error!("ERROR: Invalid window dimensions {}x{}. Please call api.set_window_size()",
                 self.view.window_size.width,
                 self.view.window_size.height,
             );
 
             return;
         }
@@ -236,20 +239,16 @@ impl Document {
         // 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 {
-            if self.view.window_size.width == 0 || self.view.window_size.height == 0 {
-                error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
-            }
-
             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(),
             })
         } else {
@@ -268,17 +267,16 @@ impl Document {
     fn render(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         resource_profile: &mut ResourceProfileCounters,
     ) -> RenderedDocument {
         let accumulated_scale_factor = self.view.accumulated_scale_factor();
         let pan = self.view.pan.to_f32() / accumulated_scale_factor;
-        let removed_pipelines = replace(&mut self.current.removed_pipelines, Vec::new());
 
         let frame = {
             let frame_builder = self.frame_builder.as_mut().unwrap();
             let frame = frame_builder.build(
                 resource_cache,
                 gpu_cache,
                 self.frame_id,
                 &mut self.clip_scroll_tree,
@@ -289,27 +287,25 @@ impl Document {
                 &mut resource_profile.texture_cache,
                 &mut resource_profile.gpu_cache,
                 &self.dynamic_properties,
             );
             self.hit_tester = Some(frame_builder.create_hit_tester(&self.clip_scroll_tree));
             frame
         };
 
-        self.make_rendered_document(frame, removed_pipelines)
+        RenderedDocument::new(frame)
     }
 
-    pub fn make_rendered_document(&mut self, frame: Frame, removed_pipelines: Vec<PipelineId>) -> RenderedDocument {
-        RenderedDocument::new(
-            PipelineInfo {
-                epochs: self.current.scene.pipeline_epochs.clone(),
-                removed_pipelines,
-            },
-            frame
-        )
+    pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
+        let removed_pipelines = replace(&mut self.current.removed_pipelines, Vec::new());
+        PipelineInfo {
+            epochs: self.current.scene.pipeline_epochs.clone(),
+            removed_pipelines,
+        }
     }
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.clip_scroll_tree
             .discard_frame_state_for_pipeline(pipeline_id);
     }
 
     /// Returns true if any nodes actually changed position or false otherwise.
@@ -1039,17 +1035,17 @@ 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.
             op.render = false;
             op.composite = false;
         }
 
         debug_assert!(op.render || !op.composite);
 
-        if op.render {
+        if op.render && doc.has_pixels() {
             profile_scope!("generate frame");
 
             *frame_counter += 1;
 
             // borrow ck hack for profile_counters
             let (pending_update, rendered_document) = {
                 let _timer = profile_counters.total_time.timer();
 
@@ -1064,26 +1060,36 @@ impl RenderBackend {
 
                 let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
                 self.result_tx.send(msg).unwrap();
 
                 let pending_update = self.resource_cache.pending_updates();
                 (pending_update, rendered_document)
             };
 
+            let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
+            self.result_tx.send(msg).unwrap();
+
             // Publish the frame
             let msg = ResultMsg::PublishDocument(
                 document_id,
                 rendered_document,
                 pending_update,
                 profile_counters.clone()
             );
             self.result_tx.send(msg).unwrap();
             profile_counters.reset();
             doc.render_on_hittest = false;
+        } else if op.render {
+            // 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
+            // new_frame_ready callback below) has the right flags.
+            let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
+            self.result_tx.send(msg).unwrap();
         }
 
         if transaction_msg.generate_frame {
             self.notifier.new_frame_ready(document_id, op.scroll, op.composite);
         }
     }
 
     #[cfg(not(feature = "debugger"))]
@@ -1235,17 +1241,17 @@ impl RenderBackend {
                 config.serialize(&doc.current.scene, file_name);
             }
             if config.bits.contains(CaptureBits::FRAME) {
                 let rendered_document = doc.render(
                     &mut self.resource_cache,
                     &mut self.gpu_cache,
                     &mut profile_counters.resources,
                 );
-                //TODO: write down full `RenderedDocument`?
+                //TODO: write down doc's pipeline info?
                 // it has `pipeline_epoch_map`,
                 // which may capture necessary details for some cases.
                 let file_name = format!("frame-{}-{}", (id.0).0, id.1);
                 config.serialize(&rendered_document.frame, file_name);
             }
         }
 
         debug!("\tresource cache");
@@ -1346,17 +1352,17 @@ impl RenderBackend {
                 dynamic_properties: SceneProperties::new(),
                 hit_tester: None,
             };
 
             let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
             let render_doc = match CaptureConfig::deserialize::<Frame, _>(root, frame_name) {
                 Some(frame) => {
                     info!("\tloaded a built frame with {} passes", frame.passes.len());
-                    doc.make_rendered_document(frame, Vec::new())
+                    RenderedDocument::new(frame)
                 }
                 None => {
                     doc.build_scene(&mut self.resource_cache);
                     doc.render(
                         &mut self.resource_cache,
                         &mut self.gpu_cache,
                         &mut profile_counters.resources,
                     )
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -1793,29 +1793,28 @@ impl Renderer {
     /// Processes the result queue.
     ///
     /// Should be called before `render()`, as texture cache updates are done here.
     pub fn update(&mut self) {
         profile_scope!("update");
         // Pull any pending results and return the most recent.
         while let Ok(msg) = self.result_rx.try_recv() {
             match msg {
+                ResultMsg::PublishPipelineInfo(mut pipeline_info) => {
+                    for (pipeline_id, epoch) in pipeline_info.epochs {
+                        self.pipeline_info.epochs.insert(pipeline_id, epoch);
+                    }
+                    self.pipeline_info.removed_pipelines.extend(pipeline_info.removed_pipelines.drain(..));
+                }
                 ResultMsg::PublishDocument(
                     document_id,
                     mut doc,
                     texture_update_list,
                     profile_counters,
                 ) => {
-                    // Update the list of available epochs for use during reftests.
-                    // This is a workaround for https://github.com/servo/servo/issues/13149.
-                    for (pipeline_id, epoch) in &doc.pipeline_info.epochs {
-                        self.pipeline_info.epochs.insert(*pipeline_id, *epoch);
-                    }
-                    self.pipeline_info.removed_pipelines.extend(doc.pipeline_info.removed_pipelines.drain(..));
-
                     // Add a new document to the active set, expressed as a `Vec` in order
                     // to re-order based on `DocumentLayer` during rendering.
                     match self.active_documents.iter().position(|&(id, _)| id == document_id) {
                         Some(pos) => {
                             // If the document we are replacing must be drawn
                             // (in order to update the texture cache), issue
                             // a render just to off-screen targets.
                             if self.active_documents[pos].1.frame.must_be_drawn() {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-672f480af48b0ad69c1b2781151278d99816763a
+bb354abbf84602d3d8357c63c4f0b1139ec4deb1
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -42,16 +42,17 @@ impl<'a> RawtestHarness<'a> {
         self.test_hit_testing();
         self.test_retained_blob_images_test();
         self.test_blob_update_test();
         self.test_blob_update_epoch_test();
         self.test_tile_decomposition();
         self.test_very_large_blob();
         self.test_save_restore();
         self.test_capture();
+        self.test_zero_height_window();
     }
 
     fn render_and_get_pixels(&mut self, window_rect: DeviceUintRect) -> Vec<u8> {
         self.rx.recv().unwrap();
         self.wrench.render();
         self.wrench.renderer.read_pixels_rgba8(window_rect)
     }
 
@@ -654,16 +655,44 @@ impl<'a> RawtestHarness<'a> {
         let mut txn = Transaction::new();
         txn.set_root_pipeline(captured.root_pipeline_id.unwrap());
         txn.generate_frame();
         self.wrench.api.send_transaction(captured.document_id, txn);
         let pixels2 = self.render_and_get_pixels(window_rect);
         assert!(pixels0 == pixels2);
     }
 
+    fn test_zero_height_window(&mut self) {
+        println!("\tzero height test...");
+
+        let layout_size = LayoutSize::new(120.0, 0.0);
+        let window_size = DeviceUintSize::new(layout_size.width as u32, layout_size.height as u32);
+        let doc_id = self.wrench.api.add_document(window_size, 1);
+
+        let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+        let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0)));
+        builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
+
+        let mut txn = Transaction::new();
+        txn.set_root_pipeline(self.wrench.root_pipeline_id);
+        txn.set_display_list(
+            Epoch(1),
+            Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
+            layout_size,
+            builder.finalize(),
+            false,
+        );
+        txn.generate_frame();
+        self.wrench.api.send_transaction(doc_id, txn);
+
+        // Ensure we get a notification from rendering the above, even though
+        // there are zero visible pixels
+        assert!(self.rx.recv().unwrap() == NotifierEvent::WakeUp);
+    }
+
 
     fn test_hit_testing(&mut self) {
         println!("\thit testing test...");
 
         let layout_size = LayoutSize::new(400., 400.);
         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
 
         // Add a rectangle that covers the entire scene.