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

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

@ -56,6 +56,11 @@ public:
* style will trigger this.
*/
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
* for constructing active layers.

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

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

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

@ -525,6 +525,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mCurrentTableItem(nullptr),
mCurrentFrame(aReferenceFrame),
mCurrentReferenceFrame(aReferenceFrame),
mWillChangeBudgetCalculated(false),
mDirtyRect(-1,-1,-1,-1),
mGlassDisplayItem(nullptr),
mMode(aMode),
@ -1111,6 +1112,11 @@ nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame)
void
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;
nsPresContext* key = aFrame->PresContext();
@ -1133,6 +1139,8 @@ nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, const nsSize& aRec
bool
nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame) const {
mWillChangeBudgetCalculated = true;
nsPresContext* key = aFrame->PresContext();
if (!mWillChangeBudget.Contains(key)) {
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.SetVisibleRect(mChildrenVisibleRect);
mPrerender = ShouldPrerenderTransformedContent(aBuilder, mFrame);
if (mPrerender) {
mMaybePrerender = ShouldPrerenderTransformedContent(aBuilder, mFrame);
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;
mVisibleRect = GetBounds(aBuilder, &snap);
}
@ -4905,10 +4920,31 @@ nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
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
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;
}
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
// might have only just had its transform animated in which case
// the ActiveLayerManager may not have been notified yet.
if (!ActiveLayerTracker::IsStyleAnimated(aBuilder, aFrame, eCSSProperty_transform) &&
if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame, eCSSProperty_transform) &&
(!aFrame->GetContent() ||
!nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
eCSSProperty_transform))) {
@ -5023,7 +5059,7 @@ nsDisplayTransform::GetTransform()
bool
nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
{
return ShouldPrerender();
return ShouldPrerender(aBuilder);
}
already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
@ -5037,7 +5073,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
return nullptr;
}
uint32_t flags = ShouldPrerender() ?
uint32_t flags = ShouldPrerender(aBuilder) ?
FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
@ -5058,7 +5094,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
this, mFrame,
eCSSProperty_transform);
if (ShouldPrerender()) {
if (ShouldPrerender(aBuilder)) {
container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
/*the value is irrelevant*/nullptr);
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.
* If we can't untransform, take the entire overflow rect */
nsRect untransformedVisibleRect;
if (ShouldPrerender() ||
if (ShouldPrerender(aBuilder) ||
!UntransformVisibleRect(aBuilder, &untransformedVisibleRect))
{
untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf();
@ -5241,7 +5277,7 @@ nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsP
*/
nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap)
{
nsRect untransformedBounds = ShouldPrerender() ?
nsRect untransformedBounds = MaybePrerender() ?
mFrame->GetVisualOverflowRectRelativeToSelf() :
mStoredList.GetBounds(aBuilder, aSnap);
*aSnap = false;
@ -5279,7 +5315,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder,
// covers the entire window, but it allows our transform to be
// updated extremely cheaply, without invalidating any other
// content.
if (ShouldPrerender() ||
if (MaybePrerender() ||
!UntransformVisibleRect(aBuilder, &untransformedVisible)) {
return nsRegion();
}

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

@ -14,6 +14,7 @@
#define NSDISPLAYLIST_H_
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "nsCOMPtr.h"
#include "nsContainerFrame.h"
#include "nsPoint.h"
@ -778,6 +779,8 @@ private:
// will-change budget tracker
nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
mWillChangeBudget;
// Assert that we never check the budget before its fully calculated.
mutable mozilla::DebugOnly<bool> mWillChangeBudgetCalculated;
// Relative to mCurrentFrame.
nsRect mDirtyRect;
nsRegion mWindowOpaqueRegion;
@ -3464,7 +3467,16 @@ public:
bool aLogAnimations = false);
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
virtual void WriteDebugInfo(nsACString& aTo) MOZ_OVERRIDE;
@ -3486,7 +3498,9 @@ private:
ComputeTransformFunction mTransformGetter;
nsRect mChildrenVisibleRect;
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;
}
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())
: mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
@ -2765,10 +2768,10 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (aBuilder->IsPaintingToWindow()) {
mScrollPosAtLastPaint = GetScrollPosition();
if (IsScrollingActive(aBuilder) && NeedToInvalidateOnScroll(mOuter)) {
if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
MarkNotRecentlyScrolled();
}
if (IsScrollingActive(aBuilder)) {
if (IsMaybeScrollingActive()) {
if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
}
@ -2875,6 +2878,11 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
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
// 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
@ -2896,11 +2904,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// info layer to make a ComputeFrameMetrics call for us as
// nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
(!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();
@ -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
ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const
{

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

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