Bug 1042291 - Implement a better heuristic for when to use HW accelerated <canvas> r=snorp

This commit is contained in:
George Wright 2014-09-10 16:15:43 -04:00
parent 4d3fe6fe9f
commit 677c5c8d14
2 changed files with 95 additions and 0 deletions

View File

@ -724,6 +724,9 @@ public:
static_cast<CanvasRenderingContext2DUserData*>(aData); static_cast<CanvasRenderingContext2DUserData*>(aData);
if (self->mContext) { if (self->mContext) {
self->mContext->MarkContextClean(); self->mContext->MarkContextClean();
if (self->mContext->mDrawObserver) {
self->mContext->mDrawObserver->FrameEnd();
}
} }
} }
bool IsForContext(CanvasRenderingContext2D *aContext) bool IsForContext(CanvasRenderingContext2D *aContext)
@ -826,6 +829,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
, mResetLayer(true) , mResetLayer(true)
, mIPC(false) , mIPC(false)
, mStream(nullptr) , mStream(nullptr)
, mDrawObserver(nullptr)
, mIsEntireFrameInvalid(false) , mIsEntireFrameInvalid(false)
, mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false) , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
, mInvalidateCount(0) , mInvalidateCount(0)
@ -838,6 +842,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
mRenderingMode = RenderingMode::SoftwareBackendMode; mRenderingMode = RenderingMode::SoftwareBackendMode;
} }
mDrawObserver = new CanvasDrawObserver(this);
} }
CanvasRenderingContext2D::~CanvasRenderingContext2D() CanvasRenderingContext2D::~CanvasRenderingContext2D()
@ -3816,6 +3821,10 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
uint8_t optional_argc, uint8_t optional_argc,
ErrorResult& error) ErrorResult& error)
{ {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
}
MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6); MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
RefPtr<SourceSurface> srcSurf; RefPtr<SourceSurface> srcSurf;
@ -4332,6 +4341,10 @@ CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
double aSy, double aSw, double aSy, double aSw,
double aSh, ErrorResult& error) double aSh, ErrorResult& error)
{ {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
}
EnsureTarget(); EnsureTarget();
if (!IsTargetValid()) { if (!IsTargetValid()) {
error.Throw(NS_ERROR_FAILURE); error.Throw(NS_ERROR_FAILURE);
@ -4412,6 +4425,10 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
uint32_t aHeight, uint32_t aHeight,
JSObject** aRetval) JSObject** aRetval)
{ {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
}
MOZ_ASSERT(aWidth && aHeight); MOZ_ASSERT(aWidth && aHeight);
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4; CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
@ -4585,6 +4602,10 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY, bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
int32_t dirtyWidth, int32_t dirtyHeight) int32_t dirtyWidth, int32_t dirtyHeight)
{ {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::PutImageData);
}
if (w == 0 || h == 0) { if (w == 0 || h == 0) {
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
} }

View File

@ -21,6 +21,7 @@
#include "mozilla/dom/CanvasPattern.h" #include "mozilla/dom/CanvasPattern.h"
#include "mozilla/gfx/Rect.h" #include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h" #include "mozilla/gfx/2D.h"
#include "mozilla/TimeStamp.h"
#include "gfx2DGlue.h" #include "gfx2DGlue.h"
#include "imgIEncoder.h" #include "imgIEncoder.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
@ -115,6 +116,7 @@ private:
struct CanvasBidiProcessor; struct CanvasBidiProcessor;
class CanvasRenderingContext2DUserData; class CanvasRenderingContext2DUserData;
class CanvasDrawObserver;
/** /**
** CanvasRenderingContext2D ** CanvasRenderingContext2D
@ -767,6 +769,11 @@ protected:
RefPtr<gl::SurfaceStream> mStream; RefPtr<gl::SurfaceStream> mStream;
// This observes our draw calls at the beginning of the canvas
// lifetime and switches to software or GPU mode depending on
// what it thinks is best
CanvasDrawObserver *mDrawObserver;
/** /**
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
* Redraw is called, reset to false when Render is called. * Redraw is called, reset to false when Render is called.
@ -1081,6 +1088,73 @@ protected:
} }
friend struct CanvasBidiProcessor; friend struct CanvasBidiProcessor;
friend class CanvasDrawObserver;
};
class CanvasDrawObserver
{
public:
CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext)
: mCanvasContext(aCanvasContext)
, mDisabled(false)
, mSoftwarePreferredCalls(0)
, mGPUPreferredCalls(0)
, mFramesRendered(0)
, mCreationTime(TimeStamp::NowLoRes())
{}
// Only enumerate draw calls that could affect the heuristic
enum DrawCallType {
PutImageData,
GetImageData,
DrawImage
};
void DidDrawCall(DrawCallType aType) {
if (mDisabled) {
return;
}
switch (aType) {
case PutImageData:
case GetImageData:
mSoftwarePreferredCalls++;
break;
case DrawImage:
mGPUPreferredCalls++;
break;
}
}
void FrameEnd() {
if (mDisabled) {
return;
}
mFramesRendered++;
TimeDuration timeElapsed = TimeStamp::NowLoRes() - mCreationTime;
// We log the first 30 frames of any canvas object then make a
// call to determine whether it should be GPU or CPU backed
if (mFramesRendered >= 30 || timeElapsed.ToSeconds() >= 5.0) {
if (mGPUPreferredCalls >= mSoftwarePreferredCalls) {
mCanvasContext->SwitchRenderingMode(CanvasRenderingContext2D::RenderingMode::OpenGLBackendMode);
} else {
mCanvasContext->SwitchRenderingMode(CanvasRenderingContext2D::RenderingMode::SoftwareBackendMode);
}
mDisabled = true;
}
}
private:
CanvasRenderingContext2D* mCanvasContext;
bool mDisabled;
unsigned int mSoftwarePreferredCalls;
unsigned int mGPUPreferredCalls;
unsigned int mFramesRendered;
TimeStamp mCreationTime;
}; };
MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::CanvasMultiGetterType) MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::CanvasMultiGetterType)