mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-26 14:46:02 +00:00
Bug 523950 - Part 9. Integrate AnimationSurfaceProvider with AnimationFrameBuffer. r=tnikkel
This commit is contained in:
parent
01f0dbdb83
commit
39b30d06d4
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user