Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Wed, 08 May 2019 06:37:15 +0300
changeset 534868 40bd76567ad73911f3dd035b3464ea5fdd7642a2
parent 534864 0e9e744accd3ed7b7b0ff2436904ed78a92bf9e2 (current diff)
parent 534867 2bcdd7ab7d95f0b24a920803df72b7e4525a96e9 (diff)
child 534884 c464e9d2f4b810cb4da5651c5c5e14c5c9ab1080
child 534933 a44f70589c96a0fbcfe065f868393fdec18e0b59
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.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
Merge inbound to mozilla-central. a=merge
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1879,16 +1879,23 @@ static bool CCRunnerFired(TimeStamp aDea
       didDoWork = true;
     }
   } else if (((sPreviousSuspectedCount + 100) <= suspected) ||
              (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
     // Only do a forget skippable if there are more than a few new objects
     // or we're doing the initial forget skippables.
     FireForgetSkippable(suspected, false, aDeadline);
     didDoWork = true;
+  } else if (!isLateTimerFire && !aDeadline.IsNull()) {
+    MOZ_ASSERT(!didDoWork);
+    // If we're called during idle time, try to find some work to do by calling
+    // the method recursively, effectively bypassing some possible forget
+    // skippable calls.
+    sCCRunnerFireCount = numEarlyTimerFires;
+    return CCRunnerFired(aDeadline);
   }
 
   if (isLateTimerFire) {
     ccDelay = NS_CC_DELAY;
 
     // We have either just run the CC or decided we don't want to run the CC
     // next time, so kill the timer.
     sPreviousSuspectedCount = 0;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2544,34 +2544,50 @@ static FeatureState& WebRenderHardwareQu
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   int32_t status;
   if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRENDER,
                                              aOutFailureId, &status))) {
     if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
       featureWebRenderQualified.Disable(FeatureStatus::Blocked,
                                         "No qualified hardware", aOutFailureId);
-    } else if (aHasBattery) {
-      featureWebRenderQualified.Disable(
-          FeatureStatus::Blocked, "Has battery",
-          NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_HAS_BATTERY"));
     } else {
       nsAutoString adapterVendorID;
       gfxInfo->GetAdapterVendorID(adapterVendorID);
 
       nsAutoString adapterDeviceID;
       gfxInfo->GetAdapterDeviceID(adapterDeviceID);
       nsresult valid;
       int32_t deviceID = adapterDeviceID.ToInteger(&valid, 16);
       if (valid != NS_OK) {
         featureWebRenderQualified.Disable(
             FeatureStatus::Blocked, "Bad device id",
             NS_LITERAL_CSTRING("FEATURE_FAILURE_BAD_DEVICE_ID"));
       } else {
-        if (adapterVendorID == u"0x10de") {
+#ifdef NIGHTLY_BUILD
+        // For Intel devices, if we have a battery, ignore it if the screen is
+        // small enough. Note that we always check for a battery with NVIDIA
+        // because we do not have a limited/curated set of devices to support
+        // WebRender on.
+        const int32_t kMaxPixelsBattery = 1920 * 1200;  // WUXGA
+        const int32_t screenPixels = aScreenSize.width * aScreenSize.height;
+        bool disableForBattery = aHasBattery;
+        if (adapterVendorID == u"0x8086" && screenPixels > 0 &&
+            screenPixels <= kMaxPixelsBattery) {
+          disableForBattery = false;
+        }
+#else
+        bool disableForBattery = aHasBattery;
+#endif
+
+        if (disableForBattery) {
+          featureWebRenderQualified.Disable(
+              FeatureStatus::Blocked, "Has battery",
+              NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_HAS_BATTERY"));
+        } else if (adapterVendorID == u"0x10de") {
           if (deviceID < 0x6c0) {
             // 0x6c0 is the lowest Fermi device id. Unfortunately some Tesla
             // devices that don't support D3D 10.1 have higher deviceIDs. They
             // will be included, but blocked by ANGLE.
             featureWebRenderQualified.Disable(
                 FeatureStatus::Blocked, "Device too old",
                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD"));
           }
@@ -2669,23 +2685,22 @@ static FeatureState& WebRenderHardwareQu
             featureWebRenderQualified.Disable(
                 FeatureStatus::Blocked, "Device too old",
                 NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_TOO_OLD"));
           }
 #  ifdef MOZ_WIDGET_GTK
           else {
             // Performance is not great on 4k screens with WebRender + Linux.
             // Disable it for now if it is too large.
-            const int32_t maxPixels = 3440 * 1440;  // UWQHD
-            int32_t pixels = aScreenSize.width * aScreenSize.height;
-            if (pixels > maxPixels) {
+            const int32_t kMaxPixelsLinux = 3440 * 1440;  // UWQHD
+            if (screenPixels > kMaxPixelsLinux) {
               featureWebRenderQualified.Disable(
                   FeatureStatus::Blocked, "Screen size too large",
                   NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_TOO_LARGE"));
-            } else if (pixels <= 0) {
+            } else if (screenPixels <= 0) {
               featureWebRenderQualified.Disable(
                   FeatureStatus::Blocked, "Screen size unknown",
                   NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_UNKNOWN"));
             }
           }
 #  endif  // MOZ_WIDGET_GTK
 #endif    // NIGHTLY_BUILD
         } else {
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -633,16 +633,18 @@ pub enum RenderTaskKind {
     #[allow(dead_code)]
     Glyph(GlyphTask),
     Readback(DeviceIntRect),
     Scaling(ScalingTask),
     Blit(BlitTask),
     Border(BorderTask),
     LineDecoration(LineDecorationTask),
     Gradient(GradientTask),
+    #[cfg(test)]
+    Test(RenderTargetKind),
 }
 
 impl RenderTaskKind {
     pub fn as_str(&self) -> &'static str {
         match *self {
             RenderTaskKind::Picture(..) => "Picture",
             RenderTaskKind::CacheMask(..) => "CacheMask",
             RenderTaskKind::ClipRegion(..) => "ClipRegion",
@@ -650,16 +652,18 @@ impl RenderTaskKind {
             RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
             RenderTaskKind::Glyph(..) => "Glyph",
             RenderTaskKind::Readback(..) => "Readback",
             RenderTaskKind::Scaling(..) => "Scaling",
             RenderTaskKind::Blit(..) => "Blit",
             RenderTaskKind::Border(..) => "Border",
             RenderTaskKind::LineDecoration(..) => "LineDecoration",
             RenderTaskKind::Gradient(..) => "Gradient",
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => "Test",
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum ClearMode {
@@ -698,16 +702,31 @@ impl RenderTask {
             location: RenderTaskLocation::Dynamic(None, size),
             children,
             kind,
             clear_mode,
             saved_index: None,
         }
     }
 
+    #[cfg(test)]
+    pub fn new_test(
+        target: RenderTargetKind,
+        location: RenderTaskLocation,
+        children: Vec<RenderTaskId>,
+    ) -> Self {
+        RenderTask {
+            location,
+            children,
+            kind: RenderTaskKind::Test(target),
+            clear_mode: ClearMode::Transparent,
+            saved_index: None,
+        }
+    }
+
     pub fn new_picture(
         location: RenderTaskLocation,
         unclipped_size: DeviceSize,
         pic_index: PictureIndex,
         content_origin: DeviceIntPoint,
         children: Vec<RenderTaskId>,
         uv_rect_kind: UvRectKind,
         root_spatial_node_index: SpatialNodeIndex,
@@ -1141,16 +1160,21 @@ impl RenderTask {
             RenderTaskKind::ClipRegion(..) |
             RenderTaskKind::Glyph(_) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Blit(..) => {
                 UvRectKind::Rect
             }
+
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {
+                unreachable!("Unexpected render task");
+            }
         }
     }
 
     // Write (up to) 8 floats of data specific to the type
     // of render task that is provided to the GPU shaders
     // via a vertex texture.
     pub fn write_task_data(&self) -> RenderTaskData {
         // NOTE: The ordering and layout of these structures are
@@ -1198,16 +1222,21 @@ impl RenderTask {
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::Blit(..) => {
                 [0.0; 3]
             }
+
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {
+                unreachable!();
+            }
         };
 
         let (mut target_rect, target_index) = self.get_target_rect();
         // The primitives inside a fixed-location render task
         // are already placed to their corresponding positions,
         // so the shader doesn't need to shift by the origin.
         if let RenderTaskLocation::Fixed(_) = self.location {
             target_rect.origin = DeviceIntPoint::origin();
@@ -1242,16 +1271,20 @@ impl RenderTask {
             RenderTaskKind::Blit(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Glyph(..) => {
                 panic!("texture handle not supported for this task kind");
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {
+                panic!("RenderTask tests aren't expected to exercise this code");
+            }
         }
     }
 
     pub fn get_dynamic_size(&self) -> DeviceIntSize {
         match self.location {
             RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
             RenderTaskLocation::Dynamic(_, size) => size,
             RenderTaskLocation::TextureCache { rect, .. } => rect.size,
@@ -1317,16 +1350,19 @@ impl RenderTask {
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::Picture(..) => {
                 RenderTargetKind::Color
             }
 
             RenderTaskKind::Blit(..) => {
                 RenderTargetKind::Color
             }
+
+            #[cfg(test)]
+            RenderTaskKind::Test(kind) => kind,
         }
     }
 
     pub fn write_gpu_blocks(
         &mut self,
         gpu_cache: &mut GpuCache,
     ) {
         let (target_rect, target_index) = self.get_target_rect();
@@ -1345,16 +1381,20 @@ impl RenderTask {
             RenderTaskKind::ClipRegion(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Glyph(..) => {
                 return;
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {
+                panic!("RenderTask tests aren't expected to exercise this code");
+            }
         };
 
         if let Some(mut request) = gpu_cache.request(cache_handle) {
             let p0 = target_rect.origin.to_f32();
             let p1 = target_rect.bottom_right().to_f32();
 
             let image_source = ImageSource {
                 p0,
@@ -1407,16 +1447,20 @@ impl RenderTask {
                 pt.add_item(format!("source: {:?}", task.source));
             }
             RenderTaskKind::Glyph(..) => {
                 pt.new_level("Glyph".to_owned());
             }
             RenderTaskKind::Gradient(..) => {
                 pt.new_level("Gradient".to_owned());
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {
+                pt.new_level("Test".to_owned());
+            }
         }
 
         pt.add_item(format!("clear to: {:?}", self.clear_mode));
 
         for &child_id in &self.children {
             if tree[child_id].print_with(pt, tree) {
                 pt.add_item(format!("self: {:?}", child_id))
             }
@@ -1869,8 +1913,180 @@ fn dump_task_dependency_link(
             path().move_to(x1, y1)
                 .cubic_bezier_to(ctrl1_x, y1, ctrl1_x, ctrl_y, mid_x, ctrl_y)
                 .cubic_bezier_to(ctrl2_x, ctrl_y, ctrl2_x, y2, x2, y2)
                 .fill(Fill::None)
                 .stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
         ).unwrap();
     }
 }
+
+#[cfg(test)]
+use euclid::{size2, rect};
+
+#[cfg(test)]
+fn dyn_location(w: i32, h: i32) -> RenderTaskLocation {
+    RenderTaskLocation::Dynamic(None, size2(w, h))
+}
+
+
+#[test]
+fn diamond_task_graph() {
+    // A simple diamon shaped task graph.
+    //
+    //     [b1]
+    //    /    \
+    // [a]      [main_pic]
+    //    \    /
+    //     [b2]
+
+    let color = RenderTargetKind::Color;
+
+    let counters = RenderTaskTreeCounters::new();
+    let mut tasks = RenderTaskTree::new(FrameId::first(), &counters);
+
+    let a = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new()));
+    let b1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a]));
+    let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a]));
+
+    let main_pic = tasks.add(RenderTask::new_test(
+        color,
+        RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)),
+        vec![b1, b2],
+    ));
+
+    let initial_number_of_tasks = tasks.tasks.len();
+
+    let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true);
+
+    // We should not have added any blits.
+    assert_eq!(tasks.tasks.len(), initial_number_of_tasks);
+
+    assert_eq!(passes.len(), 3);
+    assert_eq!(passes[0].tasks, vec![a]);
+
+    assert_eq!(passes[1].tasks.len(), 2);
+    assert!(passes[1].tasks.contains(&b1));
+    assert!(passes[1].tasks.contains(&b2));
+
+    assert_eq!(passes[2].tasks, vec![main_pic]);
+}
+
+#[test]
+fn blur_task_graph() {
+    // This test simulates a complicated shadow stack effect with target allocation
+    // conflicts to resolve.
+
+    let color = RenderTargetKind::Color;
+
+    let counters = RenderTaskTreeCounters::new();
+    let mut tasks = RenderTaskTree::new(FrameId::first(), &counters);
+
+    let pic = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new()));
+    let scale1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![pic]));
+    let scale2 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![scale1]));
+    let scale3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![scale2]));
+    let scale4 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale3]));
+
+    let vblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale4]));
+    let hblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![vblur1]));
+
+    let vblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale4]));
+    let hblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![vblur2]));
+
+    // Insert a task that is an even number of passes away from its dependency.
+    // This means the source and destination are on the same target and we have to resolve
+    // this conflict by automatically inserting a blit task.
+    let vblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![scale3]));
+    let hblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![vblur3]));
+
+    // Insert a task that is an odd number > 1 of passes away from its dependency.
+    // This should force us to mark the dependency "for saving" to keep its content valid
+    // until the task can access it. 
+    let vblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![scale2]));
+    let hblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![vblur4]));
+
+    let main_pic = tasks.add(RenderTask::new_test(
+        color,
+        RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)),
+        vec![hblur1, hblur2, hblur3, hblur4],
+    ));
+
+    let initial_number_of_tasks = tasks.tasks.len();
+
+    let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true);
+
+    // We should have added a single blit task.
+    assert_eq!(tasks.tasks.len(), initial_number_of_tasks + 1);
+
+    // vblur3's dependency to scale3 should be replaced by a blit.
+    let blit = tasks[vblur3].children[0];
+    assert!(blit != scale3);
+
+    match tasks[blit].kind {
+        RenderTaskKind::Blit(..) => {}
+        _ => { panic!("This should be a blit task."); }
+    }
+
+    assert_eq!(passes.len(), 8);
+
+    assert_eq!(passes[0].tasks, vec![pic]);
+    assert_eq!(passes[1].tasks, vec![scale1]);
+    assert_eq!(passes[2].tasks, vec![scale2]);
+    assert_eq!(passes[3].tasks, vec![scale3]);
+
+    assert_eq!(passes[4].tasks.len(), 2);
+    assert!(passes[4].tasks.contains(&scale4));
+    assert!(passes[4].tasks.contains(&blit));
+
+    assert_eq!(passes[5].tasks.len(), 4);
+    assert!(passes[5].tasks.contains(&vblur1));
+    assert!(passes[5].tasks.contains(&vblur2));
+    assert!(passes[5].tasks.contains(&vblur3));
+    assert!(passes[5].tasks.contains(&vblur4));
+
+    assert_eq!(passes[6].tasks.len(), 4);
+    assert!(passes[6].tasks.contains(&hblur1));
+    assert!(passes[6].tasks.contains(&hblur2));
+    assert!(passes[6].tasks.contains(&hblur3));
+    assert!(passes[6].tasks.contains(&hblur4));
+
+    assert_eq!(passes[7].tasks, vec![main_pic]);
+
+    // See vblur4's comment above. 
+    assert!(tasks[scale2].saved_index.is_some());
+}
+
+#[test]
+fn culled_tasks() {
+    // This test checks that tasks that do not contribute to the frame don't appear in the
+    // generated passes.
+
+    let color = RenderTargetKind::Color;
+
+    let counters = RenderTaskTreeCounters::new();
+    let mut tasks = RenderTaskTree::new(FrameId::first(), &counters);
+
+    let a1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new()));
+    let _a2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a1]));
+
+    let b1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new()));
+    let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![b1]));
+    let _b3 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![b2]));
+
+    let main_pic = tasks.add(RenderTask::new_test(
+        color,
+        RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)),
+        vec![b2],
+    ));
+
+    let initial_number_of_tasks = tasks.tasks.len();
+
+    let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true);
+
+    // We should not have added any blits.
+    assert_eq!(tasks.tasks.len(), initial_number_of_tasks);
+
+    assert_eq!(passes.len(), 3);
+    assert_eq!(passes[0].tasks, vec![b1]);
+    assert_eq!(passes[1].tasks, vec![b2]);
+    assert_eq!(passes[2].tasks, vec![main_pic]);
+}
--- a/gfx/wr/webrender/src/tiling.rs
+++ b/gfx/wr/webrender/src/tiling.rs
@@ -568,16 +568,18 @@ impl RenderTarget for ColorRenderTarget 
                         let (target_rect, _) = task.get_target_rect();
                         self.blits.push(BlitJob {
                             source: BlitJobSource::RenderTask(task_id),
                             target_rect: target_rect.inner_rect(task_info.padding)
                         });
                     }
                 }
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {}
         }
     }
 
     fn must_be_drawn(&self) -> bool {
         self.alpha_batch_containers.iter().any(|ab| {
             !ab.tile_blits.is_empty()
         })
     }
@@ -719,16 +721,18 @@ impl RenderTarget for AlphaRenderTarget 
             }
             RenderTaskKind::Scaling(ref info) => {
                 info.add_instances(
                     &mut self.scalings,
                     render_tasks.get_task_address(task_id),
                     render_tasks.get_task_address(task.children[0]),
                 );
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {}
         }
     }
 
     fn needs_depth(&self) -> bool {
         false
     }
 
     fn must_be_drawn(&self) -> bool {
@@ -867,16 +871,18 @@ impl TextureCacheRenderTarget {
             RenderTaskKind::VerticalBlur(..) |
             RenderTaskKind::Picture(..) |
             RenderTaskKind::ClipRegion(..) |
             RenderTaskKind::CacheMask(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) => {
                 panic!("BUG: unexpected task kind for texture cache target");
             }
+            #[cfg(test)]
+            RenderTaskKind::Test(..) => {}
         }
     }
 
     #[cfg(feature = "pathfinder")]
     fn add_glyph_task(&mut self, task_info: &mut GlyphTask, target_rect: DeviceIntRect) {
         self.glyphs.push(GlyphJob {
             mesh: task_info.mesh.take().unwrap(),
             target_rect: target_rect,