Bug 1718570 - Implement full partial damage support, r=gfx-reviewers,aosmond
☠☠ backed out by 425f0a769c49 ☠ ☠
authorRobert Mader <robert.mader@posteo.de>
Tue, 13 Jul 2021 00:55:30 +0000
changeset 585362 59cdbaaa6a7ba6fbd4ff90f00d4b9c8eeb2a1111
parent 585361 489ce763c140fe2dae8050c0cde0bc3c381bb95f
child 585363 64ece870cdbf0a7d9e64990b424e79b4e37a4465
push id146176
push userrobert.mader@posteo.de
push dateTue, 13 Jul 2021 00:58:04 +0000
treeherderautoland@59cdbaaa6a7b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfx-reviewers, aosmond
bugs1718570
milestone92.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 1718570 - Implement full partial damage support, r=gfx-reviewers,aosmond This becomes possible as a side effect of making sure that the current buffer content is always completely valid, by blitting from the previous buffer, analogous to `NativeLayerCA::HandlePartialUpdate` and `WindowSurfaceWaylandMB::HandlePartialUpdate`. Apart from reducing overpaint, this improves correctness is many situations, avoiding glitches. Note: the approach here may not be optimal concerning efficiency yet and improvements are planed after further refactoring. Depends on D118925 Differential Revision: https://phabricator.services.mozilla.com/D119015
gfx/layers/NativeLayerWayland.cpp
gfx/layers/SurfacePoolWayland.cpp
gfx/layers/SurfacePoolWayland.h
modules/libpref/init/StaticPrefList.yaml
--- a/gfx/layers/NativeLayerWayland.cpp
+++ b/gfx/layers/NativeLayerWayland.cpp
@@ -343,17 +343,17 @@ void NativeLayerRootWayland::RequestFram
   }
 }
 
 bool NativeLayerRootWayland::CommitToScreen() {
   MutexAutoLock lock(mMutex);
 
   wl_surface* wlSurface = moz_container_wayland_surface_lock(mContainer);
   for (RefPtr<NativeLayerWayland>& layer : mSublayers) {
-    layer->mNativeSurface->Commit(layer->mDirtyRegion);
+    layer->mNativeSurface->Commit(layer->mDirtyRegion, layer->mValidRect);
     layer->mDirtyRegion.SetEmpty();
   }
 
   if (wlSurface) {
     wl_surface_commit(wlSurface);
     moz_container_wayland_surface_unlock(mContainer, &wlSurface);
   }
 
--- a/gfx/layers/SurfacePoolWayland.cpp
+++ b/gfx/layers/SurfacePoolWayland.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nullptr; c-basic-offset: 2
  * -*- 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/. */
 
 #include "mozilla/layers/SurfacePoolWayland.h"
 
+#include "GLBlitHelper.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+
 namespace mozilla::layers {
 
 using gfx::IntRect;
 using gl::DepthAndStencilBuffer;
 using gl::MozFramebuffer;
 
 #define BACK_BUFFER_NUM 3
 
@@ -203,40 +206,71 @@ NativeSurfaceWaylandSHM::NativeSurfaceWa
 RefPtr<DrawTarget> NativeSurfaceWaylandSHM::GetNextDrawTarget() {
   MutexAutoLock lock(mMutex);
   if (!mCurrentBuffer) {
     mCurrentBuffer = ObtainBufferFromPool(lock);
   }
   return mCurrentBuffer->Lock();
 }
 
-void NativeSurfaceWaylandSHM::Commit(const IntRegion& aInvalidRegion) {
+void NativeSurfaceWaylandSHM::Commit(const IntRegion& aInvalidRegion,
+                                     const IntRect& aValidRect) {
   MutexAutoLock lock(mMutex);
 
   if (aInvalidRegion.IsEmpty()) {
     if (mCurrentBuffer) {
       ReturnBufferToPool(lock, mCurrentBuffer);
       mCurrentBuffer = nullptr;
     }
     wl_surface_commit(mWlSurface);
     return;
   }
 
+  HandlePartialUpdate(lock, aInvalidRegion, aValidRect);
+
   for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
     IntRect r = iter.Get();
     wl_surface_damage_buffer(mWlSurface, r.x, r.y, r.width, r.height);
   }
 
   MOZ_ASSERT(mCurrentBuffer);
   mCurrentBuffer->AttachAndCommit(mWlSurface);
   mCurrentBuffer = nullptr;
 
   EnforcePoolSizeLimit(lock);
 }
 
+void NativeSurfaceWaylandSHM::HandlePartialUpdate(
+    const MutexAutoLock& aProofOfLock, const IntRegion& aInvalidRegion,
+    const IntRect& aValidRect) {
+  if (!mPreviousBuffer || mPreviousBuffer == mCurrentBuffer) {
+    mPreviousBuffer = mCurrentBuffer;
+    return;
+  }
+
+  IntRegion copyRegion = IntRegion(aValidRect);
+  copyRegion.SubOut(aInvalidRegion);
+
+  if (!copyRegion.IsEmpty()) {
+    RefPtr<gfx::DataSourceSurface> dataSourceSurface =
+        gfx::CreateDataSourceSurfaceFromData(
+            mSize, mPreviousBuffer->GetSurfaceFormat(),
+            (const uint8_t*)mPreviousBuffer->GetShmPool()->GetImageData(),
+            mSize.width * BytesPerPixel(mPreviousBuffer->GetSurfaceFormat()));
+    RefPtr<DrawTarget> dt = mCurrentBuffer->Lock();
+
+    for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
+      IntRect r = iter.Get();
+      dt->CopySurface(dataSourceSurface, r, IntPoint(r.x, r.y));
+    }
+  }
+
+  mPreviousBuffer = mCurrentBuffer;
+}
+
 RefPtr<WaylandShmBuffer> NativeSurfaceWaylandSHM::ObtainBufferFromPool(
     const MutexAutoLock& aProofOfLock) {
   if (!mAvailableBuffers.IsEmpty()) {
     RefPtr<WaylandShmBuffer> buffer = mAvailableBuffers.PopLastElement();
     mInUseBuffers.AppendElement(buffer);
     return buffer;
   }
 
@@ -326,46 +360,78 @@ Maybe<GLuint> NativeSurfaceWaylandDMABUF
 
   if (!mCurrentBuffer) {
     mCurrentBuffer = ObtainBufferFromPool(lock);
   }
 
   return Some(mCurrentBuffer->GetFramebuffer()->mFB);
 }
 
-void NativeSurfaceWaylandDMABUF::Commit(const IntRegion& aInvalidRegion) {
+void NativeSurfaceWaylandDMABUF::Commit(const IntRegion& aInvalidRegion,
+                                        const IntRect& aValidRect) {
   MutexAutoLock lock(mMutex);
 
   if (aInvalidRegion.IsEmpty()) {
     if (mCurrentBuffer) {
       ReturnBufferToPool(lock, mCurrentBuffer);
       mCurrentBuffer = nullptr;
     }
     wl_surface_commit(mWlSurface);
     return;
   }
 
+  HandlePartialUpdate(lock, aInvalidRegion, aValidRect);
+
+  // We rely on implicit synchronization in the system compositor to make sure
+  // all GL operation have been finished befor presenting a new frame.
+  mGL->fFlush();
+
   for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
     IntRect r = iter.Get();
     wl_surface_damage_buffer(mWlSurface, r.x, r.y, r.width, r.height);
   }
 
   MOZ_ASSERT(mCurrentBuffer);
   wl_surface_attach(mWlSurface, mCurrentBuffer->GetWlBuffer(), 0, 0);
   wl_surface_commit(mWlSurface);
   mCurrentBuffer = nullptr;
 
   EnforcePoolSizeLimit(lock);
 }
 
+void NativeSurfaceWaylandDMABUF::HandlePartialUpdate(
+    const MutexAutoLock& aProofOfLock, const IntRegion& aInvalidRegion,
+    const IntRect& aValidRect) {
+  if (!mPreviousBuffer || mPreviousBuffer == mCurrentBuffer) {
+    mPreviousBuffer = mCurrentBuffer;
+    return;
+  }
+
+  IntRegion copyRegion = IntRegion(aValidRect);
+  copyRegion.SubOut(aInvalidRegion);
+
+  if (!copyRegion.IsEmpty()) {
+    mGL->MakeCurrent();
+    for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
+      gfx::IntRect r = iter.Get();
+      mGL->BlitHelper()->BlitFramebufferToFramebuffer(
+          mPreviousBuffer->GetFramebuffer()->mFB,
+          mCurrentBuffer->GetFramebuffer()->mFB, r, r, LOCAL_GL_NEAREST);
+    }
+  }
+
+  mPreviousBuffer = mCurrentBuffer;
+}
+
 void NativeSurfaceWaylandDMABUF::DestroyGLResources() {
   mInUseBuffers.Clear();
   mAvailableBuffers.Clear();
   mDepthBuffers.Clear();
   mCurrentBuffer = nullptr;
+  mPreviousBuffer = nullptr;
   mGL = nullptr;
 }
 
 static const struct wl_buffer_listener
     sBufferListenerNativeSurfaceWaylandDMABUF = {
         NativeSurfaceWaylandDMABUF::BufferReleaseCallbackHandler};
 
 RefPtr<WaylandDMABUFBuffer> NativeSurfaceWaylandDMABUF::ObtainBufferFromPool(
--- a/gfx/layers/SurfacePoolWayland.h
+++ b/gfx/layers/SurfacePoolWayland.h
@@ -14,16 +14,17 @@
 #include "mozilla/widget/DMABufSurface.h"
 #include "mozilla/widget/nsWaylandDisplay.h"
 #include "mozilla/widget/WaylandShmBuffer.h"
 
 namespace mozilla::layers {
 
 using gfx::DrawTarget;
 using gfx::IntPoint;
+using gfx::IntRect;
 using gfx::IntRegion;
 using gfx::IntSize;
 using gfx::Rect;
 using gl::GLContext;
 using widget::nsWaylandDisplay;
 using widget::WaylandShmBuffer;
 
 typedef void (*CallbackFunc)(void* aData, uint32_t aTime);
@@ -53,17 +54,18 @@ class NativeSurfaceWayland {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NativeSurfaceWayland);
 
   static RefPtr<NativeSurfaceWayland> Create(const IntSize& aSize,
                                              GLContext* aGL);
 
   virtual Maybe<GLuint> GetNextFramebuffer() { return Nothing(); };
   virtual RefPtr<DrawTarget> GetNextDrawTarget() { return nullptr; };
 
-  virtual void Commit(const IntRegion& aInvalidRegion) = 0;
+  virtual void Commit(const IntRegion& aInvalidRegion,
+                      const IntRect& aValidRect) = 0;
   virtual void NotifySurfaceReady(){};
   virtual void DestroyGLResources(){};
 
   void CreateSubsurface(wl_surface* aParentSurface);
   void ClearSubsurface();
   bool HasSubsurface() { return !!mWlSubsurface; }
 
   virtual void SetBufferTransformFlipped(bool aFlipped);
@@ -95,36 +97,41 @@ class NativeSurfaceWayland {
   Rect mViewportSourceRect = Rect(-1, -1, -1, -1);
   IntSize mViewportDestinationSize = IntSize(-1, -1);
   nsTArray<RefPtr<CallbackMultiplexHelper>> mCallbackMultiplexHelpers;
 };
 
 class NativeSurfaceWaylandSHM final : public NativeSurfaceWayland {
  public:
   RefPtr<DrawTarget> GetNextDrawTarget() override;
-  void Commit(const IntRegion& aInvalidRegion) override;
+  void Commit(const IntRegion& aInvalidRegion,
+              const IntRect& aValidRect) override;
 
   static void BufferReleaseCallbackHandler(void* aData, wl_buffer* aBuffer);
 
  private:
   friend RefPtr<NativeSurfaceWayland> NativeSurfaceWayland::Create(
       const IntSize& aSize, GLContext* aGL);
 
   explicit NativeSurfaceWaylandSHM(const IntSize& aSize);
 
+  void HandlePartialUpdate(const MutexAutoLock& aProofOfLock,
+                           const IntRegion& aInvalidRegion,
+                           const IntRect& aValidRect);
   RefPtr<WaylandShmBuffer> ObtainBufferFromPool(
       const MutexAutoLock& aProofOfLock);
   void ReturnBufferToPool(const MutexAutoLock& aProofOfLock,
                           const RefPtr<WaylandShmBuffer>& aBuffer);
   void EnforcePoolSizeLimit(const MutexAutoLock& aProofOfLock);
   void BufferReleaseCallbackHandler(wl_buffer* aBuffer);
 
   nsTArray<RefPtr<WaylandShmBuffer>> mInUseBuffers;
   nsTArray<RefPtr<WaylandShmBuffer>> mAvailableBuffers;
   RefPtr<WaylandShmBuffer> mCurrentBuffer;
+  RefPtr<WaylandShmBuffer> mPreviousBuffer;
 };
 
 class WaylandDMABUFBuffer {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandDMABUFBuffer);
 
   static RefPtr<WaylandDMABUFBuffer> Create(const LayoutDeviceIntSize& aSize,
                                             GLContext* aGL);
@@ -149,28 +156,32 @@ class WaylandDMABUFBuffer {
   const LayoutDeviceIntSize mSize;
   RefPtr<DMABufSurfaceRGBA> mDMABufSurface;
   UniquePtr<gl::MozFramebuffer> mFB;
 };
 
 class NativeSurfaceWaylandDMABUF final : public NativeSurfaceWayland {
  public:
   Maybe<GLuint> GetNextFramebuffer() override;
-  void Commit(const IntRegion& aInvalidRegion) override;
+  void Commit(const IntRegion& aInvalidRegion,
+              const IntRect& aValidRect) override;
   void DestroyGLResources() override;
 
   static void BufferReleaseCallbackHandler(void* aData, wl_buffer* aBuffer);
 
  private:
   friend RefPtr<NativeSurfaceWayland> NativeSurfaceWayland::Create(
       const IntSize& aSize, GLContext* aGL);
 
   NativeSurfaceWaylandDMABUF(const IntSize& aSize, GLContext* aGL);
   ~NativeSurfaceWaylandDMABUF() = default;
 
+  void HandlePartialUpdate(const MutexAutoLock& aProofOfLock,
+                           const IntRegion& aInvalidRegion,
+                           const IntRect& aValidRect);
   RefPtr<WaylandDMABUFBuffer> ObtainBufferFromPool(
       const MutexAutoLock& aProofOfLock);
   void ReturnBufferToPool(const MutexAutoLock& aProofOfLock,
                           const RefPtr<WaylandDMABUFBuffer>& aBuffer);
   void EnforcePoolSizeLimit(const MutexAutoLock& aProofOfLock);
   UniquePtr<gl::MozFramebuffer> CreateFramebufferForTexture(
       const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize,
       GLuint aTexture);
@@ -178,16 +189,17 @@ class NativeSurfaceWaylandDMABUF final :
       const MutexAutoLock& aProofOfLock, GLContext* aGL, const IntSize& aSize);
   void BufferReleaseCallbackHandler(wl_buffer* aBuffer);
 
   RefPtr<GLContext> mGL;
 
   nsTArray<RefPtr<WaylandDMABUFBuffer>> mInUseBuffers;
   nsTArray<RefPtr<WaylandDMABUFBuffer>> mAvailableBuffers;
   RefPtr<WaylandDMABUFBuffer> mCurrentBuffer;
+  RefPtr<WaylandDMABUFBuffer> mPreviousBuffer;
 
   struct DepthBufferEntry final {
     RefPtr<GLContext> mGL;
     IntSize mSize;
     WeakPtr<gl::DepthAndStencilBuffer> mBuffer;
   };
 
   nsTArray<DepthBufferEntry> mDepthBuffers;
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -5042,21 +5042,17 @@
 
 - name: gfx.webrender.compositor.force-enabled
   type: bool
   value: false
   mirror: once
 
 - name: gfx.webrender.compositor.max_update_rects
   type: uint32_t
-#if defined(XP_WIN) || defined(XP_MACOSX)
   value: 1
-#else
-  value: 0
-#endif
   mirror: once
 
 - name: gfx.webrender.compositor.surface-pool-size
   type: uint32_t
   value: 25
   mirror: once
 
 # Number of damage rects we can give to the compositor for a new frame with