Commit 004ebac4 authored by Andrew Osmond's avatar Andrew Osmond
Browse files

Bug 1874523. a=RyanVM

parent e3091f5e
Loading
Loading
Loading
Loading
+30 −24
Original line number Diff line number Diff line
@@ -432,26 +432,31 @@ RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(

  RawAccessFrameRef recycledFrame;
  if (mRecycle.front().mFrame) {
    recycledFrame = mRecycle.front().mFrame->RawAccessRef();
    MOZ_ASSERT(recycledFrame);
    recycledFrame = mRecycle.front().mFrame->RawAccessRef(
        gfx::DataSourceSurface::READ_WRITE);
    mRecycle.pop_front();

    // If we couldn't map in the surface, it is probably because the frame was
    // finalized and we did not expect to need to write into it again. This
    // happens for the first frames produced during an animation.
    if (recycledFrame) {
      if (mForceUseFirstFrameRefreshArea) {
      // We are still crossing the loop boundary and cannot rely upon the dirty
      // rects of entries in mDisplay to be representative. E.g. The first frame
      // is probably has a full frame dirty rect.
        // We are still crossing the loop boundary and cannot rely upon the
        // dirty rects of entries in mDisplay to be representative. E.g. The
        // first frame is probably has a full frame dirty rect.
        aRecycleRect = mFirstFrameRefreshArea;
      } else {
        // Calculate the recycle rect for the recycled frame. This is the
      // cumulative dirty rect of all of the frames ahead of us to be displayed,
      // and to be used for recycling. Or in other words, the dirty rect between
      // the recycled frame and the decoded frame which reuses the buffer.
        // cumulative dirty rect of all of the frames ahead of us to be
        // displayed, and to be used for recycling. Or in other words, the dirty
        // rect between the recycled frame and the decoded frame which reuses
        // the buffer.
        //
      // We know at this point that mRecycle contains either frames from the end
      // of the animation with the first frame refresh area as the dirty rect
      // (plus the first frame likewise) and frames with their actual dirty rect
      // from the start. mDisplay should also only contain frames from the start
      // of the animation onwards.
        // We know at this point that mRecycle contains either frames from the
        // end of the animation with the first frame refresh area as the dirty
        // rect (plus the first frame likewise) and frames with their actual
        // dirty rect from the start. mDisplay should also only contain frames
        // from the start of the animation onwards.
        aRecycleRect.SetRect(0, 0, 0, 0);
        for (const RefPtr<imgFrame>& frame : mDisplay) {
          aRecycleRect = aRecycleRect.Union(frame->GetDirtyRect());
@@ -460,6 +465,7 @@ RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(
          aRecycleRect = aRecycleRect.Union(entry.mDirtyRect);
        }
      }
    }
  } else {
    mRecycle.pop_front();
  }
+9 −5
Original line number Diff line number Diff line
@@ -303,8 +303,7 @@ nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
  if (mCurrentFrame) {
    mHasFrameToTake = true;

    // Gather the raw pointers the decoders will use.
    mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
    mImageData = mCurrentFrame.Data();

    // We should now be on |aFrameNum|. (Note that we're comparing the frame
    // number, which is zero-based, with the frame count, which is one-based.)
@@ -316,6 +315,9 @@ nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
    // Update our state to reflect the new frame.
    MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
    mInFrame = true;
  } else {
    mImageData = nullptr;
    mImageDataLength = 0;
  }

  return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
@@ -376,7 +378,8 @@ RawAccessFrameRef Decoder::AllocateFrameInternal(
      // animation parameters elsewhere. For now we just drop it.
      bool blocked = ref.get() == mRestoreFrame.get();
      if (!blocked) {
        blocked = NS_FAILED(ref->InitForDecoderRecycle(aAnimParams.ref()));
        blocked = NS_FAILED(
            ref->InitForDecoderRecycle(aAnimParams.ref(), &mImageDataLength));
      }

      if (blocked) {
@@ -395,12 +398,13 @@ RawAccessFrameRef Decoder::AllocateFrameInternal(
    bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
    auto frame = MakeNotNull<RefPtr<imgFrame>>();
    if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
                                        aAnimParams, bool(mFrameRecycler)))) {
                                        aAnimParams, bool(mFrameRecycler),
                                        &mImageDataLength))) {
      NS_WARNING("imgFrame::Init should succeed");
      return RawAccessFrameRef();
    }

    ref = frame->RawAccessRef();
    ref = frame->RawAccessRef(gfx::DataSourceSurface::READ_WRITE);
    if (!ref) {
      frame->Abort();
      return RawAccessFrameRef();
+16 −33
Original line number Diff line number Diff line
@@ -145,7 +145,8 @@ imgFrame::~imgFrame() {
nsresult imgFrame::InitForDecoder(const nsIntSize& aImageSize,
                                  SurfaceFormat aFormat, bool aNonPremult,
                                  const Maybe<AnimationParams>& aAnimParams,
                                  bool aShouldRecycle) {
                                  bool aShouldRecycle,
                                  uint32_t* aImageDataLength) {
  // Assert for properties that should be verified by decoders,
  // warn for properties related to bad content.
  if (!SurfaceCache::IsLegalSize(aImageSize)) {
@@ -217,10 +218,15 @@ nsresult imgFrame::InitForDecoder(const nsIntSize& aImageSize,
    }
  }

  if (aImageDataLength) {
    *aImageDataLength = GetImageDataLength();
  }

  return NS_OK;
}

nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams,
                                         uint32_t* aImageDataLength) {
  // We want to recycle this frame, but there is no guarantee that consumers are
  // done with it in a timely manner. Let's ensure they are done with it first.
  MonitorAutoLock lock(mMonitor);
@@ -287,6 +293,10 @@ nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
  mDisposalMethod = aAnimParams.mDisposalMethod;
  mDirtyRect = GetRect();

  if (aImageDataLength) {
    *aImageDataLength = GetImageDataLength();
  }

  return NS_OK;
}

@@ -391,7 +401,10 @@ nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable,

DrawableFrameRef imgFrame::DrawableRef() { return DrawableFrameRef(this); }

RawAccessFrameRef imgFrame::RawAccessRef() { return RawAccessFrameRef(this); }
RawAccessFrameRef imgFrame::RawAccessRef(
    gfx::DataSourceSurface::MapType aMapType) {
  return RawAccessFrameRef(this, aMapType);
}

imgFrame::SurfaceWithFormat imgFrame::SurfaceForDrawing(
    bool aDoPartialDecode, bool aDoTile, ImageRegion& aRegion,
@@ -586,36 +599,6 @@ uint32_t imgFrame::GetImageDataLength() const {
  return GetImageBytesPerRow() * mImageSize.height;
}

void imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const {
  MonitorAutoLock lock(mMonitor);
  GetImageDataInternal(aData, aLength);
}

void imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const {
  mMonitor.AssertCurrentThreadOwns();
  MOZ_ASSERT(mRawSurface);

  if (mRawSurface) {
    // TODO: This is okay for now because we only realloc shared surfaces on
    // the main thread after decoding has finished, but if animations want to
    // read frame data off the main thread, we will need to reconsider this.
    *aData = mRawSurface->GetData();
    MOZ_ASSERT(*aData,
               "mRawSurface is non-null, but GetData is null in GetImageData");
  } else {
    *aData = nullptr;
  }

  *aLength = GetImageDataLength();
}

uint8_t* imgFrame::GetImageData() const {
  uint8_t* data;
  uint32_t length;
  GetImageData(&data, &length);
  return data;
}

void imgFrame::FinalizeSurface() {
  MonitorAutoLock lock(mMonitor);
  FinalizeSurfaceInternal();
+32 −12
Original line number Diff line number Diff line
@@ -55,7 +55,8 @@ class imgFrame {
  nsresult InitForDecoder(const nsIntSize& aImageSize, SurfaceFormat aFormat,
                          bool aNonPremult,
                          const Maybe<AnimationParams>& aAnimParams,
                          bool aShouldRecycle);
                          bool aShouldRecycle,
                          uint32_t* aImageDataLength = nullptr);

  /**
   * Reinitialize this imgFrame with the new parameters, but otherwise retain
@@ -65,7 +66,8 @@ class imgFrame {
   * given an IDecoderFrameRecycler object which may yield a recycled imgFrame
   * that was discarded to save memory.
   */
  nsresult InitForDecoderRecycle(const AnimationParams& aAnimParams);
  nsresult InitForDecoderRecycle(const AnimationParams& aAnimParams,
                                 uint32_t* aImageDataLength = nullptr);

  /**
   * Initialize this imgFrame with a new surface and draw the provided
@@ -90,7 +92,8 @@ class imgFrame {
  /**
   * Create a RawAccessFrameRef for the frame.
   */
  RawAccessFrameRef RawAccessRef();
  RawAccessFrameRef RawAccessRef(
      gfx::DataSourceSurface::MapType aMapType = gfx::DataSourceSurface::READ);

  bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
            SamplingFilter aSamplingFilter, uint32_t aImageFlags,
@@ -160,8 +163,6 @@ class imgFrame {
  BlendMethod GetBlendMethod() const { return mBlendMethod; }
  DisposalMethod GetDisposalMethod() const { return mDisposalMethod; }
  bool FormatHasAlpha() const { return mFormat == SurfaceFormat::OS_RGBA; }
  void GetImageData(uint8_t** aData, uint32_t* length) const;
  uint8_t* GetImageData() const;

  const IntRect& GetDirtyRect() const { return mDirtyRect; }
  void SetDirtyRect(const IntRect& aDirtyRect) { mDirtyRect = aDirtyRect; }
@@ -187,7 +188,6 @@ class imgFrame {

  bool AreAllPixelsWritten() const MOZ_REQUIRES(mMonitor);
  nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
  void GetImageDataInternal(uint8_t** aData, uint32_t* length) const;
  uint32_t GetImageBytesPerRow() const;
  uint32_t GetImageDataLength() const;
  void FinalizeSurfaceInternal();
@@ -357,13 +357,25 @@ class DrawableFrameRef final {
 */
class RawAccessFrameRef final {
 public:
  RawAccessFrameRef() : mData(nullptr) {}
  RawAccessFrameRef() = default;

  explicit RawAccessFrameRef(imgFrame* aFrame)
      : mFrame(aFrame), mData(nullptr) {
  explicit RawAccessFrameRef(imgFrame* aFrame,
                             gfx::DataSourceSurface::MapType aMapType)
      : mFrame(aFrame) {
    MOZ_ASSERT(mFrame, "Need a frame");

    mData = mFrame->GetImageData();
    // Note that we do not use ScopedMap here because it holds a strong
    // reference to the underlying surface. This affects the reuse logic for
    // recycling in imgFrame::InitForDecoderRecycle.
    {
      MonitorAutoLock lock(mFrame->mMonitor);
      gfx::DataSourceSurface::MappedSurface map;
      if (mFrame->mRawSurface && mFrame->mRawSurface->Map(aMapType, &map)) {
        MOZ_ASSERT(map.mData);
        mData = map.mData;
      }
    }

    if (!mData) {
      mFrame = nullptr;
    }
@@ -374,11 +386,15 @@ class RawAccessFrameRef final {
    aOther.mData = nullptr;
  }

  ~RawAccessFrameRef() {}
  ~RawAccessFrameRef() { reset(); }

  RawAccessFrameRef& operator=(RawAccessFrameRef&& aOther) {
    MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");

    if (mFrame) {
      MonitorAutoLock lock(mFrame->mMonitor);
      mFrame->mRawSurface->Unmap();
    }
    mFrame = std::move(aOther.mFrame);
    mData = aOther.mData;
    aOther.mData = nullptr;
@@ -402,6 +418,10 @@ class RawAccessFrameRef final {
  const imgFrame* get() const { return mFrame; }

  void reset() {
    if (mFrame) {
      MonitorAutoLock lock(mFrame->mMonitor);
      mFrame->mRawSurface->Unmap();
    }
    mFrame = nullptr;
    mData = nullptr;
  }
@@ -413,7 +433,7 @@ class RawAccessFrameRef final {
  RawAccessFrameRef& operator=(const RawAccessFrameRef& aOther) = delete;

  RefPtr<imgFrame> mFrame;
  uint8_t* mData;
  uint8_t* mData = nullptr;
};

}  // namespace image
+3 −6
Original line number Diff line number Diff line
@@ -759,8 +759,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete) {
  while (!buffer.Recycle().empty()) {
    gfx::IntRect recycleRect;
    RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
    EXPECT_TRUE(frameRef);
    EXPECT_FALSE(ReinitForRecycle(frameRef));
    EXPECT_FALSE(frameRef);
  }

  // Reinsert the first two frames as recyclable and reset again.
@@ -829,8 +828,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
  gfx::IntRect recycleRect;
  EXPECT_FALSE(buffer.Recycle().empty());
  RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_TRUE(frameRef);
  EXPECT_FALSE(ReinitForRecycle(frameRef));
  EXPECT_FALSE(frameRef);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Insert a recyclable partial frame. Its dirty rect shouldn't matter since
@@ -842,8 +840,7 @@ TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
  VerifyAdvance(buffer, 5, true);
  EXPECT_FALSE(buffer.Recycle().empty());
  frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_TRUE(frameRef);
  EXPECT_FALSE(ReinitForRecycle(frameRef));
  EXPECT_FALSE(frameRef);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Insert a recyclable partial frame. Its dirty rect should match the recycle