Bug 790505, part 4: If we're just updating the transform of a prerendered layer, then schedule an empty transaction to skip unnecessary display-list overhead. r=roc

This commit is contained in:
Chris Jones 2012-10-02 22:55:52 -07:00
parent 7180e78526
commit 4f1e40ca75
4 changed files with 87 additions and 3 deletions

View File

@ -7732,30 +7732,47 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame,
nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
nsChangeHint_SyncFrameView |
nsChangeHint_UpdateOpacityLayer)));
// This must be set to true if the rendering change needs to
// invalidate content. If it's false, a composite-only paint
// (empty transaction) will be scheduled.
bool needInvalidatingPaint = false;
// if frame has view, will already be invalidated
if (aChange & nsChangeHint_RepaintFrame) {
if (aFrame->IsFrameOfType(nsIFrame::eSVG) &&
!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
if (aChange & nsChangeHint_UpdateEffects) {
needInvalidatingPaint = true;
// Invalidate and update our area:
nsSVGUtils::InvalidateAndScheduleReflowSVG(aFrame);
} else {
needInvalidatingPaint = true;
// Just invalidate our area:
nsSVGUtils::InvalidateBounds(aFrame);
}
} else {
needInvalidatingPaint = true;
aFrame->InvalidateFrameSubtree();
}
}
if (aChange & nsChangeHint_UpdateOpacityLayer) {
// FIXME/bug 796697: we can get away with empty transactions for
// opacity updates in many cases.
needInvalidatingPaint = true;
aFrame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
}
if (aChange & nsChangeHint_UpdateTransformLayer) {
aFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
// If we're not already going to do an invalidating paint, see
// if we can get away with only updating the transform on a
// layer for this frame, and not scheduling an invalidating
// paint.
if (!needInvalidatingPaint) {
needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly();
}
}
if (aChange & nsChangeHint_ChildrenOnlyTransform) {
needInvalidatingPaint = true;
// The long comment in ProcessRestyledFrames that precedes the
// |frame->GetContent()->GetPrimaryFrame()| and abort applies here too.
nsIFrame *f = aFrame->GetContent()->GetPrimaryFrame();
@ -7767,7 +7784,9 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame,
childFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
}
}
aFrame->SchedulePaint();
aFrame->SchedulePaint(needInvalidatingPaint ?
nsIFrame::PAINT_DEFAULT :
nsIFrame::PAINT_COMPOSITE_ONLY);
}
}

View File

@ -3745,6 +3745,12 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
AddAnimationsAndTransitionsToLayer(container, aBuilder,
this, eCSSProperty_transform);
if (ShouldPrerenderTransformedContent(aBuilder, mFrame, false)) {
container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
/*the value is irrelevant*/nullptr);
} else {
container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
}
return container.forget();
}

View File

@ -98,6 +98,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/css/ImageLoader.h"
#include "mozilla/gfx/Tools.h"
using namespace mozilla;
using namespace mozilla::layers;
@ -4882,7 +4883,48 @@ nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
*rect = rect->Union(aRect);
}
/*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
bool
nsIFrame::TryUpdateTransformOnly()
{
Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
this, nsDisplayItem::TYPE_TRANSFORM);
if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) {
// This layer isn't prerendered, so we can't correctly optimize to
// an empty transaction in general.
return false;
}
gfx3DMatrix transform3d;
if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
// We're not able to compute a layer transform that we know would
// be used at the next layers transaction, so we can't only update
// the transform and will need to schedule an invalidating paint.
return false;
}
gfxMatrix transform, previousTransform;
// FIXME/bug 796690 and 796705: in general, changes to 3D
// transforms, or transform changes to properties other than
// translation, may lead us to choose a different rendering
// resolution for our layer. So if the transform is 3D or has a
// non-translation change, bail and schedule an invalidating paint.
// (We can often do better than this, for example for scale-down
// changes.)
static const gfx::Float kError = 0.0001;
if (!transform3d.Is2D(&transform) ||
!layer->GetTransform().Is2D(&previousTransform) ||
!gfx::FuzzyEqual(transform.xx, previousTransform.xx, kError) ||
!gfx::FuzzyEqual(transform.yy, previousTransform.yy, kError) ||
!gfx::FuzzyEqual(transform.xy, previousTransform.xy, kError) ||
!gfx::FuzzyEqual(transform.yx, previousTransform.yx, kError)) {
return false;
}
layer->SetBaseTransformForNextTransaction(transform3d);
return true;
}
bool
nsIFrame::IsInvalid(nsRect& aRect)
{

View File

@ -2231,6 +2231,23 @@ public:
*/
virtual void InvalidateFrameForRemoval() {}
/**
* When HasUserData(frame->LayerIsPrerenderedDataKey()), then the
* entire overflow area of this frame has been rendered in its
* layer(s).
*/
static void* LayerIsPrerenderedDataKey() {
return &sLayerIsPrerenderedDataKey;
}
static uint8_t sLayerIsPrerenderedDataKey;
/**
* Try to update this frame's transform without invalidating any
* content. Return true iff successful. If unsuccessful, the
* caller is responsible for scheduling an invalidating paint.
*/
bool TryUpdateTransformOnly();
/**
* Checks if a frame has had InvalidateFrame() called on it since the
* last paint.