Bug 639689. Part 4: Optimize Redraw by creating custom RedrawUser, which avoids having to do user->device rectangle transformation if we've already invalidated the full canvas. Also, if the last batch of canvas drawing operations contained many operations causing us to invalidate the full canvas, assume that the next batch will also contain many operations and invalidate the full canvas in the first operation of the batch, r=joe

This commit is contained in:
Robert O'Callahan 2011-03-24 16:13:58 +13:00
parent 3dd0479a61
commit 6efae32538

View File

@ -411,8 +411,10 @@ public:
LayerManager *aManager);
void MarkContextClean();
NS_IMETHOD SetIsIPC(PRBool isIPC);
// this rect is in CSS pixels
// this rect is in canvas device space
NS_IMETHOD Redraw(const gfxRect &r);
// this rect is in mThebes's current user space
NS_IMETHOD RedrawUser(const gfxRect &r);
// nsISupports interface + CC
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -503,7 +505,13 @@ protected:
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
* Redraw is called, reset to false when Render is called.
*/
PRBool mIsEntireFrameInvalid;
PRPackedBool mIsEntireFrameInvalid;
/**
* When this is set, the first call to Redraw(gfxRect) should set
* mIsEntireFrameInvalid since we expect it will be followed by
* many more Redraw calls.
*/
PRPackedBool mPredictManyRedrawCalls;
/**
* Number of times we've invalidated before calling redraw
@ -603,7 +611,7 @@ protected:
* Draws the current path in the given style. Takes care of
* any shadow drawing and will use intermediate surfaces as needed.
*
* If dirtyRect is given, it will contain the device-space dirty
* If dirtyRect is given, it will contain the user-space dirty
* rectangle of the draw operation.
*/
nsresult DrawPath(Style style, gfxRect *dirtyRect = nsnull);
@ -821,7 +829,8 @@ nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
: mValid(PR_FALSE), mOpaque(PR_FALSE), mResetLayer(PR_TRUE)
, mIPC(PR_FALSE)
, mCanvasElement(nsnull)
, mSaveCount(0), mIsEntireFrameInvalid(PR_FALSE), mInvalidateCount(0)
, mSaveCount(0), mIsEntireFrameInvalid(PR_FALSE)
, mPredictManyRedrawCalls(PR_FALSE), mInvalidateCount(0)
, mLastStyle(STYLE_MAX), mStyleStack(20)
{
sNumLivingContexts++;
@ -851,6 +860,7 @@ nsCanvasRenderingContext2D::Reset()
mThebes = nsnull;
mValid = PR_FALSE;
mIsEntireFrameInvalid = PR_FALSE;
mPredictManyRedrawCalls = PR_FALSE;
return NS_OK;
}
@ -1014,6 +1024,10 @@ nsCanvasRenderingContext2D::ApplyStyle(Style aWhichStyle,
nsresult
nsCanvasRenderingContext2D::Redraw()
{
if (mIsEntireFrameInvalid)
return NS_OK;
mIsEntireFrameInvalid = PR_TRUE;
if (!mCanvasElement) {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
return NS_OK;
@ -1023,11 +1037,6 @@ nsCanvasRenderingContext2D::Redraw()
nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
#endif
if (mIsEntireFrameInvalid)
return NS_OK;
mIsEntireFrameInvalid = PR_TRUE;
HTMLCanvasElement()->InvalidateFrame();
return NS_OK;
@ -1036,9 +1045,16 @@ nsCanvasRenderingContext2D::Redraw()
NS_IMETHODIMP
nsCanvasRenderingContext2D::Redraw(const gfxRect& r)
{
++mInvalidateCount;
if (mIsEntireFrameInvalid)
return NS_OK;
if (mPredictManyRedrawCalls ||
mInvalidateCount > kCanvasMaxInvalidateCount) {
return Redraw();
}
if (!mCanvasElement) {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
return NS_OK;
@ -1048,14 +1064,22 @@ nsCanvasRenderingContext2D::Redraw(const gfxRect& r)
nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
#endif
if (++mInvalidateCount > kCanvasMaxInvalidateCount)
return Redraw();
HTMLCanvasElement()->InvalidateFrame(&r);
return NS_OK;
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::RedrawUser(const gfxRect& r)
{
if (mIsEntireFrameInvalid) {
++mInvalidateCount;
return NS_OK;
}
return Redraw(mThebes->UserToDevice(r));
}
NS_IMETHODIMP
nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
{
@ -1914,8 +1938,6 @@ nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect)
// just use the clip extents
*dirtyRect = mThebes->GetClipExtents();
}
*dirtyRect = mThebes->UserToDevice(*dirtyRect);
}
return NS_OK;
@ -1939,8 +1961,7 @@ nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
mThebes->Rectangle(gfxRect(x, y, w, h));
mThebes->Fill();
gfxRect dirty = mThebes->UserToDevice(mThebes->GetUserPathExtent());
return Redraw(dirty);
return RedrawUser(mThebes->GetUserPathExtent());
}
nsresult
@ -1959,7 +1980,7 @@ nsCanvasRenderingContext2D::DrawRect(const gfxRect& rect, Style style)
if (NS_FAILED(rv))
return rv;
return Redraw(dirty);
return RedrawUser(dirty);
}
NS_IMETHODIMP
@ -1999,7 +2020,7 @@ nsCanvasRenderingContext2D::Fill()
nsresult rv = DrawPath(STYLE_FILL, &dirty);
if (NS_FAILED(rv))
return rv;
return Redraw(dirty);
return RedrawUser(dirty);
}
NS_IMETHODIMP
@ -2009,7 +2030,7 @@ nsCanvasRenderingContext2D::Stroke()
nsresult rv = DrawPath(STYLE_STROKE, &dirty);
if (NS_FAILED(rv))
return rv;
return Redraw(dirty);
return RedrawUser(dirty);
}
NS_IMETHODIMP
@ -2848,7 +2869,7 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
mThebes->Paint(CurrentState().StyleIsColor(STYLE_FILL) ? 1.0 : CurrentState().globalAlpha);
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
return Redraw(mThebes->UserToDevice(boundingBox));
return RedrawUser(boundingBox);
return Redraw();
}
@ -3511,10 +3532,7 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
mThebes->Paint(CurrentState().globalAlpha);
}
if (!mIsEntireFrameInvalid) {
gfxRect dirty = mThebes->UserToDevice(clip);
Redraw(dirty);
}
RedrawUser(clip);
}
FINISH:
@ -3669,9 +3687,7 @@ nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY
// note that aX and aY are coordinates in the document that
// we're drawing; aX and aY are drawn to 0,0 in current user
// space.
gfxRect damageRect = mThebes->UserToDevice(gfxRect(0, 0, aW, aH));
Redraw(damageRect);
RedrawUser(gfxRect(0, 0, aW, aH));
return rv;
}
@ -4107,6 +4123,9 @@ nsCanvasRenderingContext2D::GetCanvasLayer(CanvasLayer *aOldLayer,
void
nsCanvasRenderingContext2D::MarkContextClean()
{
if (mInvalidateCount > 0) {
mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
}
mIsEntireFrameInvalid = PR_FALSE;
mInvalidateCount = 0;
}