Bug 584494 - Regression: Transform rotation testcase performs significantly worse - r=dbaron

This commit is contained in:
Robert O'Callahan 2010-09-02 14:07:37 -04:00
parent b5daa38e79
commit 7db127ab8d
9 changed files with 116 additions and 65 deletions

View File

@ -494,14 +494,8 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat
}
if (oldRect.TopLeft() != rect.TopLeft() ||
(aDelegatingFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
(kidDesiredSize.mOverflowArea + rect.TopLeft() != oldOverflowRect &&
(kidDesiredSize.mOverflowArea + rect.TopLeft() != rect || oldRect != oldOverflowRect))) {
// The frame moved; we have to invalidate the whole frame
// because the children may have moved after they were reflowed
// We also have to invalidate when we have overflow and the overflow
// changes because the change might be caused by clipping
// XXX This could be optimized in some cases, especially clipping changes
(aDelegatingFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
// The frame moved
aKidFrame->GetParent()->Invalidate(oldOverflowRect);
aKidFrame->GetParent()->Invalidate(kidDesiredSize.mOverflowArea +
rect.TopLeft());

View File

@ -520,17 +520,21 @@ nsBlockFrame::InvalidateInternal(const nsRect& aDamageRect,
PRUint32 aFlags)
{
// Optimize by suppressing invalidation of areas that are clipped out
// with CSS 'clip'.
const nsStyleDisplay* disp = GetStyleDisplay();
nsRect absPosClipRect;
if (GetAbsPosClipRect(disp, &absPosClipRect, GetSize())) {
// Restrict the invalidated area to abs-pos clip rect
// abs-pos clipping clips everything in the frame
nsRect r;
if (r.IntersectRect(aDamageRect, absPosClipRect - nsPoint(aX, aY))) {
nsBlockFrameSuper::InvalidateInternal(r, aX, aY, this, aFlags);
// with CSS 'clip'. Don't suppress invalidation of *this* frame directly,
// because when 'clip' shrinks we need to invalidate this frame and
// be able to invalidate areas outside the 'clip'.
if (aForChild) {
const nsStyleDisplay* disp = GetStyleDisplay();
nsRect absPosClipRect;
if (GetAbsPosClipRect(disp, &absPosClipRect, GetSize())) {
// Restrict the invalidated area to abs-pos clip rect
// abs-pos clipping clips everything in the frame
nsRect r;
if (r.IntersectRect(aDamageRect, absPosClipRect - nsPoint(aX, aY))) {
nsBlockFrameSuper::InvalidateInternal(r, aX, aY, this, aFlags);
}
return;
}
return;
}
nsBlockFrameSuper::InvalidateInternal(aDamageRect, aX, aY, this, aFlags);

View File

@ -5866,26 +5866,6 @@ IsInlineFrame(nsIFrame *aFrame)
type == nsGkAtoms::positionedInlineFrame;
}
nsRect
nsIFrame::GetAdditionalOverflow(const nsRect& aOverflowArea,
const nsSize& aNewSize,
PRBool* aHasOutlineOrEffects)
{
nsRect overflowRect =
ComputeOutlineAndEffectsRect(this, aHasOutlineOrEffects,
aOverflowArea, aNewSize, PR_TRUE);
// Absolute position clipping
PRBool hasAbsPosClip;
nsRect absPosClipRect;
hasAbsPosClip = GetAbsPosClipRect(GetStyleDisplay(), &absPosClipRect, aNewSize);
if (hasAbsPosClip) {
overflowRect.IntersectRect(overflowRect, absPosClipRect);
}
return overflowRect;
}
void
nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
{
@ -5931,8 +5911,20 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
}
PRBool hasOutlineOrEffects;
*aOverflowArea = GetAdditionalOverflow(*aOverflowArea, aNewSize,
&hasOutlineOrEffects);
*aOverflowArea =
ComputeOutlineAndEffectsRect(this, &hasOutlineOrEffects,
*aOverflowArea, aNewSize, PR_TRUE);
// Absolute position clipping
PRBool didHaveAbsPosClip = (GetStateBits() & NS_FRAME_HAS_CLIP) != 0;
nsRect absPosClipRect;
PRBool hasAbsPosClip = GetAbsPosClipRect(disp, &absPosClipRect, aNewSize);
if (hasAbsPosClip) {
aOverflowArea->IntersectRect(*aOverflowArea, absPosClipRect);
AddStateBits(NS_FRAME_HAS_CLIP);
} else {
RemoveStateBits(NS_FRAME_HAS_CLIP);
}
/* If we're transformed, transform the overflow rect by the current transformation. */
PRBool hasTransform = IsTransformed();
@ -5962,19 +5954,46 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
}
}
if (overflowChanged && (hasOutlineOrEffects || hasTransform)) {
// When there's an outline or box-shadow or SVG effects or transform,
// changes to those styles might require repainting of the old and new
// overflow areas. Repainting of the old overflow area is handled in
// nsCSSFrameConstructor::DoApplyRenderingChangeToTree in response
// to nsChangeHint_RepaintFrame. Since the new overflow area is not
// known at that time, we have to handle it here.
// If the overflow area hasn't changed, then we don't have to do
// anything here since repainting the old overflow area was enough.
// If there is no outline or other effects now, then we don't have
// to do anything here since removing those styles can't require
// repainting of areas that weren't in the old overflow area.
Invalidate(*aOverflowArea);
if (overflowChanged) {
if (hasOutlineOrEffects) {
// When there's an outline or box-shadow or SVG effects,
// changes to those styles might require repainting of the old and new
// overflow areas. Repainting of the old overflow area is handled in
// nsCSSFrameConstructor::DoApplyRenderingChangeToTree in response
// to nsChangeHint_RepaintFrame. Since the new overflow area is not
// known at that time, we have to handle it here.
// If the overflow area hasn't changed, then we don't have to do
// anything here since repainting the old overflow area was enough.
// If there is no outline or other effects now, then we don't have
// to do anything here since removing those styles can't require
// repainting of areas that weren't in the old overflow area.
Invalidate(*aOverflowArea);
} else if (hasAbsPosClip || didHaveAbsPosClip) {
// If we are (or were) clipped by the 'clip' property, and our
// overflow area changes, it might be because the clipping changed.
// The nsChangeHint_RepaintFrame for the style change will only
// repaint the old overflow area, so if the overflow area has
// changed (in particular, if it grows), we have to repaint the
// new area here.
Invalidate(*aOverflowArea);
} else if (hasTransform) {
// When there's a transform, changes to that style might require
// repainting of the old and new overflow areas in the widget.
// Repainting of the frame itself will not be required if there's
// a retained layer, so we can call InvalidateLayer here
// which will avoid repainting ThebesLayers if possible.
// nsCSSFrameConstructor::DoApplyRenderingChangeToTree repaints
// the old overflow area in the widget in response to
// nsChangeHint_UpdateTransformLayer. But since the new overflow
// area is not known at that time, we have to handle it here.
// If the overflow area hasn't changed, then it doesn't matter that
// we didn't reach here since repainting the old overflow area was enough.
// If there is no transform now, then the container layer for
// the transform will go away and the frame contents will change
// ThebesLayers, forcing it to be invalidated, so it doesn't matter
// that we didn't reach here.
InvalidateLayer(*aOverflowArea, nsDisplayItem::TYPE_TRANSFORM);
}
}
}

View File

@ -266,6 +266,9 @@ typedef PRUint64 nsFrameState;
// NS_FRAME_HAS_CONTAINER_LAYER bit.
#define NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT NS_FRAME_STATE_BIT(34)
// Frame's overflow area was clipped by the 'clip' property.
#define NS_FRAME_HAS_CLIP NS_FRAME_STATE_BIT(35)
// The lower 20 bits and upper 32 bits of the frame state are reserved
// by this API.
#define NS_FRAME_RESERVED ~NS_FRAME_IMPL_RESERVED
@ -2519,13 +2522,6 @@ protected:
*/
void InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags);
/**
* Gets the overflow area for any properties that are common to all types of frames
* e.g. outlines.
*/
nsRect GetAdditionalOverflow(const nsRect& aOverflowArea, const nsSize& aNewSize,
PRBool* aHasOutlineOrEffects);
/**
* Can we stop inside this frame when we're skipping non-rendered whitespace?
* @param aForward [in] Are we moving forward (or backward) in content order.

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body>
<div style="position:absolute; left:10px; top:10px; width:100px; height:100px; background:lime">
</div>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body>
<div id="d" style="position:absolute; left:10px; top:10px; width:10px; height:10px; clip:rect(auto,100px,50px,auto)">
<div style="width:100px; height:100px; background:lime;"></div>
</div>
<script>
var d = document.getElementById("d");
function doTest() {
d.style.clip = "";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body>
<div id="d" style="position:absolute; left:10px; top:10px; width:10px; height:10px;">
<div style="width:100px; height:100px; background:lime;"></div>
</div>
<script>
var d = document.getElementById("d");
function doTest() {
d.style.clip = "rect(0,0,0,0)";
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -725,6 +725,8 @@ random == 387876-2.html 387876-2-ref.html # bug 471630
== 388367-1.html about:blank
== 388980-1.html 388980-1-ref.html
== 389074-1.html 389074-1-ref.html
== 389224-1.html 389224-1-ref.html
== 389224-2.html about:blank
== 389468-1.html 389468-1-ref.html
== 389623-1.html 389623-1-ref.html
== 389636-1.html about:blank # assertion test

View File

@ -1983,16 +1983,13 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
nsChangeHint_NeedDirtyReflow)));
}
if (mClipFlags != aOther.mClipFlags || mClip != aOther.mClip) {
// FIXME: Bug 507764. Could we use a more precise hint here?
NS_UpdateHint(hint, nsChangeHint_ReflowFrame);
}
// XXX the following is conservative, for now: changing float breaking shouldn't
// necessarily require a repaint, reflow should suffice.
if (mBreakType != aOther.mBreakType
|| mBreakBefore != aOther.mBreakBefore
|| mBreakAfter != aOther.mBreakAfter
|| mAppearance != aOther.mAppearance)
|| mAppearance != aOther.mAppearance
|| mClipFlags != aOther.mClipFlags || mClip != aOther.mClip)
NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame));
if (mOpacity != aOther.mOpacity) {