Bug 961871 - Part 4: Fix budget calculation by adding Maybe prerenderer and animate. r=roc

This commit is contained in:
Benoit Girard 2014-10-21 21:55:55 -04:00
Родитель 584ac95d49
Коммит 1eda179306
7 изменённых файлов: 103 добавлений и 25 удалений

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

@ -259,6 +259,12 @@ ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
NotifyAnimated(aFrame, aProperty); NotifyAnimated(aFrame, aProperty);
} }
/* static */ bool
ActiveLayerTracker::IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
{
return IsStyleAnimated(nullptr, aFrame, aProperty);
}
/* static */ bool /* static */ bool
ActiveLayerTracker::IsStyleAnimated(nsDisplayListBuilder* aBuilder, ActiveLayerTracker::IsStyleAnimated(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsCSSProperty aProperty) nsIFrame* aFrame, nsCSSProperty aProperty)
@ -266,12 +272,12 @@ ActiveLayerTracker::IsStyleAnimated(nsDisplayListBuilder* aBuilder,
// TODO: Add some abuse restrictions // TODO: Add some abuse restrictions
if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) && if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
aProperty == eCSSProperty_transform && aProperty == eCSSProperty_transform &&
aBuilder->IsInWillChangeBudget(aFrame)) { (!aBuilder || aBuilder->IsInWillChangeBudget(aFrame))) {
return true; return true;
} }
if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
aProperty == eCSSProperty_opacity && aProperty == eCSSProperty_opacity &&
aBuilder->IsInWillChangeBudget(aFrame)) { (!aBuilder || aBuilder->IsInWillChangeBudget(aFrame))) {
return true; return true;
} }

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

@ -56,6 +56,11 @@ public:
* style will trigger this. * style will trigger this.
*/ */
static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty); static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty);
/**
* Return true if aFrame's aProperty style should be considered as being animated
* for pre-rendering.
*/
static bool IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSProperty aProperty);
/** /**
* Return true if aFrame's aProperty style should be considered as being animated * Return true if aFrame's aProperty style should be considered as being animated
* for constructing active layers. * for constructing active layers.

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

@ -2804,7 +2804,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap); nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
nsDisplayItem::Type itemType = item->GetType(); nsDisplayItem::Type itemType = item->GetType();
bool prerenderedTransform = itemType == nsDisplayItem::TYPE_TRANSFORM && bool prerenderedTransform = itemType == nsDisplayItem::TYPE_TRANSFORM &&
static_cast<nsDisplayTransform*>(item)->ShouldPrerender(); static_cast<nsDisplayTransform*>(item)->ShouldPrerender(mBuilder);
nsIntRect clipRect; nsIntRect clipRect;
const DisplayItemClip& itemClip = item->GetClip(); const DisplayItemClip& itemClip = item->GetClip();
if (itemClip.HasClip()) { if (itemClip.HasClip()) {

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

@ -525,6 +525,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mCurrentTableItem(nullptr), mCurrentTableItem(nullptr),
mCurrentFrame(aReferenceFrame), mCurrentFrame(aReferenceFrame),
mCurrentReferenceFrame(aReferenceFrame), mCurrentReferenceFrame(aReferenceFrame),
mWillChangeBudgetCalculated(false),
mDirtyRect(-1,-1,-1,-1), mDirtyRect(-1,-1,-1,-1),
mGlassDisplayItem(nullptr), mGlassDisplayItem(nullptr),
mMode(aMode), mMode(aMode),
@ -1111,6 +1112,11 @@ nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame)
void void
nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aRect) { nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aRect) {
// Make sure that we don't query the budget before the display list is fully
// built and that the will change budget is locked in.
MOZ_ASSERT(!mWillChangeBudgetCalculated,
"Can't modify the budget once it's been used.");
DocumentWillChangeBudget budget; DocumentWillChangeBudget budget;
nsPresContext* key = aFrame->PresContext(); nsPresContext* key = aFrame->PresContext();
@ -1133,6 +1139,8 @@ nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aRec
bool bool
nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame) const { nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame) const {
mWillChangeBudgetCalculated = true;
nsPresContext* key = aFrame->PresContext(); nsPresContext* key = aFrame->PresContext();
if (!mWillChangeBudget.Contains(key)) { if (!mWillChangeBudget.Contains(key)) {
MOZ_ASSERT(false, "If we added nothing to our budget then this " MOZ_ASSERT(false, "If we added nothing to our budget then this "
@ -4544,8 +4552,15 @@ nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder)
{ {
mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
mStoredList.SetVisibleRect(mChildrenVisibleRect); mStoredList.SetVisibleRect(mChildrenVisibleRect);
mPrerender = ShouldPrerenderTransformedContent(aBuilder, mFrame); mMaybePrerender = ShouldPrerenderTransformedContent(aBuilder, mFrame);
if (mPrerender) {
const nsStyleDisplay* disp = mFrame->StyleDisplay();
if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) {
// We will only pre-render if this will-change is on budget.
mMaybePrerender = true;
}
if (mMaybePrerender) {
bool snap; bool snap;
mVisibleRect = GetBounds(aBuilder, &snap); mVisibleRect = GetBounds(aBuilder, &snap);
} }
@ -4905,10 +4920,31 @@ nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
return false; return false;
} }
bool
nsDisplayTransform::ShouldPrerender(nsDisplayListBuilder* aBuilder) {
if (!mMaybePrerender) {
return false;
}
if (ShouldPrerenderTransformedContent(aBuilder, mFrame)) {
return true;
}
const nsStyleDisplay* disp = mFrame->StyleDisplay();
if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
aBuilder->IsInWillChangeBudget(mFrame)) {
return true;
}
return false;
}
bool bool
nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
{ {
if (mPrerender) { if (mMaybePrerender) {
// TODO We need to make sure that if we use async animation we actually
// pre-render even if we're out of will change budget.
return true; return true;
} }
DebugOnly<bool> prerender = ShouldPrerenderTransformedContent(aBuilder, mFrame, true); DebugOnly<bool> prerender = ShouldPrerenderTransformedContent(aBuilder, mFrame, true);
@ -4925,7 +4961,7 @@ nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBui
// have a compositor-animated transform, can be prerendered. An element // have a compositor-animated transform, can be prerendered. An element
// might have only just had its transform animated in which case // might have only just had its transform animated in which case
// the ActiveLayerManager may not have been notified yet. // the ActiveLayerManager may not have been notified yet.
if (!ActiveLayerTracker::IsStyleAnimated(aBuilder, aFrame, eCSSProperty_transform) && if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame, eCSSProperty_transform) &&
(!aFrame->GetContent() || (!aFrame->GetContent() ||
!nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(), !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
eCSSProperty_transform))) { eCSSProperty_transform))) {
@ -5023,7 +5059,7 @@ nsDisplayTransform::GetTransform()
bool bool
nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
{ {
return ShouldPrerender(); return ShouldPrerender(aBuilder);
} }
already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder, already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
@ -5037,7 +5073,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
return nullptr; return nullptr;
} }
uint32_t flags = ShouldPrerender() ? uint32_t flags = ShouldPrerender(aBuilder) ?
FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0; FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()-> nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(), BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
@ -5058,7 +5094,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
this, mFrame, this, mFrame,
eCSSProperty_transform); eCSSProperty_transform);
if (ShouldPrerender()) { if (ShouldPrerender(aBuilder)) {
container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(), container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
/*the value is irrelevant*/nullptr); /*the value is irrelevant*/nullptr);
container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM); container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM);
@ -5111,7 +5147,7 @@ bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
* think that it's painting in its original rectangular coordinate space. * think that it's painting in its original rectangular coordinate space.
* If we can't untransform, take the entire overflow rect */ * If we can't untransform, take the entire overflow rect */
nsRect untransformedVisibleRect; nsRect untransformedVisibleRect;
if (ShouldPrerender() || if (ShouldPrerender(aBuilder) ||
!UntransformVisibleRect(aBuilder, &untransformedVisibleRect)) !UntransformVisibleRect(aBuilder, &untransformedVisibleRect))
{ {
untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf(); untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
@ -5241,7 +5277,7 @@ nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsP
*/ */
nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap) nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap)
{ {
nsRect untransformedBounds = ShouldPrerender() ? nsRect untransformedBounds = MaybePrerender() ?
mFrame->GetVisualOverflowRectRelativeToSelf() : mFrame->GetVisualOverflowRectRelativeToSelf() :
mStoredList.GetBounds(aBuilder, aSnap); mStoredList.GetBounds(aBuilder, aSnap);
*aSnap = false; *aSnap = false;
@ -5279,7 +5315,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
// covers the entire window, but it allows our transform to be // covers the entire window, but it allows our transform to be
// updated extremely cheaply, without invalidating any other // updated extremely cheaply, without invalidating any other
// content. // content.
if (ShouldPrerender() || if (MaybePrerender() ||
!UntransformVisibleRect(aBuilder, &untransformedVisible)) { !UntransformVisibleRect(aBuilder, &untransformedVisible)) {
return nsRegion(); return nsRegion();
} }

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

@ -14,6 +14,7 @@
#define NSDISPLAYLIST_H_ #define NSDISPLAYLIST_H_
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsContainerFrame.h" #include "nsContainerFrame.h"
#include "nsPoint.h" #include "nsPoint.h"
@ -778,6 +779,8 @@ private:
// will-change budget tracker // will-change budget tracker
nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget> nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
mWillChangeBudget; mWillChangeBudget;
// Assert that we never check the budget before its fully calculated.
mutable mozilla::DebugOnly<bool> mWillChangeBudgetCalculated;
// Relative to mCurrentFrame. // Relative to mCurrentFrame.
nsRect mDirtyRect; nsRect mDirtyRect;
nsRegion mWindowOpaqueRegion; nsRegion mWindowOpaqueRegion;
@ -3464,7 +3467,16 @@ public:
bool aLogAnimations = false); bool aLogAnimations = false);
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE; bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
bool ShouldPrerender() const { return mPrerender; } /**
* This will return if it's possible for this element to be prerendered.
* This should never return false if we're going to prerender.
*/
bool MaybePrerender() const { return mMaybePrerender; }
/**
* Check if this element will be prerendered. This must be done after the
* display list has been fully built.
*/
bool ShouldPrerender(nsDisplayListBuilder* aBuilder);
#ifdef MOZ_DUMP_PAINTING #ifdef MOZ_DUMP_PAINTING
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE; virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
@ -3486,7 +3498,9 @@ private:
ComputeTransformFunction mTransformGetter; ComputeTransformFunction mTransformGetter;
nsRect mChildrenVisibleRect; nsRect mChildrenVisibleRect;
uint32_t mIndex; uint32_t mIndex;
bool mPrerender; // We wont know if we pre-render until the layer building phase where we can
// check layers will-change budget.
bool mMaybePrerender;
}; };
/** /**

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

@ -2546,7 +2546,10 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
return; return;
} }
mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsScrollingActive(aBuilder) // We can't check will-change budget during display list building phase.
// This means that we will build scroll bar layers for out of budget
// will-change: scroll position.
mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsMaybeScrollingActive()
? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent()) ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
: mozilla::layers::FrameMetrics::NULL_SCROLL_ID; : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
@ -2765,10 +2768,10 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (aBuilder->IsPaintingToWindow()) { if (aBuilder->IsPaintingToWindow()) {
mScrollPosAtLastPaint = GetScrollPosition(); mScrollPosAtLastPaint = GetScrollPosition();
if (IsScrollingActive(aBuilder) && NeedToInvalidateOnScroll(mOuter)) { if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
MarkNotRecentlyScrolled(); MarkNotRecentlyScrolled();
} }
if (IsScrollingActive(aBuilder)) { if (IsMaybeScrollingActive()) {
if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) { if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint; mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
} }
@ -2875,6 +2878,11 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
dirtyRect = ExpandRectToNearlyVisible(dirtyRect); dirtyRect = ExpandRectToNearlyVisible(dirtyRect);
} }
const nsStyleDisplay* disp = mOuter->StyleDisplay();
if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
aBuilder->AddToWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize());
}
// Since making new layers is expensive, only use nsDisplayScrollLayer // Since making new layers is expensive, only use nsDisplayScrollLayer
// if the area is scrollable and we're the content process (unless we're on // if the area is scrollable and we're the content process (unless we're on
// B2G, where we support async scrolling for scrollable elements in the // B2G, where we support async scrolling for scrollable elements in the
@ -2896,11 +2904,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// info layer to make a ComputeFrameMetrics call for us as // info layer to make a ComputeFrameMetrics call for us as
// nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us. // nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
(!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()); (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
const nsStyleDisplay* disp = mOuter->StyleDisplay();
bool willScroll = disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL) &&
aBuilder->IsInWillChangeBudget(mOuter);
shouldBuildLayer |= willScroll;
} }
mScrollParentID = aBuilder->GetCurrentScrollParentId(); mScrollParentID = aBuilder->GetCurrentScrollParentId();
@ -4125,6 +4128,19 @@ ScrollFrameHelper::IsScrollbarOnRight() const
} }
} }
bool
ScrollFrameHelper::IsMaybeScrollingActive() const
{
const nsStyleDisplay* disp = mOuter->StyleDisplay();
if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
return true;
}
return mHasBeenScrolledRecently ||
IsAlwaysActive() ||
mShouldBuildScrollableLayer;
}
bool bool
ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const
{ {

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

@ -285,6 +285,7 @@ public:
bool IsLTR() const; bool IsLTR() const;
bool IsScrollbarOnRight() const; bool IsScrollbarOnRight() const;
bool IsScrollingActive(nsDisplayListBuilder* aBuilder) const; bool IsScrollingActive(nsDisplayListBuilder* aBuilder) const;
bool IsMaybeScrollingActive() const;
bool IsProcessingAsyncScroll() const { bool IsProcessingAsyncScroll() const {
return mAsyncScroll != nullptr || mAsyncSmoothMSDScroll != nullptr; return mAsyncScroll != nullptr || mAsyncSmoothMSDScroll != nullptr;
} }