Bug 1465619 - Part 3. Improve the clear rect calculations to take into account recycling in BlendAnimationFilter. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Thu, 31 May 2018 11:43:15 -0400
changeset 490705 fc76b59e51b4f9faf21f40f2f518a94ce87ea100
parent 490704 847e8234669346a34af39546b1e64c93f8a8e882
child 490706 edeea0a49a3322a3af796fba2f582ed0f1f6bd5f
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewerstnikkel
bugs1465619
milestone65.0a1
Bug 1465619 - Part 3. Improve the clear rect calculations to take into account recycling in BlendAnimationFilter. r=tnikkel The clear rect and the recycle rect can overlap, and depending on the size of the clear rect, it could be a significant amount of data that needs to be copied from the restore frame. This patch minimizes the copying for a row which contains both the recycle rect and the clear rect. Differential Revision: https://phabricator.services.mozilla.com/D7508
image/SurfaceFilters.h
--- a/image/SurfaceFilters.h
+++ b/image/SurfaceFilters.h
@@ -363,16 +363,23 @@ class BlendAnimationFilter final : publi
 public:
   BlendAnimationFilter()
     : mRow(0)
     , mRowLength(0)
     , mRecycleRow(0)
     , mRecycleRowMost(0)
     , mRecycleRowOffset(0)
     , mRecycleRowLength(0)
+    , mClearRow(0)
+    , mClearRowMost(0)
+    , mClearPrefixLength(0)
+    , mClearInfixOffset(0)
+    , mClearInfixLength(0)
+    , mClearPostfixOffset(0)
+    , mClearPostfixLength(0)
     , mOverProc(nullptr)
     , mBaseFrameStartPtr(nullptr)
     , mBaseFrameRowPtr(nullptr)
   { }
 
   template <typename... Rest>
   nsresult Configure(const BlendAnimationConfig& aConfig, const Rest&... aRest)
   {
@@ -429,16 +436,17 @@ public:
         }
         break;
     }
 
     // Determine what we need to clear and what we need to copy. If this frame
     // is a full frame and uses source blending, there is no need to consider
     // the disposal method of the previous frame.
     gfx::IntRect dirtyRect(outputRect);
+    gfx::IntRect clearRect;
     if (!fullFrame || blendMethod != BlendMethod::SOURCE) {
       const RawAccessFrameRef& restoreFrame =
         aConfig.mDecoder->GetRestoreFrameRef();
       if (restoreFrame) {
         MOZ_ASSERT(restoreFrame->GetImageSize() == outputSize);
         MOZ_ASSERT(restoreFrame->IsFinished());
 
         // We can safely use this pointer without holding a RawAccessFrameRef
@@ -457,43 +465,68 @@ public:
             dirtyRect = mFrameRect.Union(restoreDirtyRect);
             break;
           case DisposalMethod::CLEAR:
             // We only need to clear if the rect is outside the frame rect (i.e.
             // overwrites a non-overlapping area) or the blend method may cause
             // us to combine old data and new.
             if (!mFrameRect.Contains(restoreBlendRect) ||
                 blendMethod == BlendMethod::OVER) {
-              mClearRect = restoreBlendRect;
+              clearRect = restoreBlendRect;
             }
 
             // If we are clearing the whole frame, we do not need to retain a
             // reference to the base frame buffer.
-            if (outputRect.IsEqualEdges(mClearRect)) {
+            if (outputRect.IsEqualEdges(clearRect)) {
               mBaseFrameStartPtr = nullptr;
             } else {
-              dirtyRect = mFrameRect.Union(restoreDirtyRect).Union(mClearRect);
+              dirtyRect = mFrameRect.Union(restoreDirtyRect).Union(clearRect);
             }
             break;
         }
       } else if (!fullFrame) {
         // This must be the first frame, clear everything.
-        mClearRect = outputRect;
+        clearRect = outputRect;
       }
     }
 
     // We may be able to reuse parts of our underlying buffer that we are
     // writing the new frame to. The recycle rect gives us the invalidation
     // region which needs to be copied from the restore frame.
     const gfx::IntRect& recycleRect = aConfig.mDecoder->GetRecycleRect();
     mRecycleRow = recycleRect.y;
     mRecycleRowMost = recycleRect.YMost();
     mRecycleRowOffset = recycleRect.x * sizeof(uint32_t);
     mRecycleRowLength = recycleRect.width * sizeof(uint32_t);
 
+    if (!clearRect.IsEmpty()) {
+      // The clear rect interacts with the recycle rect because we need to copy
+      // the prefix and postfix data from the base frame. The one thing we do
+      // know is that the infix area is always cleared explicitly.
+      mClearRow = clearRect.y;
+      mClearRowMost = clearRect.YMost();
+      mClearInfixOffset = clearRect.x * sizeof(uint32_t);
+      mClearInfixLength = clearRect.width * sizeof(uint32_t);
+
+      // The recycle row offset is where we need to begin copying base frame
+      // data for a row. If this offset begins after or at the clear infix
+      // offset, then there is no prefix data at all.
+      if (mClearInfixOffset > mRecycleRowOffset) {
+        mClearPrefixLength = mClearInfixOffset - mRecycleRowOffset;
+      }
+
+      // Similar to the prefix, if the postfix offset begins outside the recycle
+      // rect, then we know we already have all the data we need.
+      mClearPostfixOffset = mClearInfixOffset + mClearInfixLength;
+      size_t recycleRowEndOffset = mRecycleRowOffset + mRecycleRowLength;
+      if (mClearPostfixOffset < recycleRowEndOffset) {
+        mClearPostfixLength = recycleRowEndOffset - mClearPostfixOffset;
+      }
+    }
+
     // The dirty rect, or delta between the current frame and the previous frame
     // (chronologically, not necessarily the restore frame) is the last
     // animation parameter we need to initialize the new frame with.
     currentFrame->SetDirtyRect(dirtyRect);
 
     if (!mBaseFrameStartPtr) {
       // Switch to SOURCE if no base frame to ensure we don't allocate an
       // intermediate buffer below. OVER does nothing without the base frame
@@ -646,29 +679,28 @@ private:
     bool needBaseFrame = mRow >= mRecycleRow &&
                          mRow < mRecycleRowMost;
 
     if (!mBaseFrameRowPtr) {
       // No base frame, so we are clearing everything.
       if (needBaseFrame) {
         memset(dest + mRecycleRowOffset, 0, mRecycleRowLength);
       }
-    } else if (mClearRect.height > 0 &&
-               mClearRect.y <= mRow &&
-               mClearRect.YMost() > mRow) {
+    } else if (mClearRow <= mRow && mClearRowMost > mRow) {
       // We have a base frame, but we are inside the area to be cleared.
       // Only copy the data we need from the source.
-      size_t prefixLength = mClearRect.x * sizeof(uint32_t);
-      size_t clearLength = mClearRect.width * sizeof(uint32_t);
-      size_t postfixOffset = prefixLength + clearLength;
-      size_t postfixLength = mRowLength - postfixOffset;
-      MOZ_ASSERT(prefixLength + clearLength + postfixLength == mRowLength);
-      memcpy(dest, mBaseFrameRowPtr, prefixLength);
-      memset(dest + prefixLength, 0, clearLength);
-      memcpy(dest + postfixOffset, mBaseFrameRowPtr + postfixOffset, postfixLength);
+      if (needBaseFrame) {
+        memcpy(dest + mRecycleRowOffset,
+               mBaseFrameRowPtr + mRecycleRowOffset,
+               mClearPrefixLength);
+        memcpy(dest + mClearPostfixOffset,
+               mBaseFrameRowPtr + mClearPostfixOffset,
+               mClearPostfixLength);
+      }
+      memset(dest + mClearInfixOffset, 0, mClearInfixLength);
     } else if (needBaseFrame) {
       memcpy(dest + mRecycleRowOffset,
              mBaseFrameRowPtr + mRecycleRowOffset,
              mRecycleRowLength);
     }
   }
 
   bool AdvanceRowOutsideFrameRect()
@@ -715,23 +747,31 @@ private:
   int32_t  mRow;                       /// The row in unclamped frame rect space
                                        /// that we're currently writing.
   size_t mRowLength;                   /// Length in bytes of a row that is the input
                                        /// for the next filter.
   int32_t mRecycleRow;                 /// The starting row of the recycle rect.
   int32_t mRecycleRowMost;             /// The ending row of the recycle rect.
   size_t mRecycleRowOffset;            /// Row offset in bytes of the recycle rect.
   size_t mRecycleRowLength;            /// Row length in bytes of the recycle rect.
+
+  /// The frame area to clear before blending the current frame.
+  int32_t mClearRow;                   /// The starting row of the clear rect.
+  int32_t mClearRowMost;               /// The ending row of the clear rect.
+  size_t mClearPrefixLength;           /// Row length in bytes of clear prefix.
+  size_t mClearInfixOffset;            /// Row offset in bytes of clear area.
+  size_t mClearInfixLength;            /// Row length in bytes of clear area.
+  size_t mClearPostfixOffset;          /// Row offset in bytes of clear postfix.
+  size_t mClearPostfixLength;          /// Row length in bytes of clear postfix.
+
   SkBlitRow::Proc32 mOverProc;         /// Function pointer to perform over blending.
   const uint8_t* mBaseFrameStartPtr;   /// Starting row pointer to the base frame
                                        /// data from which we copy pixel data from.
   const uint8_t* mBaseFrameRowPtr;     /// Current row pointer to the base frame
                                        /// data.
-  gfx::IntRect mClearRect;             /// The frame area to clear before blending
-                                       /// the current frame.
 };
 
 //////////////////////////////////////////////////////////////////////////////
 // RemoveFrameRectFilter
 //////////////////////////////////////////////////////////////////////////////
 
 template <typename Next> class RemoveFrameRectFilter;