mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1042291 - Add a CanvasDrawObserver which observes the first few seconds/frames of a Canvas2D's lifetime to determine using a heuristic if it should be software or GPU backed. r=gw280, r=snorp
This commit is contained in:
parent
e2bc55a28c
commit
c1cc8c3af6
@ -89,6 +89,7 @@
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
@ -691,6 +692,106 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
|
||||
|
||||
class CanvasDrawObserver
|
||||
{
|
||||
public:
|
||||
CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
|
||||
|
||||
// Only enumerate draw calls that could affect the heuristic
|
||||
enum DrawCallType {
|
||||
PutImageData,
|
||||
GetImageData,
|
||||
DrawImage
|
||||
};
|
||||
|
||||
// This is the one that we call on relevant draw calls and count
|
||||
// GPU vs. CPU preferrable calls...
|
||||
void DidDrawCall(DrawCallType aType);
|
||||
|
||||
// When this returns true, the observer is done making the decisions.
|
||||
// Right now, we expect to get rid of the observer after the FrameEnd
|
||||
// returns true, though the decision could eventually change if the
|
||||
// function calls shift. If we change to monitor the functions called
|
||||
// and make decisions to change more than once, we would probably want
|
||||
// FrameEnd to reset the timer and counters as it returns true.
|
||||
bool FrameEnd();
|
||||
|
||||
private:
|
||||
// These values will be picked up from preferences:
|
||||
int32_t mMinFramesBeforeDecision;
|
||||
float mMinSecondsBeforeDecision;
|
||||
int32_t mMinCallsBeforeDecision;
|
||||
|
||||
CanvasRenderingContext2D* mCanvasContext;
|
||||
int32_t mSoftwarePreferredCalls;
|
||||
int32_t mGPUPreferredCalls;
|
||||
int32_t mFramesRendered;
|
||||
TimeStamp mCreationTime;
|
||||
};
|
||||
|
||||
// We are not checking for the validity of the preference values. For example,
|
||||
// negative values will have an effect of a quick exit, so no harm done.
|
||||
CanvasDrawObserver::CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext)
|
||||
: mMinFramesBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinFrames())
|
||||
, mMinSecondsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinSeconds())
|
||||
, mMinCallsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinCalls())
|
||||
, mCanvasContext(aCanvasContext)
|
||||
, mSoftwarePreferredCalls(0)
|
||||
, mGPUPreferredCalls(0)
|
||||
, mFramesRendered(0)
|
||||
, mCreationTime(TimeStamp::NowLoRes())
|
||||
{}
|
||||
|
||||
void
|
||||
CanvasDrawObserver::DidDrawCall(DrawCallType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case PutImageData:
|
||||
case GetImageData:
|
||||
if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
|
||||
mCreationTime = TimeStamp::NowLoRes();
|
||||
}
|
||||
mSoftwarePreferredCalls++;
|
||||
break;
|
||||
case DrawImage:
|
||||
if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
|
||||
mCreationTime = TimeStamp::NowLoRes();
|
||||
}
|
||||
mGPUPreferredCalls++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we return true, the observer is done making the decisions...
|
||||
bool
|
||||
CanvasDrawObserver::FrameEnd()
|
||||
{
|
||||
mFramesRendered++;
|
||||
|
||||
// We log the first mMinFramesBeforeDecision frames of any
|
||||
// canvas object then make a call to determine whether it should
|
||||
// be GPU or CPU backed
|
||||
if ((mFramesRendered >= mMinFramesBeforeDecision) ||
|
||||
((TimeStamp::NowLoRes() - mCreationTime).ToSeconds()) > mMinSecondsBeforeDecision) {
|
||||
|
||||
// If we don't have enough data, don't bother changing...
|
||||
if (mGPUPreferredCalls > mMinCallsBeforeDecision ||
|
||||
mSoftwarePreferredCalls > mMinCallsBeforeDecision) {
|
||||
if (mGPUPreferredCalls >= mSoftwarePreferredCalls) {
|
||||
mCanvasContext->SwitchRenderingMode(CanvasRenderingContext2D::RenderingMode::OpenGLBackendMode);
|
||||
} else {
|
||||
mCanvasContext->SwitchRenderingMode(CanvasRenderingContext2D::RenderingMode::SoftwareBackendMode);
|
||||
}
|
||||
}
|
||||
|
||||
// If we ever redesign this class to constantly monitor the functions
|
||||
// and keep making decisions, we would probably want to reset the counters
|
||||
// and the timers here...
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class CanvasRenderingContext2DUserData : public LayerUserData {
|
||||
public:
|
||||
explicit CanvasRenderingContext2DUserData(CanvasRenderingContext2D *aContext)
|
||||
@ -724,6 +825,12 @@ public:
|
||||
static_cast<CanvasRenderingContext2DUserData*>(aData);
|
||||
if (self->mContext) {
|
||||
self->mContext->MarkContextClean();
|
||||
if (self->mContext->mDrawObserver) {
|
||||
if (self->mContext->mDrawObserver->FrameEnd()) {
|
||||
// Note that this call deletes and nulls out mDrawObserver:
|
||||
self->mContext->RemoveDrawObserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool IsForContext(CanvasRenderingContext2D *aContext)
|
||||
@ -828,6 +935,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
|
||||
, mZero(false), mOpaque(false)
|
||||
, mResetLayer(true)
|
||||
, mIPC(false)
|
||||
, mDrawObserver(nullptr)
|
||||
, mIsEntireFrameInvalid(false)
|
||||
, mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
|
||||
, mInvalidateCount(0)
|
||||
@ -839,10 +947,14 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
|
||||
mRenderingMode = RenderingMode::SoftwareBackendMode;
|
||||
}
|
||||
|
||||
if (gfxPlatform::GetPlatform()->HaveChoiceOfHWAndSWCanvas()) {
|
||||
mDrawObserver = new CanvasDrawObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
CanvasRenderingContext2D::~CanvasRenderingContext2D()
|
||||
{
|
||||
RemoveDrawObserver();
|
||||
RemovePostRefreshObserver();
|
||||
Reset();
|
||||
// Drop references from all CanvasRenderingContext2DUserData to this context
|
||||
@ -1443,6 +1555,10 @@ CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value
|
||||
if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
|
||||
// Use software when there is going to be a lot of readback
|
||||
if (attributes.mWillReadFrequently) {
|
||||
|
||||
// We want to lock into software, so remove the observer that
|
||||
// may potentially change that...
|
||||
RemoveDrawObserver();
|
||||
mRenderingMode = RenderingMode::SoftwareBackendMode;
|
||||
}
|
||||
}
|
||||
@ -4009,6 +4125,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;
|
||||
@ -4628,6 +4748,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);
|
||||
@ -4708,6 +4832,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;
|
||||
@ -4887,6 +5015,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;
|
||||
}
|
||||
@ -5066,6 +5198,15 @@ CanvasRenderingContext2D::SkiaGLTex() const
|
||||
return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE);
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::RemoveDrawObserver()
|
||||
{
|
||||
if (mDrawObserver) {
|
||||
delete mDrawObserver;
|
||||
mDrawObserver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<CanvasLayer>
|
||||
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
|
||||
CanvasLayer *aOldLayer,
|
||||
|
@ -114,6 +114,7 @@ private:
|
||||
|
||||
struct CanvasBidiProcessor;
|
||||
class CanvasRenderingContext2DUserData;
|
||||
class CanvasDrawObserver;
|
||||
|
||||
/**
|
||||
** CanvasRenderingContext2D
|
||||
@ -775,6 +776,12 @@ protected:
|
||||
|
||||
uint32_t SkiaGLTex() const;
|
||||
|
||||
// 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;
|
||||
void RemoveDrawObserver();
|
||||
|
||||
/**
|
||||
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
|
||||
* Redraw is called, reset to false when Render is called.
|
||||
@ -1089,6 +1096,7 @@ protected:
|
||||
}
|
||||
|
||||
friend struct CanvasBidiProcessor;
|
||||
friend class CanvasDrawObserver;
|
||||
};
|
||||
|
||||
MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::CanvasMultiGetterType)
|
||||
|
@ -406,6 +406,11 @@ gfxAndroidPlatform::GetScreenDepth() const
|
||||
|
||||
bool
|
||||
gfxAndroidPlatform::UseAcceleratedSkiaCanvas()
|
||||
{
|
||||
return HaveChoiceOfHWAndSWCanvas() && gfxPlatform::UseAcceleratedSkiaCanvas();
|
||||
}
|
||||
|
||||
bool gfxAndroidPlatform::HaveChoiceOfHWAndSWCanvas()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (AndroidBridge::Bridge()->GetAPIVersion() < 11) {
|
||||
@ -413,6 +418,5 @@ gfxAndroidPlatform::UseAcceleratedSkiaCanvas()
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return gfxPlatform::UseAcceleratedSkiaCanvas();
|
||||
return gfxPlatform::HaveChoiceOfHWAndSWCanvas();
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool HaveChoiceOfHWAndSWCanvas() MOZ_OVERRIDE;
|
||||
virtual bool UseAcceleratedSkiaCanvas() MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -939,6 +939,11 @@ gfxPlatform::UseAcceleratedSkiaCanvas()
|
||||
mPreferredCanvasBackend == BackendType::SKIA;
|
||||
}
|
||||
|
||||
bool gfxPlatform::HaveChoiceOfHWAndSWCanvas()
|
||||
{
|
||||
return mPreferredCanvasBackend == BackendType::SKIA;
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatform::InitializeSkiaCacheLimits()
|
||||
{
|
||||
|
@ -262,6 +262,14 @@ public:
|
||||
return BackendTypeBit(aType) & mContentBackendBitmask;
|
||||
}
|
||||
|
||||
/// This function lets us know if the current preferences/platform
|
||||
/// combination allows for both accelerated and not accelerated canvas
|
||||
/// implementations. If it does, and other relevant preferences are
|
||||
/// asking for it, we will examine the commands in the first few seconds
|
||||
/// of the canvas usage, and potentially change to accelerated or
|
||||
/// non-accelerated canvas.
|
||||
virtual bool HaveChoiceOfHWAndSWCanvas();
|
||||
|
||||
virtual bool UseAcceleratedSkiaCanvas();
|
||||
virtual void InitializeSkiaCacheLimits();
|
||||
|
||||
|
@ -185,6 +185,9 @@ private:
|
||||
#if defined(ANDROID)
|
||||
DECL_GFX_PREF(Once, "gfx.apitrace.enabled", UseApitrace, bool, false);
|
||||
#endif
|
||||
DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30);
|
||||
DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_seconds", CanvasAutoAccelerateMinSeconds, float, 5.0f);
|
||||
DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls", CanvasAutoAccelerateMinCalls, int32_t, 4);
|
||||
DECL_GFX_PREF(Live, "gfx.canvas.azure.accelerated", CanvasAzureAccelerated, bool, false);
|
||||
DECL_GFX_PREF(Once, "gfx.canvas.skiagl.dynamic-cache", CanvasSkiaGLDynamicCache, bool, false);
|
||||
DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-size", CanvasSkiaGLCacheSize, int32_t, 96);
|
||||
|
Loading…
Reference in New Issue
Block a user