Bug 1579849 - [Wayland] Optimize cached rendering to wl_buffer for widget.wayland_cache_mode=1, r=jhorak a=RyanVM
authorMartin Stransky <stransky@redhat.com>
Wed, 11 Sep 2019 15:32:48 +0000
changeset 555141 a358333fb896bada0a8ba91d1b02fcfddb750b76
parent 555140 50da78761e577c6a45dc14a62d9316ee7bfd0118
child 555142 05e4691a98d392b383ba112493229bb3710a0bde
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak, RyanVM
bugs1579849
milestone70.0
Bug 1579849 - [Wayland] Optimize cached rendering to wl_buffer for widget.wayland_cache_mode=1, r=jhorak a=RyanVM - When widget.wayland_cache_mode=1 use direct rendering to wl_buffer only when there isn't any cached rendering pending and we're updating one big piece of screen (video playback/scrolling etc..) - Disable frame callback commits between WindowSurfaceWayland::Lock and WindowSurfaceWayland::Commit as the buffer can be updated by gecko compositor Differential Revision: https://phabricator.services.mozilla.com/D45184
widget/gtk/WindowSurfaceWayland.cpp
widget/gtk/WindowSurfaceWayland.h
--- a/widget/gtk/WindowSurfaceWayland.cpp
+++ b/widget/gtk/WindowSurfaceWayland.cpp
@@ -706,37 +706,73 @@ already_AddRefed<gfx::DrawTarget> Window
     }
   }
 
   return gfxPlatform::CreateDrawTargetForData(
       mImageSurface->Data(), mImageSurface->GetSize(), mImageSurface->Stride(),
       WindowBackBuffer::GetSurfaceFormat());
 }
 
-static bool IsWindowFullScreenUpdate(LayoutDeviceIntRect& screenRect,
-                                     const LayoutDeviceIntRegion& aRegion) {
-  if (aRegion.GetNumRects() > 1) return false;
+static bool IsWindowFullScreenUpdate(
+    LayoutDeviceIntRect& aScreenRect,
+    const LayoutDeviceIntRegion& aUpdatedRegion) {
+  if (aUpdatedRegion.GetNumRects() > 1) return false;
 
-  IntRect rect = aRegion.RectIter().Get().ToUnknownRect();
-  return (rect.x == 0 && rect.y == 0 && screenRect.width == rect.width &&
-          screenRect.height == rect.height);
+  IntRect rect = aUpdatedRegion.RectIter().Get().ToUnknownRect();
+  return (rect.x == 0 && rect.y == 0 && aScreenRect.width == rect.width &&
+          aScreenRect.height == rect.height);
 }
 
-static bool IsPopupFullScreenUpdate(LayoutDeviceIntRect& screenRect,
-                                    const LayoutDeviceIntRegion& aRegion) {
+static bool IsPopupFullScreenUpdate(
+    LayoutDeviceIntRect& aScreenRect,
+    const LayoutDeviceIntRegion& aUpdatedRegion) {
   // We know that popups can be drawn from two parts; a panel and an arrow.
   // Assume we redraw whole popups when we have two rects and bounding
   // box is equal to window borders.
-  if (aRegion.GetNumRects() > 2) return false;
+  if (aUpdatedRegion.GetNumRects() > 2) return false;
 
-  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::IntRect bounds = aUpdatedRegion.GetBounds().ToUnknownRect();
   gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
 
-  return (screenRect.width == lockSize.width &&
-          screenRect.height == lockSize.height);
+  return (aScreenRect.width == lockSize.width &&
+          aScreenRect.height == lockSize.height);
+}
+
+bool WindowSurfaceWayland::CanDrawToWaylandBufferDirectly(
+    const LayoutDeviceIntRect& aScreenRect,
+    const LayoutDeviceIntRegion& aUpdatedRegion) {
+  // whole buffer damage or no cache - we can go direct rendering safely.
+  if (mWholeWindowBufferDamage) {
+    return true;
+  }
+
+  // Let's try to eliminate a buffer copy
+  if (mRenderingCacheMode != CACHE_ALL) {
+    // There's some cached rendering, we can't throw it away.
+    if (mDelayedImageCommits.Length()) {
+      return false;
+    }
+
+    // More than one regions can overlap and produce flickering/artifacts.
+    if (aUpdatedRegion.GetNumRects() > 1) {
+      return false;
+    }
+
+    gfx::IntRect bounds = aUpdatedRegion.GetBounds().ToUnknownRect();
+    gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+    // There's some heuristics here. Let's enable direct rendering for large
+    // screen updates like video playback or page scrolling which is bigger
+    // than 1/3 of screen.
+    if (lockSize.width * 3 > aScreenRect.width &&
+        lockSize.height * 3 > aScreenRect.height) {
+      return true;
+    }
+  }
+  return false;
 }
 
 /*
   There are some situations which can happen here:
 
   A) Lock() is called to whole surface. In that case we don't need
      to clip/buffer the drawing and we can return wl_buffer directly
      for drawing.
@@ -746,16 +782,20 @@ static bool IsPopupFullScreenUpdate(Layo
 
   B) Lock() is requested for part(s) of screen. We need to provide temporary
      surface to draw into and copy result (clipped) to target wl_surface.
  */
 already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
     const LayoutDeviceIntRegion& aRegion) {
   MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
 
+  // Disable all commits from frame callback handler and delayed comit handler
+  // as we're updated by gecko compositor.
+  mPendingCommit = false;
+
   LayoutDeviceIntRect lockedScreenRect = mWindow->GetBounds();
   gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
   gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
 
   // Are we asked for entire nsWindow to draw?
   bool isTransparentPopup =
       mWindow->IsWaylandPopup() &&
       (eTransparencyTransparent == mWindow->GetTransparencyMode());
@@ -807,18 +847,17 @@ already_AddRefed<gfx::DrawTarget> Window
       // partal screen update instead of whole screen. Discard this painting
       // as it produces artifacts.
       return nullptr;
     }
     mBufferScreenRect = lockedScreenRect;
   }
 
   mDrawToWaylandBufferDirectly =
-      (mWholeWindowBufferDamage || mRenderingCacheMode != CACHE_ALL);
-
+      CanDrawToWaylandBufferDirectly(mBufferScreenRect, aRegion);
   if (mDrawToWaylandBufferDirectly) {
     // If there's any pending image commit scratch them as we're going
     // to redraw the whole sceen anyway.
     mDelayedImageCommits.Clear();
 
     RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(
         /* aCanSwitchBuffer */ mWholeWindowBufferDamage);
     if (dt) {
@@ -841,32 +880,20 @@ already_AddRefed<gfx::DrawTarget> Window
 
   LOGWAYLAND(("   Indirect drawing.\n"));
   return LockImageSurface(lockSize);
 }
 
 void WindowImageSurface::Draw(gfx::SourceSurface* aSurface,
                               gfx::DrawTarget* aDest,
                               const LayoutDeviceIntRegion& aRegion) {
-  uint32_t numRects = aRegion.GetNumRects();
-  if (numRects != 1) {
-    AutoTArray<IntRect, 32> rects;
-    rects.SetCapacity(numRects);
-    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
-      rects.AppendElement(iter.Get().ToUnknownRect());
-    }
-    aDest->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
-  }
-
-  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
-  gfx::Rect rect(bounds);
-  aDest->DrawSurface(aSurface, rect, rect);
-
-  if (numRects != 1) {
-    aDest->PopClip();
+  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+    mozilla::LayoutDeviceIntRect r = iter.Get();
+    gfx::Rect rect(r.ToUnknownRect());
+    aDest->DrawSurface(aSurface, rect, rect);
   }
 }
 
 void WindowImageSurface::Draw(gfx::DrawTarget* aDest,
                               LayoutDeviceIntRegion& aWaylandBufferDamage) {
   Draw(mSurface.get(), aDest, mUpdateRegion);
   aWaylandBufferDamage.OrWith(mUpdateRegion);
 }
@@ -882,49 +909,44 @@ WindowImageSurface::WindowImageSurface(
 void WindowSurfaceWayland::DrawDelayedImageCommits(
     gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aWaylandBufferDamage) {
   for (unsigned int i = 0; i < mDelayedImageCommits.Length(); i++) {
     mDelayedImageCommits[i].Draw(aDrawTarget, aWaylandBufferDamage);
   }
   mDelayedImageCommits.Clear();
 }
 
-bool WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(
-    const LayoutDeviceIntRegion& aRegion,
-    LayoutDeviceIntRegion& aWaylandBufferDamage) {
-  MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
-
-#ifdef DEBUG
-  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
-  gfx::Rect rect(bounds);
-  MOZ_ASSERT(!rect.IsEmpty(), "Empty drawing?");
-#endif
-
-  LOGWAYLAND(
-      ("WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer [%p] "
-       "screenSize [%d x %d]\n",
-       (void*)this, mBufferScreenRect.width, mBufferScreenRect.height));
+void WindowSurfaceWayland::CacheImageSurface(
+    const LayoutDeviceIntRegion& aRegion) {
+  LOGWAYLAND(("WindowSurfaceWayland::CacheImageSurface [%p]", (void*)this));
 
   mDelayedImageCommits.AppendElement(
       WindowImageSurface(mImageSurface, aRegion));
   // mImageSurface is owned by mDelayedImageCommits
   mImageSurface = nullptr;
+}
 
-  RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(
-      /* aCanSwitchBuffer */ mWholeWindowBufferDamage);
-  if (!dt) {
-    return false;
-  }
+bool WindowSurfaceWayland::CommitImageCacheToWaylandBuffer() {
+  MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
 
-  LOGWAYLAND(("   Flushing %ld cached WindowImageSurfaces to Wayland buffer\n",
-              long(mDelayedImageCommits.Length() + 1)));
+  if (mDelayedImageCommits.Length()) {
+    RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(
+        /* aCanSwitchBuffer */ mWholeWindowBufferDamage);
+    if (!dt) {
+      return false;
+    }
 
-  // Draw any delayed image commits first
-  DrawDelayedImageCommits(dt, aWaylandBufferDamage);
-  UnlockWaylandBuffer();
+    LOGWAYLAND(
+        ("   Flushing %ld cached WindowImageSurfaces to Wayland buffer\n",
+         long(mDelayedImageCommits.Length() + 1)));
+
+    // Draw any delayed image commits first
+    DrawDelayedImageCommits(dt, mWaylandBufferDamage);
+    UnlockWaylandBuffer();
+  }
 
   return true;
 }
 
 static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland** aSurface) {
   if (*aSurface) {
     (*aSurface)->DelayedCommitHandler();
   } else {
@@ -1047,20 +1069,18 @@ void WindowSurfaceWayland::Commit(const 
     if (!mWholeWindowBufferDamage) {
       mWaylandBufferDamage.OrWith(aInvalidRegion);
     }
     UnlockWaylandBuffer();
     mPendingCommit = true;
   } else {
     MOZ_ASSERT(!mWaylandBuffer->IsLocked(),
                "Drawing to already locked buffer?");
-    if (CommitImageSurfaceToWaylandBuffer(aInvalidRegion,
-                                          mWaylandBufferDamage)) {
-      // Our cached drawing is flushed, we can draw fullscreen again.
-      mDrawToWaylandBufferDirectly = true;
+    CacheImageSurface(aInvalidRegion);
+    if (CommitImageCacheToWaylandBuffer()) {
       mPendingCommit = true;
     }
   }
 
   if (mPendingCommit) {
     CommitWaylandBuffer();
   }
 }
--- a/widget/gtk/WindowSurfaceWayland.h
+++ b/widget/gtk/WindowSurfaceWayland.h
@@ -193,21 +193,25 @@ class WindowSurfaceWayland : public Wind
 
  private:
   WindowBackBuffer* CreateWaylandBuffer(int aWidth, int aHeight);
   WindowBackBuffer* GetWaylandBufferToDraw(bool aCanSwitchBuffer);
 
   already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(bool aCanSwitchBuffer);
   void UnlockWaylandBuffer();
 
+  bool CanDrawToWaylandBufferDirectly(
+      const LayoutDeviceIntRect& aScreenRect,
+      const LayoutDeviceIntRegion& aUpdatedRegion);
+
   already_AddRefed<gfx::DrawTarget> LockImageSurface(
       const gfx::IntSize& aLockSize);
-  bool CommitImageSurfaceToWaylandBuffer(
-      const LayoutDeviceIntRegion& aRegion,
-      LayoutDeviceIntRegion& aWaylandBufferDamage);
+
+  void CacheImageSurface(const LayoutDeviceIntRegion& aRegion);
+  bool CommitImageCacheToWaylandBuffer();
   void CommitWaylandBuffer();
 
   void DrawDelayedImageCommits(gfx::DrawTarget* aDrawTarget,
                                LayoutDeviceIntRegion& aWaylandBufferDamage);
 
   // TODO: Do we need to hold a reference to nsWindow object?
   nsWindow* mWindow;
   // Buffer screen rects helps us understand if we operate on