Bug 523950 - Part 9. Integrate AnimationSurfaceProvider with AnimationFrameBuffer. r=tnikkel

This commit is contained in:
Andrew Osmond 2018-02-28 13:34:53 -05:00
parent 01f0dbdb83
commit 39b30d06d4
2 changed files with 160 additions and 47 deletions

View File

@ -8,6 +8,7 @@
#include "gfxPrefs.h"
#include "nsProxyRelease.h"
#include "DecodePool.h"
#include "Decoder.h"
using namespace mozilla::gfx;
@ -30,6 +31,22 @@ AnimationSurfaceProvider::AnimationSurfaceProvider(NotNull<RasterImage*> aImage,
"Use MetadataDecodingTask for metadata decodes");
MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
"Use DecodedSurfaceProvider for single-frame image decodes");
// We still produce paletted surfaces for GIF which means the frames are
// smaller than one would expect for APNG. This may be removed if/when
// bug 1337111 lands and it is enabled by default.
size_t pixelSize = aDecoder->GetType() == DecoderType::GIF
? sizeof(uint8_t) : sizeof(uint32_t);
// Calculate how many frames we need to decode in this animation before we
// enter decode-on-demand mode.
IntSize frameSize = aSurfaceKey.Size();
size_t threshold =
(size_t(gfxPrefs::ImageAnimatedDecodeOnDemandThresholdKB()) * 1024) /
(pixelSize * frameSize.width * frameSize.height);
size_t batch = gfxPrefs::ImageAnimatedDecodeOnDemandBatchSize();
mFrames.Initialize(threshold, batch, aCurrentFrame);
}
AnimationSurfaceProvider::~AnimationSurfaceProvider()
@ -49,6 +66,64 @@ AnimationSurfaceProvider::DropImageReference()
mImage.forget());
}
void
AnimationSurfaceProvider::Reset()
{
// We want to go back to the beginning.
bool mayDiscard;
bool restartDecoder;
{
MutexAutoLock lock(mFramesMutex);
// If we have not crossed the threshold, we know we haven't discarded any
// frames, and thus we know it is safe move our display index back to the
// very beginning. It would be cleaner to let the frame buffer make this
// decision inside the AnimationFrameBuffer::Reset method, but if we have
// crossed the threshold, we need to hold onto the decoding mutex too. We
// should avoid blocking the main thread on the decoder threads.
mayDiscard = mFrames.MayDiscard();
if (!mayDiscard) {
restartDecoder = mFrames.Reset();
}
}
if (mayDiscard) {
// We are over the threshold and have started discarding old frames. In
// that case we need to seize the decoding mutex. Thankfully we know that
// we are in the process of decoding at most the batch size frames, so
// this should not take too long to acquire.
MutexAutoLock lock(mDecodingMutex);
// Recreate the decoder so we can regenerate the frames again.
mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
MOZ_ASSERT(mDecoder);
MutexAutoLock lock2(mFramesMutex);
restartDecoder = mFrames.Reset();
}
if (restartDecoder) {
DecodePool::Singleton()->AsyncRun(this);
}
}
void
AnimationSurfaceProvider::Advance(size_t aFrame)
{
bool restartDecoder;
{
// Typical advancement of a frame.
MutexAutoLock lock(mFramesMutex);
restartDecoder = mFrames.AdvanceTo(aFrame);
}
if (restartDecoder) {
DecodePool::Singleton()->AsyncRun(this);
}
}
DrawableFrameRef
AnimationSurfaceProvider::DrawableRef(size_t aFrame)
{
@ -59,19 +134,7 @@ AnimationSurfaceProvider::DrawableRef(size_t aFrame)
return DrawableFrameRef();
}
if (mFrames.IsEmpty()) {
MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no frames");
return DrawableFrameRef();
}
// If we don't have that frame, return an empty frame ref.
if (aFrame >= mFrames.Length()) {
return DrawableFrameRef();
}
// We've got the requested frame. Return it.
MOZ_ASSERT(mFrames[aFrame]);
return mFrames[aFrame]->DrawableRef();
return mFrames.Get(aFrame);
}
bool
@ -84,13 +147,20 @@ AnimationSurfaceProvider::IsFinished() const
return false;
}
if (mFrames.IsEmpty()) {
if (mFrames.Frames().IsEmpty()) {
MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no frames");
return false;
}
// As long as we have at least one finished frame, we're finished.
return mFrames[0]->IsFinished();
return mFrames.Frames()[0]->IsFinished();
}
bool
AnimationSurfaceProvider::IsFullyDecoded() const
{
MutexAutoLock lock(mFramesMutex);
return mFrames.SizeKnown() && !mFrames.MayDiscard();
}
size_t
@ -122,9 +192,11 @@ AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
// that we must be careful to always use the same ordering elsewhere.
MutexAutoLock lock(mFramesMutex);
for (const RawAccessFrameRef& frame : mFrames) {
frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
aNonHeapSizeOut, aExtHandlesOut);
for (const RawAccessFrameRef& frame : mFrames.Frames()) {
if (frame) {
frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
aNonHeapSizeOut, aExtHandlesOut);
}
}
}
@ -133,7 +205,7 @@ AnimationSurfaceProvider::Run()
{
MutexAutoLock lock(mDecodingMutex);
if (!mDecoder || !mImage) {
if (!mDecoder) {
MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
return;
}
@ -148,15 +220,21 @@ AnimationSurfaceProvider::Run()
// Since we're not sure, rather than call CheckForNewFrameAtYield() here
// we call CheckForNewFrameAtTerminalState(), which handles both of these
// possibilities.
CheckForNewFrameAtTerminalState();
// We're done!
bool continueDecoding = CheckForNewFrameAtTerminalState();
FinishDecoding();
// Even if it is the last frame, we may not have enough frames buffered
// ahead of the current.
if (continueDecoding) {
MOZ_ASSERT(mDecoder);
continue;
}
return;
}
// Notify for the progress we've made so far.
if (mDecoder->HasProgress()) {
if (mImage && mDecoder->HasProgress()) {
NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
}
@ -166,19 +244,23 @@ AnimationSurfaceProvider::Run()
return;
}
// There's new output available - a new frame! Grab it.
// There's new output available - a new frame! Grab it. If we don't need any
// more for the moment we can break out of the loop.
MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
CheckForNewFrameAtYield();
if (!CheckForNewFrameAtYield()) {
return;
}
}
}
void
bool
AnimationSurfaceProvider::CheckForNewFrameAtYield()
{
mDecodingMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mDecoder);
bool justGotFirstFrame = false;
bool continueDecoding;
{
MutexAutoLock lock(mFramesMutex);
@ -187,17 +269,20 @@ AnimationSurfaceProvider::CheckForNewFrameAtYield()
RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
if (!frame) {
MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
return;
return true;
}
// We should've gotten a different frame than last time.
MOZ_ASSERT_IF(!mFrames.IsEmpty(),
mFrames.LastElement().get() != frame.get());
MOZ_ASSERT_IF(!mFrames.Frames().IsEmpty(),
mFrames.Frames().LastElement().get() != frame.get());
// Append the new frame to the list.
mFrames.AppendElement(Move(frame));
continueDecoding = mFrames.Insert(Move(frame));
if (mFrames.Length() == 1) {
// We only want to handle the first frame if it is the first pass for the
// animation decoder. The owning image will be cleared after that.
size_t frameCount = mFrames.Frames().Length();
if (frameCount == 1 && mImage) {
justGotFirstFrame = true;
}
}
@ -205,32 +290,37 @@ AnimationSurfaceProvider::CheckForNewFrameAtYield()
if (justGotFirstFrame) {
AnnounceSurfaceAvailable();
}
return continueDecoding;
}
void
bool
AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
{
mDecodingMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mDecoder);
bool justGotFirstFrame = false;
bool continueDecoding;
{
MutexAutoLock lock(mFramesMutex);
// The decoder may or may not have a new frame for us at this point. Avoid
// reinserting the same frame again.
RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
if (!frame) {
return;
}
if (!mFrames.IsEmpty() && mFrames.LastElement().get() == frame.get()) {
return; // We already have this one.
if (!frame || (!mFrames.Frames().IsEmpty() &&
mFrames.Frames().LastElement().get() == frame.get())) {
return mFrames.MarkComplete();
}
// Append the new frame to the list.
mFrames.AppendElement(Move(frame));
mFrames.Insert(Move(frame));
continueDecoding = mFrames.MarkComplete();
if (mFrames.Length() == 1) {
// We only want to handle the first frame if it is the first pass for the
// animation decoder. The owning image will be cleared after that.
if (mFrames.Frames().Length() == 1 && mImage) {
justGotFirstFrame = true;
}
}
@ -238,6 +328,8 @@ AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
if (justGotFirstFrame) {
AnnounceSurfaceAvailable();
}
return continueDecoding;
}
void
@ -258,14 +350,27 @@ void
AnimationSurfaceProvider::FinishDecoding()
{
mDecodingMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mImage);
MOZ_ASSERT(mDecoder);
// Send notifications.
NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
if (mImage) {
// Send notifications.
NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
}
// Destroy our decoder; we don't need it anymore.
mDecoder = nullptr;
bool mayDiscard;
{
MutexAutoLock lock(mFramesMutex);
mayDiscard = mFrames.MayDiscard();
}
if (mayDiscard) {
// Recreate the decoder so we can regenerate the frames again.
mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
MOZ_ASSERT(mDecoder);
} else {
mDecoder = nullptr;
}
// We don't need a reference to our image anymore, either, and we don't want
// one. We may be stored in the surface cache for a long time after decoding

View File

@ -13,6 +13,7 @@
#include "FrameAnimator.h"
#include "IDecodingTask.h"
#include "ISurfaceProvider.h"
#include "AnimationFrameBuffer.h"
namespace mozilla {
namespace image {
@ -45,11 +46,14 @@ public:
DrawableSurface Surface() override { return DrawableSurface(WrapNotNull(this)); }
bool IsFinished() const override;
bool IsFullyDecoded() const override;
size_t LogicalSizeInBytes() const override;
void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
size_t& aHeapSizeOut,
size_t& aNonHeapSizeOut,
size_t& aExtHandlesOut) override;
void Reset() override;
void Advance(size_t aFrame) override;
protected:
DrawableFrameRef DrawableRef(size_t aFrame) override;
@ -79,11 +83,15 @@ private:
virtual ~AnimationSurfaceProvider();
void DropImageReference();
void CheckForNewFrameAtYield();
void CheckForNewFrameAtTerminalState();
void AnnounceSurfaceAvailable();
void FinishDecoding();
// @returns Whether or not we should continue decoding.
bool CheckForNewFrameAtYield();
// @returns Whether or not we should restart decoding.
bool CheckForNewFrameAtTerminalState();
/// The image associated with our decoder.
RefPtr<RasterImage> mImage;
@ -97,7 +105,7 @@ private:
mutable Mutex mFramesMutex;
/// The frames of this animation, in order.
nsTArray<RawAccessFrameRef> mFrames;
AnimationFrameBuffer mFrames;
};
} // namespace image