Bug 1711061 - Part 4. Implement AnimationSurfaceProvider for animated rasterized images. r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D126790
This commit is contained in:
Andrew Osmond 2021-10-27 01:24:32 +00:00
parent bd93e83c2a
commit 546e3f7640
7 changed files with 114 additions and 36 deletions

View File

@ -60,6 +60,7 @@ bool AnimationFrameRetainedBuffer::ResetInternal() {
bool AnimationFrameRetainedBuffer::MarkComplete(
const gfx::IntRect& aFirstFrameRefreshArea) {
MOZ_ASSERT(!mSizeKnown);
mFirstFrameRefreshArea = aFirstFrameRefreshArea;
mSizeKnown = true;
mPending = 0;
mFrames.Compact();
@ -192,6 +193,11 @@ bool AnimationFrameDiscardingQueue::MarkComplete(
mPending = 0;
}
// If we encounter a redecode error, just make the first frame refresh area to
// be the full frame, because we don't really know what we can safely recycle.
mFirstFrameRefreshArea =
mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea;
// We reached the end of the animation, the next frame we get, if we get
// another, will be the first frame again.
mInsertIndex = 0;
@ -461,17 +467,5 @@ RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(
return recycledFrame;
}
bool AnimationFrameRecyclingQueue::MarkComplete(
const gfx::IntRect& aFirstFrameRefreshArea) {
bool continueDecoding =
AnimationFrameDiscardingQueue::MarkComplete(aFirstFrameRefreshArea);
// If we encounter a redecode error, just make the first frame refresh area to
// be the full frame, because we don't really know what we can safely recycle.
mFirstFrameRefreshArea =
mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea;
return continueDecoding;
}
} // namespace image
} // namespace mozilla

View File

@ -103,6 +103,14 @@ class AnimationFrameBuffer {
*/
size_t Size() const { return mSize; }
/**
* @returns The first frame refresh area. This is used instead of the dirty
* rect for the last frame when transitioning back to the first frame.
*/
const gfx::IntRect& FirstFrameRefreshArea() const {
return mFirstFrameRefreshArea;
}
/**
* @returns True if encountered an error during redecode which should cause
* the caller to stop inserting frames.
@ -286,6 +294,10 @@ class AnimationFrameBuffer {
*/
virtual bool ResetInternal() = 0;
/// The first frame refresh area. This is used instead of the dirty rect for
/// the last frame when transitioning back to the first frame.
gfx::IntRect mFirstFrameRefreshArea;
// The total number of frames in the animation. If mSizeKnown is true, it is
// the actual total regardless of how many frames are available, otherwise it
// is the total number of inserted frames.
@ -420,7 +432,6 @@ class AnimationFrameRecyclingQueue final
public:
explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer&& aQueue);
bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) override;
void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
const AddSizeOfCb& aCallback) override;
@ -447,9 +458,6 @@ class AnimationFrameRecyclingQueue final
};
const std::deque<RecycleEntry>& Recycle() const { return mRecycle; }
const gfx::IntRect& FirstFrameRefreshArea() const {
return mFirstFrameRefreshArea;
}
protected:
void AdvanceInternal() override;
@ -460,10 +468,6 @@ class AnimationFrameRecyclingQueue final
/// is adjacent to the first frame in the mDisplay queue.
std::deque<RecycleEntry> mRecycle;
/// The first frame refresh area. This is used instead of the dirty rect for
/// the last frame when transitioning back to the first frame.
gfx::IntRect mFirstFrameRefreshArea;
/// Force recycled frames to use the first frame refresh area as their dirty
/// rect. This is used when we are recycling frames from the end of an
/// animation to produce frames at the beginning of an animation.

View File

@ -7,12 +7,15 @@
#include "mozilla/StaticPrefs_image.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/SharedSurfacesChild.h"
#include "mozilla/layers/SourceSurfaceSharedData.h"
#include "nsProxyRelease.h"
#include "DecodePool.h"
#include "Decoder.h"
using namespace mozilla::gfx;
using namespace mozilla::layers;
namespace mozilla {
namespace image {
@ -25,7 +28,9 @@ AnimationSurfaceProvider::AnimationSurfaceProvider(
mImage(aImage.get()),
mDecodingMutex("AnimationSurfaceProvider::mDecoder"),
mDecoder(aDecoder.get()),
mFramesMutex("AnimationSurfaceProvider::mFrames") {
mFramesMutex("AnimationSurfaceProvider::mFrames"),
mCompositedFrameRequested(false),
mSharedAnimation(MakeRefPtr<SharedSurfacesAnimation>()) {
MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
"Use MetadataDecodingTask for metadata decodes");
MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
@ -47,6 +52,7 @@ AnimationSurfaceProvider::AnimationSurfaceProvider(
AnimationSurfaceProvider::~AnimationSurfaceProvider() {
DropImageReference();
mSharedAnimation->Destroy();
if (mDecoder) {
mDecoder->SetFrameRecycler(nullptr);
}
@ -115,15 +121,32 @@ void AnimationSurfaceProvider::Reset() {
void AnimationSurfaceProvider::Advance(size_t aFrame) {
bool restartDecoder;
RefPtr<SourceSurface> surface;
IntRect dirtyRect;
{
// Typical advancement of a frame.
MutexAutoLock lock(mFramesMutex);
restartDecoder = mFrames->AdvanceTo(aFrame);
imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
MOZ_ASSERT(frame);
if (aFrame != 0) {
dirtyRect = frame->GetDirtyRect();
} else {
MOZ_ASSERT(mFrames->SizeKnown());
dirtyRect = mFrames->FirstFrameRefreshArea();
}
surface = frame->GetSourceSurface();
MOZ_ASSERT(surface);
}
if (restartDecoder) {
DecodePool::Singleton()->AsyncRun(this);
}
mCompositedFrameRequested = false;
auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
mSharedAnimation->SetCurrentFrame(sharedSurface, dirtyRect);
}
DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
@ -485,5 +508,27 @@ RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
return mFrames->RecycleFrame(aRecycleRect);
}
nsresult AnimationSurfaceProvider::UpdateKey(
layers::RenderRootStateManager* aManager,
wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<SourceSurface> surface;
{
MutexAutoLock lock(mFramesMutex);
imgFrame* frame =
mFrames->Get(mFrames->Displayed(), /* aForDisplay */ true);
if (!frame) {
return NS_ERROR_NOT_AVAILABLE;
}
surface = frame->GetSourceSurface();
}
mCompositedFrameRequested = true;
auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
return mSharedAnimation->UpdateKey(sharedSurface, aManager, aResources, aKey);
}
} // namespace image
} // namespace mozilla

View File

@ -19,6 +19,10 @@
#include "AnimationFrameBuffer.h"
namespace mozilla {
namespace layers {
class SharedSurfacesAnimation;
}
namespace image {
/**
@ -41,12 +45,6 @@ class AnimationSurfaceProvider final : public ISurfaceProvider,
//////////////////////////////////////////////////////////////////////////////
public:
// We use the ISurfaceProvider constructor of DrawableSurface to indicate that
// our surfaces are computed lazily.
DrawableSurface Surface() override {
return DrawableSurface(WrapNotNull(this));
}
bool IsFinished() const override;
bool IsFullyDecoded() const override;
size_t LogicalSizeInBytes() const override;
@ -54,6 +52,8 @@ class AnimationSurfaceProvider final : public ISurfaceProvider,
const AddSizeOfCb& aCallback) override;
void Reset() override;
void Advance(size_t aFrame) override;
bool MayAdvance() const override { return mCompositedFrameRequested; }
void MarkMayAdvance() override { mCompositedFrameRequested = true; }
protected:
DrawableFrameRef DrawableRef(size_t aFrame) override;
@ -86,6 +86,15 @@ class AnimationSurfaceProvider final : public ISurfaceProvider,
public:
RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override;
//////////////////////////////////////////////////////////////////////////////
// IDecoderFrameRecycler implementation.
//////////////////////////////////////////////////////////////////////////////
public:
nsresult UpdateKey(layers::RenderRootStateManager* aManager,
wr::IpcResourceUpdateQueue& aResources,
wr::ImageKey& aKey) override;
private:
virtual ~AnimationSurfaceProvider();
@ -114,6 +123,13 @@ class AnimationSurfaceProvider final : public ISurfaceProvider,
/// The frames of this animation, in order.
UniquePtr<AnimationFrameBuffer> mFrames;
/// Whether the current frame was requested for display since the last time we
/// advanced the animation.
bool mCompositedFrameRequested;
///
RefPtr<layers::SharedSurfacesAnimation> mSharedAnimation;
};
} // namespace image

View File

@ -318,7 +318,6 @@ RefreshResult FrameAnimator::AdvanceFrame(AnimationState& aState,
// Set currentAnimationFrameIndex at the last possible moment
aState.mCurrentAnimationFrameIndex = nextFrameIndex;
aState.mCompositedFrameRequested = false;
aCurrentFrame = std::move(nextFrame);
aFrames.Advance(nextFrameIndex);
@ -395,7 +394,7 @@ RefreshResult FrameAnimator::RequestRefresh(AnimationState& aState,
// If nothing has accessed the composited frame since the last time we
// advanced, then there is no point in continuing to advance the animation.
// This has the effect of freezing the animation while not in view.
if (!aState.mCompositedFrameRequested &&
if (!result.Surface().MayAdvance() &&
aState.MaybeAdvanceAnimationFrameTime(aTime)) {
return ret;
}
@ -443,13 +442,18 @@ RefreshResult FrameAnimator::RequestRefresh(AnimationState& aState,
LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState,
bool aMarkUsed) {
aState.mCompositedFrameRequested = true;
LookupResult result = SurfaceCache::Lookup(
ImageKey(mImage),
RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated),
aMarkUsed);
if (result) {
// If we are getting the frame directly (e.g. through tests or canvas), we
// need to ensure the animation is marked to allow advancing to the next
// frame.
result.Surface().MarkMayAdvance();
}
if (aState.mCompositedFrameInvalid) {
MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
MOZ_ASSERT(aState.GetHasRequestedDecode());

View File

@ -35,7 +35,6 @@ class AnimationState {
mHasRequestedDecode(false),
mIsCurrentlyDecoded(false),
mCompositedFrameInvalid(false),
mCompositedFrameRequested(false),
mDiscarded(false) {}
/**
@ -240,10 +239,6 @@ class AnimationState {
//! valid to draw to the screen.
bool mCompositedFrameInvalid;
//! Whether the composited frame was requested from the animator since the
//! last time we advanced the animation.
bool mCompositedFrameRequested;
//! Whether this image is currently discarded. Only set to true after the
//! image has been decoded at least once.
bool mDiscarded;

View File

@ -82,6 +82,8 @@ class ISurfaceProvider : public WebRenderImageProvider {
virtual void Reset() {}
virtual void Advance(size_t aFrame) {}
virtual bool MayAdvance() const { return false; }
virtual void MarkMayAdvance() {}
/// @return the availability state of this ISurfaceProvider, which indicates
/// whether DrawableRef() could successfully return a surface. Should only be
@ -220,6 +222,24 @@ class MOZ_STACK_CLASS DrawableSurface final {
mProvider->Advance(aFrame);
}
bool MayAdvance() const {
if (!mProvider) {
MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
return false;
}
return mProvider->MayAdvance();
}
void MarkMayAdvance() {
if (!mProvider) {
MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
return;
}
mProvider->MarkMayAdvance();
}
bool IsFullyDecoded() const {
if (!mProvider) {
MOZ_ASSERT_UNREACHABLE(