зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
551edfd98f
Коммит
63ca81fcba
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче