Don't create back buffer for front buffer until we know what type to create. (bug 1399692 part 4, r=bas) draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 23 Oct 2017 15:33:40 -0400
changeset 695945 1e7f2e8659f332ad5a691dd36dc1fc626f83a540
parent 695944 d84c721a7e2672d499bc532fa46f08795c965d88
child 695946 f3f76c0a394d30ddb01e0e63ff992a80f967a25a
push id88593
push userbmo:rhunt@eqrion.net
push dateFri, 10 Nov 2017 01:41:10 +0000
reviewersbas
bugs1399692
milestone58.0a1
Don't create back buffer for front buffer until we know what type to create. (bug 1399692 part 4, r=bas) This commit is an optimization for double buffering that delays the creation of a back buffer until we know what kind of content type it needs to be. Before this commit, we would EnsureBackBufferIfFrontBuffer before BeginPaint, then in BeginPaint we could determine that we actually needed a different kind of buffer because the content changed type, and recreate it. This was needed because BeginPaint would copy the old front buffer to the buffer created by EnsureBackBufferIfFrontBuffer, and then if anything failed or we had determined we couldn't reuse the buffer, we would create a new one and copy that "temporary" back buffer over, and use the new one. This is unnecessary because we only need read access on that "temporary" back buffer, and so we can just use the current front buffer instead. This optimization only affects the double buffered case, and the single buffered or basic cases should remain the same. Note: Because we now need the front buffer for copying into the new back buffer, we cannot Clear() it away in some error cases. Note: The code in FinalizeFrame assumes that the back and front buffer have the same size. This was implicitly enforced before, and now needs to be explicitly enforced. This commit tries to preserve the exact same behavior, although the restriction should be removed as long as the back buffer is large enough for the visible region. MozReview-Commit-ID: 2hyrrUhA4zO
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -144,58 +144,55 @@ ContentClient::BeginPaint(PaintedLayer* 
                            dest.mValidRegion);
 
   if (result.mRegionToDraw.IsEmpty())
     return result;
 
   OpenMode lockMode = aFlags & PAINT_ASYNC ? OpenMode::OPEN_READ_ASYNC_WRITE
                                            : OpenMode::OPEN_READ_WRITE;
 
-  if (mBuffer) {
-    if (mBuffer->Lock(lockMode)) {
-      // Do not modify result.mRegionToDraw or result.mContentType after this call.
-      // Do not modify the back buffer's bufferRect, bufferRotation, or didSelfCopy.
-      FinalizeFrame(result.mRegionToDraw);
-    } else {
-      // Abandon everything and redraw it all. Ideally we'd reallocate and copy
-      // the old to the new and then call FinalizeFrame on the new buffer so that
-      // we only need to draw the latest bits, but we need a big refactor to support
-      // that ordering.
-      result.mRegionToDraw = dest.mNeededRegion;
-      dest.mCanReuseBuffer = false;
-      Clear();
-    }
-  }
-
   // We need to disable rotation if we're going to be resampled when
   // drawing, because we might sample across the rotation boundary.
   // Also disable buffer rotation when using webrender.
   bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
                          !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
                          !(aLayer->Manager()->AsWebRenderLayerManager());
   bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
   IntRect drawBounds = result.mRegionToDraw.GetBounds();
 
   if (dest.mCanReuseBuffer) {
     MOZ_ASSERT(mBuffer);
 
-    if (!mBuffer->AdjustTo(dest.mBufferRect,
-                           drawBounds,
-                           canHaveRotation,
-                           canDrawRotated)) {
+    if (mBuffer->Lock(lockMode)) {
+      // Do not modify result.mRegionToDraw or result.mContentType after this call.
+      // Do not modify the back buffer's bufferRect, bufferRotation, or didSelfCopy.
+      FinalizeFrame(result.mRegionToDraw);
+
+      if (!mBuffer->AdjustTo(dest.mBufferRect,
+                             drawBounds,
+                             canHaveRotation,
+                             canDrawRotated)) {
+        dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
+        dest.mCanReuseBuffer = false;
+        dest.mMustRemoveFrontBuffer = true;
+        mBuffer->Unlock();
+      }
+    } else {
       dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
       dest.mCanReuseBuffer = false;
+      dest.mMustRemoveFrontBuffer = true;
     }
   }
 
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
-  // The buffer's not big enough, changed content, or we failed to unrotate it,
-  // so we need to allocate a new one and prepare it for drawing
+  // We never had a buffer, the buffer wasn't big enough, the content changed
+  // types, or we failed to unrotate the buffer when requested. In any case,
+  // we need to allocate a new one and prepare it for drawing.
   if (!dest.mCanReuseBuffer) {
     uint32_t bufferFlags = 0;
     if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
       bufferFlags |= BUFFER_COMPONENT_ALPHA;
     }
 
     RefPtr<RotatedBuffer> newBuffer = CreateBuffer(result.mContentType,
                                                    dest.mBufferRect,
@@ -204,39 +201,39 @@ ContentClient::BeginPaint(PaintedLayer* 
     if (!newBuffer) {
       if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
         gfxCriticalNote << "Failed buffer for "
                         << dest.mBufferRect.x << ", "
                         << dest.mBufferRect.y << ", "
                         << dest.mBufferRect.Width() << ", "
                         << dest.mBufferRect.Height();
       }
-      Clear();
       return result;
     }
 
     if (!newBuffer->Lock(lockMode)) {
       gfxCriticalNote << "Failed to lock new back buffer.";
-      Clear();
       return result;
     }
 
-    // If we have an existing back buffer, copy it into the new back buffer
-    if (mBuffer) {
-      newBuffer->UpdateDestinationFrom(*mBuffer, newBuffer->BufferRect());
+    // If we have an existing front buffer, copy it into the new back buffer
+    if (RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer()) {
+      if (frontBuffer->Lock(OpenMode::OPEN_READ_ONLY)) {
+        newBuffer->UpdateDestinationFrom(*frontBuffer, newBuffer->BufferRect());
+        frontBuffer->Unlock();
+      } else {
+        gfxCriticalNote << "Failed to copy front buffer to back buffer.";
+        return result;
+      }
 
-      // We are done with the old back buffer now and it is about to be
-      // destroyed, so unlock it
-      mBuffer->Unlock();
+      if (dest.mMustRemoveFrontBuffer) {
+        Clear();
+      }
     }
 
-    // Ensure our reference to the front buffer is released
-    // as well as the old back buffer
-    Clear();
-
     mBuffer = newBuffer;
   }
 
   NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
                "Rotation disabled, but we have nonzero rotation?");
 
   nsIntRegion invalidate;
   invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
@@ -363,24 +360,27 @@ ContentClient::PrepareDrawTargetForPaint
 ContentClient::BufferDecision
 ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
                                        uint32_t aFlags)
 {
   gfxContentType layerContentType =
     aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
                                     gfxContentType::COLOR_ALPHA;
 
+  RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
+
   SurfaceMode mode;
   gfxContentType contentType;
   IntRect destBufferRect;
   nsIntRegion neededRegion;
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canReuseBuffer = !!mBuffer;
   bool canKeepBufferContents = true;
+  bool mustRemoveFrontBuffer = false;
 
   while (true) {
     mode = aLayer->GetSurfaceMode();
     neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
     canReuseBuffer = canReuseBuffer && ValidBufferSize(mBufferSizePolicy,
                                                        mBuffer->BufferRect().Size(),
                                                        neededRegion.GetBounds().Size());
     contentType = layerContentType;
@@ -392,18 +392,32 @@ ContentClient::CalculateBufferForPaint(P
       } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) {
         // The buffer's big enough but doesn't contain everything that's
         // going to be visible. We'll move it.
         destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size());
       } else {
         destBufferRect = neededRegion.GetBounds();
       }
     } else {
-      // We won't be reusing the buffer.  Compute a new rect.
-      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+      // We won't be reusing the buffer. Compute a new rect.
+      if (frontBuffer) {
+        // We must create a buffer that is the same size as the front buffer,
+        // or else we need to remove the front buffer
+        if (ValidBufferSize(mBufferSizePolicy,
+                            frontBuffer->BufferRect().Size(),
+                            neededRegion.GetBounds().Size())) {
+          destBufferRect = frontBuffer->BufferRect();
+          destBufferRect.MoveTo(neededRegion.GetBounds().TopLeft());
+        } else {
+          destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+          mustRemoveFrontBuffer = true;
+        }
+      } else {
+        destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+      }
     }
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
       if (!aLayer->GetParent() ||
           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
@@ -428,21 +442,21 @@ ContentClient::CalculateBufferForPaint(P
 
       // We need to validate the entire buffer, to make sure that only valid
       // pixels are sampled.
       neededRegion = destBufferRect;
     }
 
     // If we have an existing buffer, but the content type has changed or we
     // have transitioned into/out of component alpha, then we need to recreate it.
-    if (canKeepBufferContents &&
-        mBuffer &&
-        (contentType != mBuffer->GetContentType() ||
-        (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mBuffer->HaveBufferOnWhite()))
-    {
+    bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
+    bool changedSurfaceOrContent = frontBuffer &&
+                                   (contentType != frontBuffer->GetContentType() ||
+                                    needsComponentAlpha != frontBuffer->HaveBufferOnWhite());
+    if (canKeepBufferContents && changedSurfaceOrContent) {
       // Restart the decision process; we won't re-enter since we guard on
       // being able to keep the buffer contents.
       canReuseBuffer = false;
       canKeepBufferContents = false;
       validRegion.SetEmpty();
       continue;
     }
 
@@ -455,29 +469,36 @@ ContentClient::CalculateBufferForPaint(P
   BufferDecision dest;
   dest.mNeededRegion = Move(neededRegion);
   dest.mValidRegion = Move(validRegion);
   dest.mBufferRect = destBufferRect;
   dest.mBufferMode = mode;
   dest.mBufferContentType = contentType;
   dest.mCanReuseBuffer = canReuseBuffer;
   dest.mCanKeepBufferContents = canKeepBufferContents;
+  dest.mMustRemoveFrontBuffer = mustRemoveFrontBuffer;
   return dest;
 }
 
 bool
 ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
                                const gfx::IntSize& aBufferSize,
                                const gfx::IntSize& aVisibleBoundsSize)
 {
   return (aVisibleBoundsSize == aBufferSize ||
           (SizedToVisibleBounds != aPolicy &&
            aVisibleBoundsSize < aBufferSize));
 }
 
+RefPtr<RotatedBuffer>
+ContentClient::GetFrontBuffer() const
+{
+  return mBuffer;
+}
+
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
@@ -826,18 +847,16 @@ ContentClientDoubleBuffered::SwapBuffers
 
   mFrontAndBackBufferDiffer = true;
 }
 
 ContentClient::PaintState
 ContentClientDoubleBuffered::BeginPaint(PaintedLayer* aLayer,
                                         uint32_t aFlags)
 {
-  EnsureBackBufferIfFrontBuffer();
-
   mIsNewBuffer = false;
 
   if (!mFrontBuffer || !mBuffer) {
     mFrontAndBackBufferDiffer = false;
   }
 
   if (mFrontAndBackBufferDiffer) {
     if (mFrontBuffer->DidSelfCopy()) {
@@ -854,16 +873,22 @@ ContentClientDoubleBuffered::BeginPaint(
       mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
       mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
     }
   }
 
   return ContentClient::BeginPaint(aLayer, aFlags);
 }
 
+RefPtr<RotatedBuffer>
+ContentClientDoubleBuffered::GetFrontBuffer() const
+{
+  return mFrontBuffer;
+}
+
 // Sync front/back buffers content
 // After executing, the new back buffer has the same (interesting) pixels as
 // the new front buffer, and mValidRegion et al. are correct wrt the new
 // back buffer (i.e. as they were for the old back buffer)
 void
 ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
 {
   if (!mFrontAndBackBufferDiffer) {
@@ -902,21 +927,10 @@ ContentClientDoubleBuffered::FinalizeFra
   }
 
   if (mFrontBuffer->Lock(OpenMode::OPEN_READ_ONLY)) {
     mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion.GetBounds());
     mFrontBuffer->Unlock();
   }
 }
 
-void
-ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
-{
-  if (!mBuffer && mFrontBuffer) {
-    mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(),
-                                   mFrontBuffer->GetFormat(),
-                                   mTextureFlags);
-    MOZ_ASSERT(mBuffer);
-  }
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -188,30 +188,33 @@ protected:
   struct BufferDecision {
     nsIntRegion mNeededRegion;
     nsIntRegion mValidRegion;
     gfx::IntRect mBufferRect;
     SurfaceMode mBufferMode;
     gfxContentType mBufferContentType;
     bool mCanReuseBuffer;
     bool mCanKeepBufferContents;
+    bool mMustRemoveFrontBuffer;
   };
 
   /**
    * Decide whether we can keep our current buffer and its contents,
    * and return a struct containing the regions to paint, invalidate,
    * the new buffer rect, surface mode, and content type.
    */
   BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer,
                                          uint32_t aFlags);
 
   static bool ValidBufferSize(BufferSizePolicy aPolicy,
                               const gfx::IntSize& aBufferSize,
                               const gfx::IntSize& aVisibleBoundsSize);
 
+  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const;
+
   /**
    * Any actions that should be performed at the last moment before we begin
    * rendering the next frame. I.e., after we calculate what we will draw,
    * but before we rotate the buffer and possibly create new buffers.
    * aRegionToDraw is the region which is guaranteed to be overwritten when
    * drawing the next frame.
    */
   virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) {}
@@ -352,26 +355,26 @@ public:
                     TextureDumpMode aCompress=TextureDumpMode::Compress) override;
 
   virtual void Clear() override;
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
   virtual PaintState BeginPaint(PaintedLayer* aLayer, uint32_t aFlags) override;
 
+  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const override;
+
   virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
   }
 
 private:
-  void EnsureBackBufferIfFrontBuffer();
-
   RefPtr<RemoteRotatedBuffer> mFrontBuffer;
   nsIntRegion mFrontUpdatedRegion;
   bool mFrontAndBackBufferDiffer;
 };
 
 /**
  * A single buffered ContentClientRemoteBuffer. We have a single
  * TextureClient/Host which we update and then send a message to the