Bug 1484812 - Use SwapBuffersWithDamage on EGL platforms (Wayland/Android) r=jnicol,mstange,jgilbert
authorGreg V <greg@unrelenting.technology>
Thu, 30 Jan 2020 10:46:54 +0000
changeset 512120 c7072bfa62d69ad0bf15e2d2d0b83a896f891f0a
parent 512119 9dc9deb3ba3bdfb7875b8259308d31ec46f4d6d1
child 512121 92d62985486856c0fb0b5a7b940816e19b1faf64
push id37072
push usercsabou@mozilla.com
push dateThu, 30 Jan 2020 15:44:43 +0000
treeherdermozilla-central@f97c48da9cee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjnicol, mstange, jgilbert
bugs1484812
milestone74.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 1484812 - Use SwapBuffersWithDamage on EGL platforms (Wayland/Android) r=jnicol,mstange,jgilbert EGL_KHR_swap_buffers_with_damage (or EGL_EXT_swap_buffers_with_damage) is an EGL extension that allows the application to inform the display server (system compositor) which areas of the window have changed. This commit implements support for that extension in the layers compositor. The layers compositor always renders the whole frame, so we're only getting the benefit of not redrawing unchanged areas *in the system compositor*, not actually doing partial invalidation/compositing, but that makes the implementation simpler (no need to track buffer age). Differential Revision: https://phabricator.services.mozilla.com/D51517
gfx/gl/GLContext.h
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -31,16 +31,17 @@
 
 #include "../../mfbt/RefPtr.h"
 #include "../../mfbt/UniquePtr.h"
 #include "../../mfbt/ThreadLocal.h"
 
 #include "GLDefs.h"
 #include "GLLibraryLoader.h"
 #include "nsISupportsImpl.h"
+#include "nsRegionFwd.h"
 #include "plstr.h"
 #include "GLContextTypes.h"
 #include "SurfaceTypes.h"
 #include "GLContextSymbols.h"
 #include "base/platform_thread.h"  // for PlatformThreadId
 #include "mozilla/GenericRefCounted.h"
 #include "mozilla/WeakPtr.h"
 #include "gfx2DGlue.h"
@@ -3347,16 +3348,26 @@ class GLContext : public GenericAtomicRe
   /**
    * If this context wraps a double-buffered target, swap the back
    * and front buffers.  It should be assumed that after a swap, the
    * contents of the new back buffer are undefined.
    */
   virtual bool SwapBuffers() { return false; }
 
   /**
+   * Stores a damage region (in origin bottom left coordinates), which
+   * makes the next SwapBuffers call do eglSwapBuffersWithDamage if supported.
+   *
+   * Note that even if only part of the context is damaged, the entire buffer
+   * needs to be filled with up-to-date contents. This region is only a hint
+   * telling the system compositor which parts of the buffer were updated.
+   */
+  virtual void SetDamage(const nsIntRegion& aDamageRegion) {}
+
+  /**
    * Defines a two-dimensional texture image for context target surface
    */
   virtual bool BindTexImage() { return false; }
   /*
    * Releases a color buffer that is being used as a texture
    */
   virtual bool ReleaseTexImage() { return false; }
 
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -65,16 +65,18 @@ class GLContextEGL : public GLContext {
   virtual bool RenewSurface(widget::CompositorWidget* aWidget) override;
 
   virtual void ReleaseSurface() override;
 
   Maybe<SymbolLoader> GetSymbolLoader() const override;
 
   virtual bool SwapBuffers() override;
 
+  virtual void SetDamage(const nsIntRegion& aDamageRegion) override;
+
   virtual void GetWSIInfo(nsCString* const out) const override;
 
   // hold a reference to the given surface
   // for the lifetime of this context.
   void HoldSurface(gfxASurface* aSurf);
 
   EGLSurface GetEGLSurface() const { return mSurface; }
 
@@ -112,16 +114,18 @@ class GLContextEGL : public GLContext {
   bool mBound = false;
 
   bool mIsPBuffer = false;
   bool mIsDoubleBuffered = false;
   bool mCanBindToTexture = false;
   bool mShareWithEGLImage = false;
   bool mOwnsContext = true;
 
+  nsIntRegion mDamageRegion;
+
   static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(
       GLLibraryEGL*, EGLConfig config, EGLenum bindToTextureFormat,
       gfx::IntSize& pbsize);
 #if defined(MOZ_WAYLAND)
   static EGLSurface CreateWaylandBufferSurface(GLLibraryEGL*, EGLConfig config,
                                                gfx::IntSize& pbsize);
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -501,22 +501,42 @@ void GLContextEGL::ReleaseSurface() {
 Maybe<SymbolLoader> GLContextEGL::GetSymbolLoader() const {
   return mEgl->GetSymbolLoader();
 }
 
 bool GLContextEGL::SwapBuffers() {
   EGLSurface surface =
       mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
   if (surface) {
+    if ((mEgl->IsExtensionSupported(
+             GLLibraryEGL::EXT_swap_buffers_with_damage) ||
+         mEgl->IsExtensionSupported(
+             GLLibraryEGL::KHR_swap_buffers_with_damage))) {
+      std::vector<EGLint> rects;
+      for (auto iter = mDamageRegion.RectIter(); !iter.Done(); iter.Next()) {
+        const IntRect& r = iter.Get();
+        rects.push_back(r.X());
+        rects.push_back(r.Y());
+        rects.push_back(r.Width());
+        rects.push_back(r.Height());
+      }
+      mDamageRegion.SetEmpty();
+      return mEgl->fSwapBuffersWithDamage(mEgl->Display(), surface,
+                                          rects.data(), rects.size() / 4);
+    }
     return mEgl->fSwapBuffers(mEgl->Display(), surface);
   } else {
     return false;
   }
 }
 
+void GLContextEGL::SetDamage(const nsIntRegion& aDamageRegion) {
+  mDamageRegion = aDamageRegion;
+}
+
 void GLContextEGL::GetWSIInfo(nsCString* const out) const {
   out->AppendLiteral("EGL_VENDOR: ");
   out->Append(
       (const char*)mEgl->fQueryString(mEgl->Display(), LOCAL_EGL_VENDOR));
 
   out->AppendLiteral("\nEGL_VERSION: ");
   out->Append(
       (const char*)mEgl->fQueryString(mEgl->Display(), LOCAL_EGL_VERSION));
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -67,17 +67,19 @@ static const char* sEGLExtensionNames[] 
     "EGL_KHR_stream_consumer_gltexture",
     "EGL_EXT_device_query",
     "EGL_NV_stream_consumer_gltexture_yuv",
     "EGL_ANGLE_stream_producer_d3d_texture",
     "EGL_ANGLE_device_creation",
     "EGL_ANGLE_device_creation_d3d11",
     "EGL_KHR_surfaceless_context",
     "EGL_KHR_create_context_no_error",
-    "EGL_MOZ_create_context_provoking_vertex_dont_care"};
+    "EGL_MOZ_create_context_provoking_vertex_dont_care",
+    "EGL_EXT_swap_buffers_with_damage",
+    "EGL_KHR_swap_buffers_with_damage"};
 
 PRLibrary* LoadApitraceLibrary() {
   const char* path = nullptr;
 
 #ifdef ANDROID
   // We only need to explicitly dlopen egltrace
   // on android as we can use LD_PRELOAD or other tricks
   // on other platforms. We look for it in /data/local
@@ -698,16 +700,42 @@ bool GLLibraryEGL::DoEnsureInitialized(b
     // fails to render anything when a real surface is provided later on. We
     // only have the EGL vendor available here, so just avoid using this
     // extension on all Mali devices.
     if (strcmp((const char*)vendor, "ARM") == 0) {
       MarkExtensionUnsupported(KHR_surfaceless_context);
     }
   }
 
+  if (IsExtensionSupported(EXT_swap_buffers_with_damage)) {
+    const SymLoadStruct symbols[] = {
+        {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
+         {{"eglSwapBuffersWithDamageEXT"}}},
+        END_OF_SYMBOLS};
+    if (!fnLoadSymbols(symbols)) {
+      NS_ERROR(
+          "EGL supports EXT_swap_buffers_with_damage without exposing its "
+          "functions!");
+      MarkExtensionUnsupported(EXT_swap_buffers_with_damage);
+    }
+  }
+
+  if (IsExtensionSupported(KHR_swap_buffers_with_damage)) {
+    const SymLoadStruct symbols[] = {
+        {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
+         {{"eglSwapBuffersWithDamageKHR"}}},
+        END_OF_SYMBOLS};
+    if (!fnLoadSymbols(symbols)) {
+      NS_ERROR(
+          "EGL supports KHR_swap_buffers_with_damage without exposing its "
+          "functions!");
+      MarkExtensionUnsupported(KHR_swap_buffers_with_damage);
+    }
+  }
+
   mInitialized = true;
   reporter.SetSuccessful();
   return true;
 }
 
 #undef SYMBOL
 #undef END_OF_SYMBOLS
 
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -80,16 +80,18 @@ class GLLibraryEGL final {
     EXT_device_query,
     NV_stream_consumer_gltexture_yuv,
     ANGLE_stream_producer_d3d_texture,
     ANGLE_device_creation,
     ANGLE_device_creation_d3d11,
     KHR_surfaceless_context,
     KHR_create_context_no_error,
     MOZ_create_context_provoking_vertex_dont_care,
+    EXT_swap_buffers_with_damage,
+    KHR_swap_buffers_with_damage,
     Extensions_Max
   };
 
   bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
     return mAvailableExtensions[aKnownExtension];
   }
 
   void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
@@ -344,16 +346,21 @@ class GLLibraryEGL final {
 
       // ANGLE_device_creation
       EGLDeviceEXT fCreateDeviceANGLE(EGLint device_type, void* native_device,
                                       const EGLAttrib* attrib_list) const
       WRAP(fCreateDeviceANGLE(device_type, native_device, attrib_list))
 
           EGLBoolean fReleaseDeviceANGLE(EGLDeviceEXT device)
               WRAP(fReleaseDeviceANGLE(device))
+
+      // EGL_EXT_swap_buffers_with_damage / EGL_KHR_swap_buffers_with_damage
+      EGLBoolean fSwapBuffersWithDamage(EGLDisplay dpy, EGLSurface surface,
+                                        const EGLint* rects, EGLint n_rects)
+          WRAP(fSwapBuffersWithDamage(dpy, surface, rects, n_rects))
 #undef WRAP
 #undef VOID_WRAP
 #undef PROFILE_CALL
 #undef BEFORE_CALL
 #undef AFTER_CALL
 #undef MOZ_FUNCTION_NAME
 
       ////
@@ -515,17 +522,21 @@ class GLLibraryEGL final {
     EGLBoolean(GLAPIENTRY* fStreamPostD3DTextureANGLE)(
         EGLDisplay dpy, EGLStreamKHR stream, void* texture,
         const EGLAttrib* attrib_list);
     // ANGLE_device_creation
     EGLDeviceEXT(GLAPIENTRY* fCreateDeviceANGLE)(EGLint device_type,
                                                  void* native_device,
                                                  const EGLAttrib* attrib_list);
     EGLBoolean(GLAPIENTRY* fReleaseDeviceANGLE)(EGLDeviceEXT device);
-
+    // EGL_EXT_swap_buffers_with_damage / EGL_KHR_swap_buffers_with_damage
+    EGLBoolean(GLAPIENTRY* fSwapBuffersWithDamage)(EGLDisplay dpy,
+                                                   EGLSurface surface,
+                                                   const EGLint* rects,
+                                                   EGLint n_rects);
   } mSymbols = {};
 
  private:
   bool DoEnsureInitialized();
   bool DoEnsureInitialized(bool forceAccel, nsACString* const out_failureId);
   EGLDisplay CreateDisplay(bool forceAccel, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                            nsACString* const out_failureId);
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1040,16 +1040,22 @@ Maybe<IntRect> CompositorOGL::BeginFrame
                             mClearColor.a);
   }
 #else
   mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b,
                           mClearColor.a);
 #endif  // defined(MOZ_WIDGET_ANDROID)
   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
 
+  for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const IntRect& r = iter.Get();
+    mCurrentFrameInvalidRegion.OrWith(
+        IntRect(r.X(), FlipY(r.YMost()), r.Width(), r.Height()));
+  }
+
   return Some(rect);
 }
 
 void CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect,
                                          bool aCopyFromSource,
                                          GLuint aSourceFrameBuffer,
                                          GLuint* aFBO, GLuint* aTexture,
                                          gfx::IntSize* aAllocSize) {
@@ -2028,28 +2034,31 @@ void CompositorOGL::EndFrame() {
   mCurrentRenderTarget = nullptr;
 
   if (mTexturePool) {
     mTexturePool->EndFrame();
   }
 
   InsertFrameDoneSync();
 
+  mGLContext->SetDamage(mCurrentFrameInvalidRegion);
   mGLContext->SwapBuffers();
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 
   // Unbind all textures
   for (GLuint i = 0; i <= 4; i++) {
     mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
     mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
     if (!mGLContext->IsGLES()) {
       mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
     }
   }
 
+  mCurrentFrameInvalidRegion.SetEmpty();
+
   Compositor::EndFrame();
 }
 
 void CompositorOGL::InsertFrameDoneSync() {
 #ifdef XP_MACOSX
   // Only do this on macOS.
   // On other platforms, SwapBuffers automatically applies back-pressure.
   if (mThisFrameDoneSync) {
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -518,15 +518,17 @@ class CompositorOGL final : public Compo
   bool mDestroyed;
 
   /**
    * Size of the OpenGL context's primary framebuffer in pixels. Used by
    * FlipY for the y-flipping calculation and by the DEAA shader.
    */
   gfx::IntSize mViewportSize;
 
+  gfx::IntRegion mCurrentFrameInvalidRegion;
+
   ShaderProgramOGL* mCurrentProgram;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITOROGL_H */