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

View File

@ -21,6 +21,7 @@
#include "mozilla/dom/CanvasPattern.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/TimeStamp.h"
#include "gfx2DGlue.h"
#include "imgIEncoder.h"
#include "nsLayoutUtils.h"
@ -115,6 +116,7 @@ private:
struct CanvasBidiProcessor;
class CanvasRenderingContext2DUserData;
class CanvasDrawObserver;
/**
** CanvasRenderingContext2D
@ -767,6 +769,11 @@ protected:
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
* Redraw is called, reset to false when Render is called.
@ -1081,6 +1088,73 @@ protected:
}
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)