зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1454149 - Do not advance animated images which are not displayed. r=tnikkel
All animated images on a page are currently registered with the refresh driver and advance with the tick refresh. These animations may not even be in view, and if they are large and thus cause redecoding, cause a marked increase in CPU usage for no benefit to the user. This patch adds an additional flag, mCompositedFrameRequested, to the AnimationState used by FrameAnimator. It is set to true each time the current animated image frame is requested via FrameAnimator::GetCompositedFrame. It is set to false each time the frame is advanced in FrameAnimator::AdvanceFrame (via FrameAnimator::RequestRefresh). If it is true when FrameAnimator::RequestRefresh is called, then it will advance the animation according to the normal rules. If it is false, then it will set the current animation time to the current time instead of advancing. This should not cause the animation to fall behind anymore or skip frames more than it does today. This is because if FrameAnimator::GetCompositedFrame is not called, then the internal state of the animation is advancing ahead of what the user sees. Once it is called, the new frame is far ahead of the previously displayed frame. The only difference now is that we will display the previous frame for slightly longer until the next refresh tick. Note that if an animated image is layerized (should not happen today) or otherwise uses an ImageContainer, this optimization fails. While we know whether or not we have an image container, we do not know if anything is actively using it.
This commit is contained in:
Родитель
49932142ae
Коммит
d19566e09c
|
@ -532,6 +532,7 @@ private:
|
|||
|
||||
DECL_GFX_PREF(Live, "image.animated.decode-on-demand.threshold-kb", ImageAnimatedDecodeOnDemandThresholdKB, uint32_t, 20480);
|
||||
DECL_GFX_PREF(Live, "image.animated.decode-on-demand.batch-size", ImageAnimatedDecodeOnDemandBatchSize, uint32_t, 6);
|
||||
DECL_GFX_PREF(Live, "image.animated.resume-from-last-displayed", ImageAnimatedResumeFromLastDisplayed, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.cache.factor2.threshold-surfaces", ImageCacheFactor2ThresholdSurfaces, int32_t, -1);
|
||||
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
|
||||
DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
|
||||
|
|
|
@ -168,6 +168,21 @@ AnimationState::SetAnimationFrameTime(const TimeStamp& aTime)
|
|||
mCurrentAnimationFrameTime = aTime;
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationState::MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime)
|
||||
{
|
||||
if (!gfxPrefs::ImageAnimatedResumeFromLastDisplayed() ||
|
||||
mCurrentAnimationFrameTime >= aTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are configured to stop an animation when it is out of view, and restart
|
||||
// it from the same point when it comes back into view. The same applies if it
|
||||
// was discarded while out of view.
|
||||
mCurrentAnimationFrameTime = aTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AnimationState::GetCurrentAnimationFrameIndex() const
|
||||
{
|
||||
|
@ -329,6 +344,7 @@ FrameAnimator::AdvanceFrame(AnimationState& aState,
|
|||
MOZ_ASSERT(currentFrameEndTime.isSome());
|
||||
aState.mCurrentAnimationFrameTime = *currentFrameEndTime;
|
||||
aState.mCurrentAnimationFrameIndex = nextFrameIndex;
|
||||
aState.mCompositedFrameRequested = false;
|
||||
aFrames.Advance(nextFrameIndex);
|
||||
|
||||
return ret;
|
||||
|
@ -375,6 +391,7 @@ FrameAnimator::AdvanceFrame(AnimationState& aState,
|
|||
|
||||
// Set currentAnimationFrameIndex at the last possible moment
|
||||
aState.mCurrentAnimationFrameIndex = nextFrameIndex;
|
||||
aState.mCompositedFrameRequested = false;
|
||||
aFrames.Advance(nextFrameIndex);
|
||||
|
||||
// If we're here, we successfully advanced the frame.
|
||||
|
@ -411,6 +428,7 @@ FrameAnimator::RequestRefresh(AnimationState& aState,
|
|||
RefreshResult ret;
|
||||
|
||||
if (aState.IsDiscarded()) {
|
||||
aState.MaybeAdvanceAnimationFrameTime(aTime);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -426,6 +444,7 @@ FrameAnimator::RequestRefresh(AnimationState& aState,
|
|||
|
||||
ret.mDirtyRect = aState.UpdateStateInternal(result, aAnimationFinished, mSize);
|
||||
if (aState.IsDiscarded() || !result) {
|
||||
aState.MaybeAdvanceAnimationFrameTime(aTime);
|
||||
if (!ret.mDirtyRect.IsEmpty()) {
|
||||
ret.mFrameAdvanced = true;
|
||||
}
|
||||
|
@ -442,6 +461,15 @@ FrameAnimator::RequestRefresh(AnimationState& aState,
|
|||
MOZ_ASSERT(aState.mCompositedFrameInvalid);
|
||||
// Nothing we can do but wait for our previous current frame to be decoded
|
||||
// again so we can determine what to do next.
|
||||
aState.MaybeAdvanceAnimationFrameTime(aTime);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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 &&
|
||||
aState.MaybeAdvanceAnimationFrameTime(aTime)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -479,6 +507,8 @@ FrameAnimator::RequestRefresh(AnimationState& aState,
|
|||
LookupResult
|
||||
FrameAnimator::GetCompositedFrame(AnimationState& aState)
|
||||
{
|
||||
aState.mCompositedFrameRequested = true;
|
||||
|
||||
LookupResult result =
|
||||
SurfaceCache::Lookup(ImageKey(mImage),
|
||||
RasterSurfaceKey(mSize,
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
, mHasRequestedDecode(false)
|
||||
, mIsCurrentlyDecoded(false)
|
||||
, mCompositedFrameInvalid(false)
|
||||
, mCompositedFrameRequested(false)
|
||||
, mDiscarded(false)
|
||||
{ }
|
||||
|
||||
|
@ -141,6 +142,13 @@ public:
|
|||
*/
|
||||
void SetAnimationFrameTime(const TimeStamp& aTime);
|
||||
|
||||
/**
|
||||
* Set the animation frame time to @aTime if we are configured to stop the
|
||||
* animation when not visible and aTime is later than the current time.
|
||||
* Returns true if the time was updated, else false.
|
||||
*/
|
||||
bool MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime);
|
||||
|
||||
/**
|
||||
* The current frame we're on, from 0 to (numFrames - 1).
|
||||
*/
|
||||
|
@ -238,6 +246,10 @@ private:
|
|||
//! 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;
|
||||
|
|
|
@ -4517,6 +4517,10 @@ pref("image.animated.decode-on-demand.threshold-kb", 4194303);
|
|||
// animation's currently displayed frame.
|
||||
pref("image.animated.decode-on-demand.batch-size", 6);
|
||||
|
||||
// Resume an animated image from the last displayed frame rather than
|
||||
// advancing when out of view.
|
||||
pref("image.animated.resume-from-last-displayed", true);
|
||||
|
||||
// Maximum number of surfaces for an image before entering "factor of 2" mode.
|
||||
// This in addition to the number of "native" sizes of an image. A native size
|
||||
// is a size for which we can decode a frame without up or downscaling. Most
|
||||
|
|
Загрузка…
Ссылка в новой задаче