Bug 1465619 - Part 2. Add basic support for recycling a frame buffer to BlendAnimationFilter. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Sun, 03 Jun 2018 19:06:24 -0400
changeset 490704 847e8234669346a34af39546b1e64c93f8a8e882
parent 490703 b2a31a31fba6da90949b485e1728ef2dee40450e
child 490705 fc76b59e51b4f9faf21f40f2f518a94ce87ea100
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewerstnikkel
bugs1465619
milestone65.0a1
Bug 1465619 - Part 2. Add basic support for recycling a frame buffer to BlendAnimationFilter. r=tnikkel Given an invalidation rect, called the recycle rect, which represents the area which may have changed between the current frame and the frame we are recycling, we can not only reuse the buffer itself to avoid an allocation and free, we can also avoid copying pixel data from the restore frame which is already set. Differential Revision: https://phabricator.services.mozilla.com/D7507
image/Decoder.cpp
image/Decoder.h
image/SurfaceFilters.h
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -334,16 +334,20 @@ Decoder::AllocateFrameInternal(const gfx
   }
 
   if (aOutputSize.width <= 0 || aOutputSize.height <= 0 ||
       aFrameRect.Width() <= 0 || aFrameRect.Height() <= 0) {
     NS_WARNING("Trying to add frame with zero or negative size");
     return RawAccessFrameRef();
   }
 
+  // There is no underlying data to reuse, so reset the recycle rect to be
+  // the full frame, to ensure the restore frame is fully copied.
+  mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
+
   auto frame = MakeNotNull<RefPtr<imgFrame>>();
   bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
   if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat,
                                       aPaletteDepth, nonPremult,
                                       aAnimParams, ShouldBlendAnimation()))) {
     NS_WARNING("imgFrame::Init should succeed");
     return RawAccessFrameRef();
   }
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -440,16 +440,22 @@ public:
   }
 
   const gfx::IntRect& GetRestoreDirtyRect() const
   {
     MOZ_ASSERT(ShouldBlendAnimation());
     return mRestoreDirtyRect;
   }
 
+  const gfx::IntRect& GetRecycleRect() const
+  {
+    MOZ_ASSERT(ShouldBlendAnimation());
+    return mRecycleRect;
+  }
+
   bool HasFrameToTake() const { return mHasFrameToTake; }
   void ClearHasFrameToTake() {
     MOZ_ASSERT(mHasFrameToTake);
     mHasFrameToTake = false;
   }
 
 protected:
   friend class AutoRecordDecoderTelemetry;
@@ -601,16 +607,18 @@ private:
   // a complete current frame.
   RawAccessFrameRef mRestoreFrame;
 
   ImageMetadata mImageMetadata;
 
   gfx::IntRect mInvalidRect; // Tracks new rows as the current frame is decoded.
   gfx::IntRect mRestoreDirtyRect; // Tracks an invalidation region between the
                                   // restore frame and the previous frame.
+  gfx::IntRect mRecycleRect; // Tracks an invalidation region between the recycled
+                             // frame and the current frame.
   Maybe<gfx::IntSize> mOutputSize;  // The size of our output surface.
   Maybe<gfx::IntSize> mExpectedSize; // The expected size of the image.
   Progress mProgress;
 
   uint32_t mFrameCount; // Number of frames, including anything in-progress
   FrameTimeout mLoopLength;  // Length of a single loop of this image.
   gfx::IntRect mFirstFrameRefreshArea;  // The area of the image that needs to
                                         // be invalidated when the animation loops.
--- a/image/SurfaceFilters.h
+++ b/image/SurfaceFilters.h
@@ -359,16 +359,20 @@ struct BlendAnimationConfig
  */
 template <typename Next>
 class BlendAnimationFilter final : public SurfaceFilter
 {
 public:
   BlendAnimationFilter()
     : mRow(0)
     , mRowLength(0)
+    , mRecycleRow(0)
+    , mRecycleRowMost(0)
+    , mRecycleRowOffset(0)
+    , mRecycleRowLength(0)
     , mOverProc(nullptr)
     , mBaseFrameStartPtr(nullptr)
     , mBaseFrameRowPtr(nullptr)
   { }
 
   template <typename... Rest>
   nsresult Configure(const BlendAnimationConfig& aConfig, const Rest&... aRest)
   {
@@ -471,16 +475,25 @@ public:
             break;
         }
       } else if (!fullFrame) {
         // This must be the first frame, clear everything.
         mClearRect = 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);
+
     // 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
@@ -623,34 +636,43 @@ private:
 
   void WriteBaseFrameRow()
   {
     uint8_t* dest = mNext.CurrentRowPointer();
     if (!dest) {
       return;
     }
 
+    // No need to copy pixels from the base frame for rows that will not change
+    // between the recycled frame and the new frame.
+    bool needBaseFrame = mRow >= mRecycleRow &&
+                         mRow < mRecycleRowMost;
+
     if (!mBaseFrameRowPtr) {
       // No base frame, so we are clearing everything.
-      memset(dest, 0, mRowLength);
+      if (needBaseFrame) {
+        memset(dest + mRecycleRowOffset, 0, mRecycleRowLength);
+      }
     } else if (mClearRect.height > 0 &&
                mClearRect.y <= mRow &&
                mClearRect.YMost() > 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);
-    } else {
-      memcpy(dest, mBaseFrameRowPtr, mRowLength);
+    } else if (needBaseFrame) {
+      memcpy(dest + mRecycleRowOffset,
+             mBaseFrameRowPtr + mRecycleRowOffset,
+             mRecycleRowLength);
     }
   }
 
   bool AdvanceRowOutsideFrameRect()
   {
     // The unclamped frame rect may have a negative offset however we should
     // never be advancing the row via this path (otherwise mBaseFrameRowPtr
     // will be wrong.
@@ -689,16 +711,20 @@ private:
   gfx::IntRect mUnclampedFrameRect;    /// The frame rect before clamping.
   UniquePtr<uint8_t[]> mBuffer;        /// The intermediate buffer, if one is
                                        /// necessary because the frame rect width
                                        /// is larger than the image's logical width.
   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.
   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.
 };