Bug 946952 - Run a 'compressed' paint up to 1 second after skipping the ColorLayer optimization. r=roc

This commit is contained in:
Matt Woodrow 2014-01-30 18:41:17 +13:00
parent ff3e7f88a3
commit a3449e66b6
11 changed files with 117 additions and 15 deletions

View File

@ -1759,7 +1759,7 @@ ContainerState::PopThebesLayerData()
nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
data->mLayer->GetValidRegion().IsEmpty()) {
(data->mLayer->GetValidRegion().IsEmpty() || mLayerBuilder->CheckInLayerTreeCompressionMode())) {
NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
"Can't be a solid color as well as an image!");
if (imageContainer) {
@ -2817,6 +2817,20 @@ FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer)
}
}
bool
FrameLayerBuilder::CheckInLayerTreeCompressionMode()
{
if (mInLayerTreeCompressionMode) {
return true;
}
// If we wanted to be in layer tree compression mode, but weren't, then scheduled
// a delayed repaint where we will be.
mRootPresContext->PresShell()->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DELAYED_COMPRESS);
return false;
}
void
ContainerState::CollectOldLayers()
{

View File

@ -152,6 +152,7 @@ public:
mRetainingManager(nullptr),
mDetectedDOMModification(false),
mInvalidateAllLayers(false),
mInLayerTreeCompressionMode(false),
mContainerLayerGeneration(0),
mMaxContainerLayerGeneration(0)
{
@ -591,6 +592,13 @@ public:
return mContainingThebesLayer;
}
/**
* Attempt to build the most compressed layer tree possible, even if it means
* throwing away existing retained buffers.
*/
void SetLayerTreeCompressionMode() { mInLayerTreeCompressionMode = true; }
bool CheckInLayerTreeCompressionMode();
protected:
void RemoveThebesItemsAndOwnerDataForLayerSubtree(Layer* aLayer,
bool aRemoveThebesItems,
@ -652,6 +660,8 @@ protected:
*/
bool mInvalidateAllLayers;
bool mInLayerTreeCompressionMode;
uint32_t mContainerLayerGeneration;
uint32_t mMaxContainerLayerGeneration;
};

View File

@ -1124,6 +1124,10 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
FrameLayerBuilder *layerBuilder = new FrameLayerBuilder();
layerBuilder->Init(aBuilder, layerManager);
if (aFlags & PAINT_COMPRESSED) {
layerBuilder->SetLayerTreeCompressionMode();
}
if (aFlags & PAINT_FLUSH_LAYERS) {
FrameLayerBuilder::InvalidateAllLayers(layerManager);
}

View File

@ -1512,6 +1512,9 @@ public:
* layer manager has already had BeginTransaction() called on it and
* we should not call it again.
*
* If PAINT_COMPRESSED is set, the FrameLayerBuilder should be set to compressed mode
* to avoid short cut optimizations.
*
* ComputeVisibility must be called before Paint.
*
* This must only be called on the root display list of the display list
@ -1522,7 +1525,8 @@ public:
PAINT_USE_WIDGET_LAYERS = 0x01,
PAINT_FLUSH_LAYERS = 0x02,
PAINT_EXISTING_TRANSACTION = 0x04,
PAINT_NO_COMPOSITE = 0x08
PAINT_NO_COMPOSITE = 0x08,
PAINT_COMPRESSED = 0x10
};
void PaintRoot(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
uint32_t aFlags) const;

View File

@ -1322,8 +1322,15 @@ public:
/**
* Ensures that the refresh driver is running, and schedules a view
* manager flush on the next tick.
*
* @param aType PAINT_DELAYED_COMPRESS : Schedule a paint to be executed after a delay, and
* put FrameLayerBuilder in 'compressed' mode that avoids short cut optimizations.
*/
virtual void ScheduleViewManagerFlush() = 0;
enum PaintType {
PAINT_DEFAULT,
PAINT_DELAYED_COMPRESS
};
virtual void ScheduleViewManagerFlush(PaintType aType = PAINT_DEFAULT) = 0;
virtual void ClearMouseCaptureOnView(nsView* aView) = 0;
virtual bool IsVisible() = 0;
virtual void DispatchSynthMouseMove(mozilla::WidgetGUIEvent* aEvent,

View File

@ -2335,6 +2335,9 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram
if (aFlags & PAINT_NO_COMPOSITE) {
flags |= nsDisplayList::PAINT_NO_COMPOSITE;
}
if (aFlags & PAINT_COMPRESSED) {
flags |= nsDisplayList::PAINT_COMPRESSED;
}
list.PaintRoot(&builder, aRenderingContext, flags);

View File

@ -768,7 +768,8 @@ public:
PAINT_ALL_CONTINUATIONS = 0x40,
PAINT_TO_WINDOW = 0x80,
PAINT_EXISTING_TRANSACTION = 0x100,
PAINT_NO_COMPOSITE = 0x200
PAINT_NO_COMPOSITE = 0x200,
PAINT_COMPRESSED = 0x400
};
/**
@ -799,6 +800,8 @@ public:
* If PAINT_EXISTING_TRANSACTION is set, then BeginTransaction() has already
* been called on aFrame's widget's layer manager and should not be
* called again.
* If PAINT_COMPRESSED is set, the FrameLayerBuilder should be set to compressed mode
* to avoid short cut optimizations.
*
* So there are three possible behaviours:
* 1) PAINT_WIDGET_LAYERS is set and aRenderingContext is null; we paint

View File

@ -1056,6 +1056,11 @@ PresShell::Destroy()
mReflowContinueTimer = nullptr;
}
if (mDelayedPaintTimer) {
mDelayedPaintTimer->Cancel();
mDelayedPaintTimer = nullptr;
}
mSynthMouseMoveEvent.Revoke();
mUpdateImageVisibilityEvent.Revoke();
@ -3541,9 +3546,41 @@ PresShell::GetRectVisibility(nsIFrame* aFrame,
return nsRectVisibility_kVisible;
}
void
PresShell::ScheduleViewManagerFlush()
class PaintTimerCallBack MOZ_FINAL : public nsITimerCallback
{
public:
PaintTimerCallBack(PresShell* aShell) : mShell(aShell) {}
NS_DECL_ISUPPORTS
NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
{
mShell->SetNextPaintCompressed();
mShell->AddInvalidateHiddenPresShellObserver(mShell->GetPresContext()->RefreshDriver());
mShell->ScheduleViewManagerFlush();
return NS_OK;
}
private:
PresShell* mShell;
};
NS_IMPL_ISUPPORTS1(PaintTimerCallBack, nsITimerCallback)
void
PresShell::ScheduleViewManagerFlush(PaintType aType)
{
if (aType == PAINT_DELAYED_COMPRESS) {
// Delay paint for 1 second.
static const uint32_t kPaintDelayPeriod = 1000;
if (!mDelayedPaintTimer) {
mDelayedPaintTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
nsRefPtr<PaintTimerCallBack> cb = new PaintTimerCallBack(this);
mDelayedPaintTimer->InitWithCallback(cb, kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT);
}
return;
}
nsPresContext* presContext = GetPresContext();
if (presContext) {
presContext->RefreshDriver()->ScheduleViewManagerFlush();
@ -5800,7 +5837,8 @@ PresShell::Paint(nsView* aViewToPaint,
layerManager->BeginTransaction();
}
if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE)) {
if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) &&
!mNextPaintCompressed) {
NotifySubDocInvalidationFunc computeInvalidFunc =
presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
bool computeInvalidRect = computeInvalidFunc ||
@ -5851,6 +5889,9 @@ PresShell::Paint(nsView* aViewToPaint,
if (!(aFlags & PAINT_COMPOSITE)) {
flags |= nsLayoutUtils::PAINT_NO_COMPOSITE;
}
if (mNextPaintCompressed) {
flags |= nsLayoutUtils::PAINT_COMPRESSED;
}
if (frame) {
// We can paint directly into the widget using its layer manager.

View File

@ -201,7 +201,7 @@ public:
virtual void WillPaint() MOZ_OVERRIDE;
virtual void WillPaintWindow() MOZ_OVERRIDE;
virtual void DidPaintWindow() MOZ_OVERRIDE;
virtual void ScheduleViewManagerFlush() MOZ_OVERRIDE;
virtual void ScheduleViewManagerFlush(PaintType aType = PAINT_DEFAULT) MOZ_OVERRIDE;
virtual void DispatchSynthMouseMove(mozilla::WidgetGUIEvent* aEvent,
bool aFlushOnHoverChange) MOZ_OVERRIDE;
virtual void ClearMouseCaptureOnView(nsView* aView) MOZ_OVERRIDE;
@ -351,6 +351,8 @@ public:
virtual void RestyleShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot);
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
protected:
virtual ~PresShell();
@ -762,6 +764,8 @@ protected:
// moving/sizing loop is running, see bug 491700 for details.
nsCOMPtr<nsITimer> mReflowContinueTimer;
nsCOMPtr<nsITimer> mDelayedPaintTimer;
// The `performance.now()` value when we last started to process reflows.
DOMHighResTimeStamp mLastReflowStart;
@ -802,6 +806,8 @@ protected:
bool mImageVisibilityVisited : 1;
bool mNextPaintCompressed : 1;
static bool sDisableNonTestMouseEvents;
};

View File

@ -4886,7 +4886,7 @@ nsIFrame::IsInvalid(nsRect& aRect)
}
void
nsIFrame::SchedulePaint(uint32_t aFlags)
nsIFrame::SchedulePaint(PaintType aType)
{
nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
nsPresContext *pres = displayRoot->PresContext()->GetRootPresContext();
@ -4897,8 +4897,15 @@ nsIFrame::SchedulePaint(uint32_t aFlags)
return;
}
pres->PresShell()->ScheduleViewManagerFlush();
if (!(aFlags & PAINT_COMPOSITE_ONLY)) {
pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ?
nsIPresShell::PAINT_DELAYED_COMPRESS :
nsIPresShell::PAINT_DEFAULT);
if (aType == PAINT_DELAYED_COMPRESS) {
return;
}
if (aType == PAINT_DEFAULT) {
displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
}
nsIPresShell* shell = PresContext()->PresShell();

View File

@ -2328,15 +2328,18 @@ public:
* do not trigger a reflow should have this called for them by
* DoApplyRenderingChangeToTree.
*
* @param aFlags PAINT_COMPOSITE_ONLY : No changes have been made
* @param aType PAINT_COMPOSITE_ONLY : No changes have been made
* that require a layer tree update, so only schedule a layer
* tree composite.
* PAINT_DELAYED_COMPRESS : Schedule a paint to be executed after a delay, and
* put FrameLayerBuilder in 'compressed' mode that avoids short cut optimizations.
*/
enum {
enum PaintType {
PAINT_DEFAULT = 0,
PAINT_COMPOSITE_ONLY = 1 << 0
PAINT_COMPOSITE_ONLY,
PAINT_DELAYED_COMPRESS
};
void SchedulePaint(uint32_t aFlags = PAINT_DEFAULT);
void SchedulePaint(PaintType aType = PAINT_DEFAULT);
/**
* Checks if the layer tree includes a dedicated layer for this