Bug 1427033 - Throttle animations in opacity: 0 subtrees. r=hiro

I hope you can help me figure out writing tests for this and figuring whether
the reasoning here is correct / I'm missing something else :)

Differential Revision: https://phabricator.services.mozilla.com/D64442

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2020-02-27 01:36:14 +00:00
Родитель 551edfd98f
Коммит 63ca81fcba
8 изменённых файлов: 78 добавлений и 11 удалений

Просмотреть файл

@ -35,13 +35,14 @@
#include "nsCSSPseudoElements.h" // For PseudoStyleType
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "nsPresContextInlines.h"
#include "nsRefreshDriver.h"
namespace mozilla {
void AnimationProperty::SetPerformanceWarning(
const AnimationPerformanceWarning& aWarning, const Element* aElement) {
const AnimationPerformanceWarning& aWarning, const dom::Element* aElement) {
if (mPerformanceWarning && *mPerformanceWarning == aWarning) {
return;
}
@ -1252,6 +1253,46 @@ KeyframeEffect::OverflowRegionRefreshInterval() {
return kOverflowRegionRefreshInterval;
}
static bool IsDefinitivelyInvisibleDueToOpacity(const nsIFrame& aFrame) {
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
return false;
}
// Find the root of the opacity: 0 subtree.
const nsIFrame* root = &aFrame;
while (true) {
auto* parent = root->GetInFlowParent();
if (!parent || !parent->Style()->IsInOpacityZeroSubtree()) {
break;
}
root = parent;
}
MOZ_ASSERT(root && root->Style()->IsInOpacityZeroSubtree());
// If aFrame is the root of the opacity: zero subtree, we can't prove we can
// optimize it away, because it may have an opacity animation itself.
if (root == &aFrame) {
return false;
}
// Even if we're in an opacity: zero subtree, if the root of the subtree may
// have an opacity animation, we can't optimize us away, as we may become
// visible ourselves.
return !root->HasAnimationOfOpacity();
}
static bool CanOptimizeAwayDueToOpacity(const KeyframeEffect& aEffect,
const nsIFrame& aFrame) {
if (!aFrame.Style()->IsInOpacityZeroSubtree()) {
return false;
}
if (IsDefinitivelyInvisibleDueToOpacity(aFrame)) {
return true;
}
return !aEffect.HasOpacityChange();
}
bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
// Unless we are newly in-effect, we can throttle the animation if the
// animation is paint only and the target frame is out of view or the document
@ -1267,8 +1308,13 @@ bool KeyframeEffect::CanThrottleIfNotVisible(nsIFrame& aFrame) const {
const bool isVisibilityHidden =
!aFrame.IsVisibleOrMayHaveVisibleDescendants();
if ((!isVisibilityHidden || HasVisibilityChange()) &&
!aFrame.IsScrolledOutOfView()) {
const bool canOptimizeAwayVisibility =
isVisibilityHidden && !HasVisibilityChange();
const bool invisible = canOptimizeAwayVisibility ||
CanOptimizeAwayDueToOpacity(*this, aFrame) ||
aFrame.IsScrolledOutOfView();
if (!invisible) {
return false;
}
@ -1890,6 +1936,7 @@ KeyframeEffect::MatchForCompositor KeyframeEffect::IsMatchForCompositor(
// If we know that the animation is not visible, we don't need to send the
// animation to the compositor.
if (!aFrame->IsVisibleOrMayHaveVisibleDescendants() ||
IsDefinitivelyInvisibleDueToOpacity(*aFrame) ||
aFrame->IsScrolledOutOfView()) {
return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
}

Просмотреть файл

@ -347,6 +347,10 @@ class KeyframeEffect : public AnimationEffect {
const Nullable<double>& aProgressOnLastCompose,
uint64_t aCurrentIterationOnLastCompose);
bool HasOpacityChange() const {
return mCumulativeChangeHint & nsChangeHint_UpdateOpacityLayer;
}
protected:
~KeyframeEffect() override = default;

Просмотреть файл

@ -63,4 +63,6 @@ LOCAL_INCLUDES += [
'/layout/style',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

Просмотреть файл

@ -1116,7 +1116,7 @@ waitForAllPaints(() => {
// running on the main thread.
div.style.setProperty('z-index', '0', 'important');
const markers = await observeStyling(5);
todo_is(markers.length, 0,
is(markers.length, 0,
'Changing cascading result for the property running on the main ' +
'thread does not cause synchronization layer of opacity animation ' +
'running on the compositor');

Просмотреть файл

@ -95,6 +95,7 @@ with Files('nsVideoFrame.*'):
BUG_COMPONENT = ('Core', 'Audio/Video')
EXPORTS += [
'JustificationUtils.h',
'nsAtomicContainerFrame.h',
'nsCanvasFrame.h',
'nsContainerFrame.h',
@ -115,6 +116,7 @@ EXPORTS += [
'nsIStatefulFrame.h',
'nsLineBox.h',
'nsPageSequenceFrame.h',
'nsPlaceholderFrame.h',
'nsPluginFrame.h',
'nsQueryFrame.h',
'nsRubyBaseContainerFrame.h',
@ -124,6 +126,7 @@ EXPORTS += [
'nsRubyTextFrame.h',
'nsSplittableFrame.h',
'nsSubDocumentFrame.h',
'nsTextFrame.h',
'nsTextFrameUtils.h',
'nsTextRunTransformations.h',
'RubyUtils.h',

Просмотреть файл

@ -1748,6 +1748,14 @@ bool nsIFrame::ChildrenHavePerspective(
return aStyleDisplay->HasPerspective(this);
}
bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
->IsPrimaryFrame()) &&
nsLayoutUtils::HasAnimationOfPropertySet(
this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
}
bool nsIFrame::HasOpacityInternal(float aThreshold,
const nsStyleDisplay* aStyleDisplay,
const nsStyleEffects* aStyleEffects,
@ -1762,11 +1770,7 @@ bool nsIFrame::HasOpacityInternal(float aThreshold,
return false;
}
return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
->IsPrimaryFrame()) &&
nsLayoutUtils::HasAnimationOfPropertySet(
this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
return HasAnimationOfOpacity(aEffectSet);
}
bool nsIFrame::IsSVGTransformed(gfx::Matrix* aOwnTransforms,

Просмотреть файл

@ -1768,10 +1768,16 @@ class nsIFrame : public nsQueryFrame {
/**
* True if this frame has any animation of transform in effect.
*
*/
bool HasAnimationOfTransform() const;
/**
* True if this frame has any animation of opacity in effect.
*
* EffectSet is just an optimization.
*/
bool HasAnimationOfOpacity(mozilla::EffectSet* = nullptr) const;
/**
* Returns true if the frame is translucent or the frame has opacity
* animations for the purposes of creating a stacking context.

Просмотреть файл

@ -3303,6 +3303,8 @@ nsChangeHint nsStyleEffects::CalcDifference(
}
if (mOpacity != aNewData.mOpacity) {
hint |= nsChangeHint_UpdateOpacityLayer;
// If we're going from the optimized >=0.99 opacity value to 1.0 or back,
// then repaint the frame because DLBI will not catch the invalidation.
// Otherwise, just update the opacity layer.
@ -3311,7 +3313,6 @@ nsChangeHint nsStyleEffects::CalcDifference(
mOpacity == 1.0f)) {
hint |= nsChangeHint_RepaintFrame;
} else {
hint |= nsChangeHint_UpdateOpacityLayer;
if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) {
hint |= nsChangeHint_UpdateUsesOpacity;
}