Skip to content

Commit

Permalink
Bug 1465619 - Part 9. Use redesigned AnimationFrameBuffer interface a…
Browse files Browse the repository at this point in the history
…nd retaining buffer. r=tnikkel

This patch makes AnimationSurfaceProvider use the new abstractions
provided by AnimationFrameBuffer and AnimationFrameRetainedBuffer to
provide storage and lifetime management of decoders and the produced
frames. We initially start out with an implementation that will just
keep every frame forever, like our historical behaviour. The next patch
will add support for discarding.

Differential Revision: https://phabricator.services.mozilla.com/D7514
  • Loading branch information
aosmond committed Oct 22, 2018
1 parent 5615197 commit f525b64
Show file tree
Hide file tree
Showing 4 changed files with 584 additions and 45 deletions.
145 changes: 145 additions & 0 deletions image/AnimationFrameBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,150 @@
namespace mozilla {
namespace image {

AnimationFrameRetainedBuffer::AnimationFrameRetainedBuffer(size_t aThreshold,
size_t aBatch,
size_t aStartFrame)
: AnimationFrameBuffer(aBatch, aStartFrame)
, mThreshold(aThreshold)
{
// To simplify the code, we have the assumption that the threshold for
// entering discard-after-display mode is at least twice the batch size (since
// that is the most frames-pending-decode we will request) + 1 for the current
// frame. That way the redecoded frames being inserted will never risk
// overlapping the frames we will discard due to the animation progressing.
// That may cause us to use a little more memory than we want but that is an
// acceptable tradeoff for simplicity.
size_t minThreshold = 2 * mBatch + 1;
if (mThreshold < minThreshold) {
mThreshold = minThreshold;
}

// The maximum number of frames we should ever have decoded at one time is
// twice the batch. That is a good as number as any to start our decoding at.
mPending = mBatch * 2;
}

bool
AnimationFrameRetainedBuffer::InsertInternal(RefPtr<imgFrame>&& aFrame)
{
// We should only insert new frames if we actually asked for them.
MOZ_ASSERT(!mSizeKnown);
MOZ_ASSERT(mFrames.Length() < mThreshold);

mFrames.AppendElement(std::move(aFrame));
MOZ_ASSERT(mSize == mFrames.Length());
return mSize < mThreshold;
}

bool
AnimationFrameRetainedBuffer::ResetInternal()
{
// If we haven't crossed the threshold, then we know by definition we have
// not discarded any frames. If we previously requested more frames, but
// it would have been more than we would have buffered otherwise, we can
// stop the decoding after one more frame.
if (mPending > 1 && mSize >= mBatch * 2 + 1) {
MOZ_ASSERT(!mSizeKnown);
mPending = 1;
}

// Either the decoder is still running, or we have enough frames already.
// No need for us to restart it.
return false;
}

bool
AnimationFrameRetainedBuffer::MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea)
{
MOZ_ASSERT(!mSizeKnown);
mSizeKnown = true;
mPending = 0;
mFrames.Compact();
return false;
}

void
AnimationFrameRetainedBuffer::AdvanceInternal()
{
// We should not have advanced if we never inserted.
MOZ_ASSERT(!mFrames.IsEmpty());
// We only want to change the current frame index if we have advanced. This
// means either a higher frame index, or going back to the beginning.
size_t framesLength = mFrames.Length();
// We should never have advanced beyond the frame buffer.
MOZ_ASSERT(mGetIndex < framesLength);
// We should never advance if the current frame is null -- it needs to know
// the timeout from it at least to know when to advance.
MOZ_ASSERT_IF(mGetIndex > 0, mFrames[mGetIndex - 1]);
MOZ_ASSERT_IF(mGetIndex == 0, mFrames[framesLength - 1]);
// The owner should have already accessed the next frame, so it should also
// be available.
MOZ_ASSERT(mFrames[mGetIndex]);

if (!mSizeKnown) {
// Calculate how many frames we have requested ahead of the current frame.
size_t buffered = mPending + framesLength - mGetIndex - 1;
if (buffered < mBatch) {
// If we have fewer frames than the batch size, then ask for more. If we
// do not have any pending, then we know that there is no active decoding.
mPending += mBatch;
}
}
}

imgFrame*
AnimationFrameRetainedBuffer::Get(size_t aFrame, bool aForDisplay)
{
// We should not have asked for a frame if we never inserted.
if (mFrames.IsEmpty()) {
MOZ_ASSERT_UNREACHABLE("Calling Get() when we have no frames");
return nullptr;
}

// If we don't have that frame, return an empty frame ref.
if (aFrame >= mFrames.Length()) {
return nullptr;
}

// If we have space for the frame, it should always be available.
if (!mFrames[aFrame]) {
MOZ_ASSERT_UNREACHABLE("Calling Get() when frame is unavailable");
return nullptr;
}

// If we are advancing on behalf of the animation, we don't expect it to be
// getting any frames (besides the first) until we get the desired frame.
MOZ_ASSERT(aFrame == 0 || mAdvance == 0);
return mFrames[aFrame].get();
}

bool
AnimationFrameRetainedBuffer::IsFirstFrameFinished() const
{
return !mFrames.IsEmpty() && mFrames[0]->IsFinished();
}

bool
AnimationFrameRetainedBuffer::IsLastInsertedFrame(imgFrame* aFrame) const
{
return !mFrames.IsEmpty() && mFrames.LastElement().get() == aFrame;
}

void
AnimationFrameRetainedBuffer::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
const AddSizeOfCb& aCallback)
{
size_t i = 0;
for (const RefPtr<imgFrame>& frame : mFrames) {
++i;
frame->AddSizeOfExcludingThis(aMallocSizeOf,
[&](AddSizeOfCbData& aMetadata) {
aMetadata.index = i;
aCallback(aMetadata);
}
);
}
}

} // namespace image
} // namespace mozilla
Loading

0 comments on commit f525b64

Please sign in to comment.