Bug 1601868 - Fix composition skips when scrollbars are disabled. r=nical
authorGlenn Watson <git@intuitionlibrary.com>
Mon, 09 Dec 2019 09:06:49 +0000
changeset 505986 bdb6fc50b1976cb7939b729dd7bad4ceb0d040a4
parent 505985 7f8a7ac4bfe49199d34e8581d1fbbce4e25b9650
child 505987 260044e4e029d7e69f03ec80c1c006699fc115e0
push id36895
push userccoroiu@mozilla.com
push dateMon, 09 Dec 2019 16:35:51 +0000
treeherdermozilla-central@666b11c33fee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1601868
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1601868 - Fix composition skips when scrollbars are disabled. r=nical Differential Revision: https://phabricator.services.mozilla.com/D56309
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/composite.rs
gfx/wr/webrender/src/render_backend.rs
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1249,16 +1249,17 @@ impl BatchBuilder {
                                     };
 
                                     let tile = CompositeTile {
                                         surface,
                                         rect: device_rect,
                                         dirty_rect,
                                         clip_rect: device_clip_rect,
                                         z_id,
+                                        tile_id: tile.id,
                                     };
 
                                     composite_state.push_tile(tile, is_opaque);
                                 }
                             }
                             PictureCompositeMode::Filter(ref filter) => {
                                 assert!(filter.is_visible());
                                 match filter {
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::ColorF;
 use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect, DevicePixelScale};
 use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
-use crate::picture::{ResolvedSurfaceTexture};
+use crate::picture::{ResolvedSurfaceTexture, TileId};
 use std::{ops, u64};
 
 /*
  Types and definitions related to compositing picture cache tiles
  and/or OS compositor integration.
  */
 
 /// Describes details of an operation to apply to a native surface
@@ -53,16 +53,17 @@ pub enum CompositeTileSurface {
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CompositeTile {
     pub surface: CompositeTileSurface,
     pub rect: DeviceRect,
     pub clip_rect: DeviceRect,
     pub dirty_rect: DeviceRect,
     pub z_id: ZBufferId,
+    pub tile_id: TileId,
 }
 
 /// Public interface specified in `RendererOptions` that configures
 /// how WR compositing will operate.
 pub enum CompositorConfig {
     /// Let WR draw tiles via normal batching. This requires no special OS support.
     Draw {
         /// If this is zero, a full screen present occurs at the end of the
@@ -124,16 +125,40 @@ impl Default for CompositorKind {
 /// Information about an opaque surface used to occlude tiles.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct Occluder {
     slice: usize,
     device_rect: DeviceIntRect,
 }
 
+/// Describes the properties that identify a tile composition uniquely.
+#[derive(PartialEq)]
+pub struct CompositeTileDescriptor {
+    pub rect: DeviceRect,
+    pub clip_rect: DeviceRect,
+    pub tile_id: TileId,
+}
+
+/// Describes which tiles and properties were used to composite a frame. This
+/// is used to compare compositions between frames.
+#[derive(PartialEq)]
+pub struct CompositeDescriptor {
+    tiles: Vec<CompositeTileDescriptor>,
+}
+
+impl CompositeDescriptor {
+    /// Construct an empty descriptor.
+    pub fn empty() -> Self {
+        CompositeDescriptor {
+            tiles: Vec::new(),
+        }
+    }
+}
+
 /// The list of tiles to be drawn this frame
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CompositeState {
     // TODO(gw): Consider splitting up CompositeState into separate struct types depending
     //           on the selected compositing mode. Many of the fields in this state struct
     //           are only applicable to either Native or Draw compositing mode.
     /// List of tiles to be drawn by the native compositor. These are added in draw order
@@ -190,16 +215,39 @@ impl CompositeState {
             dirty_rects_are_valid: true,
             compositor_kind,
             picture_caching_is_enabled,
             global_device_pixel_scale,
             occluders: Vec::new(),
         }
     }
 
+    /// Construct a descriptor of this composition state. Used for comparing
+    /// composition states between frames.
+    pub fn create_descriptor(&self) -> CompositeDescriptor {
+        let mut tiles = Vec::new();
+
+        let native_iter = self.native_tiles.iter();
+        let opaque_iter = self.opaque_tiles.iter();
+        let clear_iter = self.clear_tiles.iter();
+        let alpha_iter = self.alpha_tiles.iter();
+
+        for tile in native_iter.chain(opaque_iter).chain(clear_iter).chain(alpha_iter) {
+            tiles.push(CompositeTileDescriptor {
+                rect: tile.rect,
+                clip_rect: tile.clip_rect,
+                tile_id: tile.tile_id,
+            });
+        }
+
+        CompositeDescriptor {
+            tiles,
+        }
+    }
+
     /// Register an occluder during picture cache updates that can be
     /// used during frame building to occlude tiles.
     pub fn register_occluder(
         &mut self,
         slice: usize,
         rect: WorldRect,
     ) {
         let device_rect = (rect * self.global_device_pixel_scale).round().to_i32();
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -16,17 +16,17 @@ use api::{NotificationRequest, Checkpoin
 use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind};
 use api::units::*;
 use api::channel::{MsgReceiver, MsgSender, Payload};
 #[cfg(feature = "capture")]
 use api::CaptureBits;
 #[cfg(feature = "replay")]
 use api::CapturedDocument;
 use crate::clip_scroll_tree::SpatialNodeIndex;
-use crate::composite::CompositorKind;
+use crate::composite::{CompositorKind, CompositeDescriptor};
 #[cfg(feature = "debugger")]
 use crate::debug_server;
 use crate::frame_builder::{FrameBuilder, FrameBuilderConfig};
 use crate::glyph_rasterizer::{FontInstance};
 use crate::gpu_cache::GpuCache;
 use crate::hit_test::{HitTest, HitTester};
 use crate::intern::DataStore;
 use crate::internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
@@ -377,16 +377,19 @@ struct Document {
     /// re-allocating and moving memory around.
     scratch: PrimitiveScratchBuffer,
     /// Keep track of the size of render task graph to pre-allocate memory up-front
     /// the next frame.
     render_task_counters: RenderTaskGraphCounters,
 
     #[cfg(feature = "replay")]
     loaded_scene: Scene,
+
+    /// Tracks the state of the picture cache tiles that were composited on the previous frame.
+    prev_composite_descriptor: CompositeDescriptor,
 }
 
 impl Document {
     pub fn new(
         id: DocumentId,
         size: DeviceIntSize,
         layer: DocumentLayer,
         default_device_pixel_ratio: f32,
@@ -413,16 +416,17 @@ impl Document {
             hit_tester_is_valid: false,
             rendered_frame_is_valid: false,
             has_built_scene: false,
             data_stores: DataStores::default(),
             scratch: PrimitiveScratchBuffer::new(),
             render_task_counters: RenderTaskGraphCounters::new(),
             #[cfg(feature = "replay")]
             loaded_scene: Scene::new(),
+            prev_composite_descriptor: CompositeDescriptor::empty(),
         }
     }
 
     fn can_render(&self) -> bool {
         self.scene.has_root_pipeline
     }
 
     fn has_pixels(&self) -> bool {
@@ -1535,21 +1539,31 @@ impl RenderBackend {
                 self.result_tx.send(msg).unwrap();
 
                 frame_build_time = Some(precise_time_ns() - frame_build_start_time);
 
                 let pending_update = self.resource_cache.pending_updates();
                 (pending_update, rendered_document)
             };
 
+            // Build a small struct that represents the state of the tiles to be composited.
+            let composite_descriptor = rendered_document
+                .frame
+                .composite_state
+                .create_descriptor();
+
             // If there are no texture cache updates to apply, and if the produced
-            // frame is a no-op, then we can skip compositing this frame completely.
-            if pending_update.is_nop() && rendered_document.frame.is_nop() {
+            // frame is a no-op, and the compositor state is equal, then we can skip
+            // compositing this frame completely.
+            if pending_update.is_nop() &&
+               rendered_document.frame.is_nop() &&
+               composite_descriptor == doc.prev_composite_descriptor {
                 doc.rendered_frame_is_valid = true;
             }
+            doc.prev_composite_descriptor = composite_descriptor;
 
             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,
@@ -1834,16 +1848,17 @@ impl RenderBackend {
                 frame_is_valid: false,
                 hit_tester_is_valid: false,
                 rendered_frame_is_valid: false,
                 has_built_scene: false,
                 data_stores,
                 scratch: PrimitiveScratchBuffer::new(),
                 render_task_counters: RenderTaskGraphCounters::new(),
                 loaded_scene: scene.clone(),
+                prev_composite_descriptor: CompositeDescriptor::empty(),
             };
 
             let frame_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
             let frame = CaptureConfig::deserialize::<Frame, _>(root, frame_name);
             let build_frame = match frame {
                 Some(frame) => {
                     info!("\tloaded a built frame with {} passes", frame.passes.len());