Bug 1216030 - Part 16: Move CanThrottleAnimation and CanThrottleTransformChanges from AnimationCollection into KeyframeEffectReadOnly::CanThrottle. r=bbirtles

The preference check has been removed from CanThrottleTransformChanges
because we already perform that check that when deciding if we should run
an animation on the compositor (in CanPerformOnCompositorThread, as called
by GetAnimationsForCompositor). Hence if the "is running on compositor" flag
is true, we can assume the preference is set (or was set when we decided to
put the animation on the compositor-- we don't worry about pulling the
animation off the compositor immediately if the preference changes while
it is running)
This commit is contained in:
Hiroyuki Ikezoe 2015-11-06 02:53:00 +01:00
parent 6e809a002a
commit 325530125c
4 changed files with 101 additions and 102 deletions

View File

@ -9,8 +9,10 @@
#include "mozilla/dom/KeyframeEffectBinding.h"
#include "mozilla/dom/PropertyIndexedKeyframesBinding.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/LookAndFeel.h" // For LookAndFeel::GetInt
#include "mozilla/StyleAnimationValue.h"
#include "AnimationCommon.h"
#include "Layers.h" // For Layer
#include "nsCSSParser.h"
#include "nsCSSPropertySet.h"
#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
@ -1726,14 +1728,110 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
bool
KeyframeEffectReadOnly::CanThrottle() const
{
// Animation::CanThrottle checks for finished and not in effect animations
// before calling this.
MOZ_ASSERT(IsInEffect() && IsCurrent(),
"Effect should be in effect and current");
nsIFrame* frame = GetAnimationFrame();
if (!frame) {
// There are two possible cases here.
// a) No target element
// b) The target element has no frame, e.g. because it is in a display:none
// subtree.
// In either case we can throttle the animation because there is no
// need to update on the main thread.
return true;
}
// First we need to check layer generation and transform overflow
// prior to the IsPropertyRunningOnCompositor check because we should
// occasionally unthrottle these animations even if the animations are
// already running on compositor.
for (const LayerAnimationInfo::Record& record :
LayerAnimationInfo::sRecords) {
// Skip properties that are overridden in the cascade.
// (GetAnimationOfProperty, as called by HasAnimationOfProperty,
// only returns an animation if it currently wins in the cascade.)
if (!HasAnimationOfProperty(record.mProperty)) {
continue;
}
AnimationCollection* collection = GetCollection();
MOZ_ASSERT(collection,
"CanThrottle should be called on an effect associated with an animation");
layers::Layer* layer =
FrameLayerBuilder::GetDedicatedLayer(frame, record.mLayerType);
// Unthrottle if the layer needs to be brought up to date with the animation.
if (!layer ||
collection->mAnimationGeneration > layer->GetAnimationGeneration()) {
return false;
}
// If this is a transform animation that affects the overflow region,
// we should unthrottle the animation periodically.
if (record.mProperty == eCSSProperty_transform &&
!CanThrottleTransformChanges(*frame)) {
return false;
}
}
for (const AnimationProperty& property : mProperties) {
if (!IsPropertyRunningOnCompositor(property.mProperty)) {
return false;
}
}
return true;
}
bool
KeyframeEffectReadOnly::CanThrottleTransformChanges(nsIFrame& aFrame) const
{
// If we know that the animation cannot cause overflow,
// we can just disable flushes for this animation.
// If we don't show scrollbars, we don't care about overflow.
if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0) {
return true;
}
nsPresContext* presContext = GetPresContext();
// CanThrottleTransformChanges is only called as part of a refresh driver tick
// in which case we expect to has a pres context.
MOZ_ASSERT(presContext);
TimeStamp now =
presContext->RefreshDriver()->MostRecentRefresh();
AnimationCollection* collection = GetCollection();
MOZ_ASSERT(collection,
"CanThrottleTransformChanges should be involved with animation collection");
TimeStamp styleRuleRefreshTime = collection->mStyleRuleRefreshTime;
// If this animation can cause overflow, we can throttle some of the ticks.
if (!styleRuleRefreshTime.IsNull() &&
(now - styleRuleRefreshTime) < TimeDuration::FromMilliseconds(200)) {
return true;
}
// If the nearest scrollable ancestor has overflow:hidden,
// we don't care about overflow.
nsIScrollableFrame* scrollable =
nsLayoutUtils::GetNearestScrollableFrame(&aFrame);
if (!scrollable) {
return true;
}
ScrollbarStyles ss = scrollable->GetScrollbarStyles();
if (ss.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
ss.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
scrollable->GetLogicalScrollPosition() == nsPoint(0, 0)) {
return true;
}
return false;
}
nsIFrame*
KeyframeEffectReadOnly::GetAnimationFrame() const
{

View File

@ -328,6 +328,9 @@ protected:
private:
nsIFrame* GetAnimationFrame() const;
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
// Returns true unless Gecko limitations prevent performing transform
// animations for |aFrame|. Any limitations that are encountered are
// logged using |aContent| to describe the affected content.

View File

@ -17,9 +17,7 @@
#include "nsStyleContext.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "mozilla/LookAndFeel.h"
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
#include "Layers.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "mozilla/MemoryReporting.h"
@ -29,7 +27,6 @@
#include "nsStyleSet.h"
#include "nsStyleChangeList.h"
using mozilla::layers::Layer;
using mozilla::dom::Animation;
using mozilla::dom::KeyframeEffectReadOnly;
@ -603,91 +600,6 @@ AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime)
}
}
bool
AnimationCollection::CanThrottleTransformChanges(TimeStamp aTime)
{
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
return false;
}
// If we know that the animation cannot cause overflow,
// we can just disable flushes for this animation.
// If we don't show scrollbars, we don't care about overflow.
if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0) {
return true;
}
// If this animation can cause overflow, we can throttle some of the ticks.
if (!mStyleRuleRefreshTime.IsNull() &&
(aTime - mStyleRuleRefreshTime) < TimeDuration::FromMilliseconds(200)) {
return true;
}
dom::Element* element = GetElementToRestyle();
if (!element) {
return false;
}
// If the nearest scrollable ancestor has overflow:hidden,
// we don't care about overflow.
nsIScrollableFrame* scrollable = nsLayoutUtils::GetNearestScrollableFrame(
nsLayoutUtils::GetStyleFrame(element));
if (!scrollable) {
return true;
}
ScrollbarStyles ss = scrollable->GetScrollbarStyles();
if (ss.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
ss.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
scrollable->GetLogicalScrollPosition() == nsPoint(0, 0)) {
return true;
}
return false;
}
bool
AnimationCollection::CanThrottleAnimation(TimeStamp aTime)
{
dom::Element* element = GetElementToRestyle();
if (!element) {
return false;
}
nsIFrame* frame = nsLayoutUtils::GetStyleFrame(element);
if (!frame) {
return false;
}
for (const LayerAnimationInfo::Record& record :
LayerAnimationInfo::sRecords) {
// We only need to worry about *current* animations here.
// - If we have a newly-finished animation, Animation::CanThrottle will
// detect that and force an unthrottled sample.
// - If we have a newly-idle animation, then whatever caused the animation
// to be idle will update the animation generation so we'll return false
// from the layer generation check below for any other running compositor
// animations (and if no other compositor animations exist we won't get
// this far).
if (!HasCurrentAnimationOfProperty(record.mProperty)) {
continue;
}
Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
frame, record.mLayerType);
if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
return false;
}
if (record.mProperty == eCSSProperty_transform &&
!CanThrottleTransformChanges(aTime)) {
return false;
}
}
return true;
}
void
AnimationCollection::ClearIsRunningOnCompositor(nsCSSProperty aProperty)
{
@ -729,15 +641,6 @@ AnimationCollection::RequestRestyle(RestyleType aRestyleType)
return;
}
// Upgrade throttled restyles if other factors prevent
// throttling (e.g. async animations are not enabled).
if (aRestyleType == RestyleType::Throttled) {
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
if (!CanThrottleAnimation(now)) {
aRestyleType = RestyleType::Standard;
}
}
if (aRestyleType >= RestyleType::Standard) {
mHasPendingAnimationRestyle = true;
PostRestyleForAnimation(presContext);

View File

@ -259,11 +259,6 @@ struct AnimationCollection : public LinkedListElement<AnimationCollection>
void RequestRestyle(RestyleType aRestyleType);
void ClearIsRunningOnCompositor(nsCSSProperty aProperty);
private:
bool CanThrottleAnimation(TimeStamp aTime);
bool CanThrottleTransformChanges(TimeStamp aTime);
public:
// True if this animation can be performed on the compositor thread.
//