--- a/dom/canvas/BasicRenderingContext2D.cpp
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -2,84 +2,511 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AdjustedTarget.h"
#include "CanvasImageCache.h"
#include "CanvasUtils.h"
#include "DrawResult.h"
+#include "gfxPrefs.h"
#include "gfxUtils.h"
#include "GLContext.h"
#include "ImageRegion.h"
#include "mozilla/dom/BasicRenderingContext2D.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
#include "nsContentUtils.h"
#include "nsICanvasRenderingContextInternal.h"
+#include "nsIMemoryReporter.h"
#include "nsPrintfCString.h"
#include "nsStyleUtil.h"
#include "SkiaGLGlue.h"
#ifdef USE_SKIA
#include "GLBlitHelper.h"
#include "SurfaceTypes.h"
#endif
using mozilla::CanvasUtils::FloatValidate;
using mozilla::gfx::AntialiasMode;
using mozilla::gfx::ArcToBezier;
using mozilla::gfx::CapStyle;
using mozilla::gfx::Color;
+using mozilla::gfx::CriticalLog;
using mozilla::gfx::DrawOptions;
using mozilla::gfx::DrawSurfaceOptions;
using mozilla::gfx::ExtendMode;
+using mozilla::gfx::Factory;
using mozilla::gfx::FillRule;
using mozilla::gfx::IntPoint;
+using mozilla::gfx::IntRect;
using mozilla::gfx::JoinStyle;
using mozilla::gfx::LogReason;
using mozilla::gfx::IntSize;
using mozilla::gfx::NativeSurface;
using mozilla::gfx::NativeSurfaceType;
using mozilla::gfx::Path;
using mozilla::gfx::SamplingBounds;
using mozilla::gfx::SamplingFilter;
using mozilla::gfx::Size;
using mozilla::gfx::SourceSurface;
using mozilla::gfx::StrokeOptions;
using mozilla::gfx::ToDeviceColor;
using mozilla::image::DrawResult;
using mozilla::image::ImageRegion;
using mozilla::layers::AutoLockImage;
+using mozilla::layers::PersistentBufferProvider;
+using mozilla::layers::PersistentBufferProviderBasic;
namespace mozilla {
namespace dom{
+// This is KIND_OTHER because it's not always clear where in memory the pixels
+// of a canvas are stored. Furthermore, this memory will be tracked by the
+// underlying surface implementations. See bug 655638 for details.
+class Canvas2dPixelsReporter final : public nsIMemoryReporter
+{
+ ~Canvas2dPixelsReporter() {}
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
+ BasicRenderingContext2D::sCanvasAzureMemoryUsed,
+ "Memory used by 2D canvases. Each canvas requires "
+ "(width * height * 4) bytes.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
+
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
+NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+CanvasShutdownObserver::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ mCanvas->OnShutdown();
+ nsContentUtils::UnregisterShutdownObserver(this);
+ }
+
+ return NS_OK;
+}
// Cap sigma to avoid overly large temp surfaces.
const mozilla::gfx::Float SIGMA_MAX = 100;
const size_t MAX_STYLE_STACK_SIZE = 1024;
/**
** BasicRenderingContext2D impl
**/
+
+// Initialize our static variables.
+uint32_t BasicRenderingContext2D::sNumLivingContexts = 0;
+DrawTarget* BasicRenderingContext2D::sErrorTarget = nullptr;
+int64_t BasicRenderingContext2D::sCanvasAzureMemoryUsed = 0;
+
+BasicRenderingContext2D::BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
+ : mRenderingMode(RenderingMode::OpenGLBackendMode)
+ , mCompositorBackend(aCompositorBackend)
+ // these are the default values from the Canvas spec
+ , mWidth(0), mHeight(0)
+ , mPathTransformWillUpdate(false)
+ , mIsSkiaGL(false)
+ , mHasPendingStableStateCallback(false)
+{
+ sNumLivingContexts++;
+
+ mShutdownObserver = new CanvasShutdownObserver(this);
+ nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
+}
+
+BasicRenderingContext2D::~BasicRenderingContext2D()
+{
+ RemoveShutdownObserver();
+ Reset();
+
+ sNumLivingContexts--;
+ if (!sNumLivingContexts) {
+ NS_IF_RELEASE(sErrorTarget);
+ }
+}
+
+nsresult
+BasicRenderingContext2D::Reset()
+{
+ // only do this for non-docshell created contexts,
+ // since those are the ones that we created a surface for
+ if (mTarget && IsTargetValid()) {
+ sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+ }
+
+ bool forceReset = true;
+ ReturnTarget(forceReset);
+ mTarget = nullptr;
+ mBufferProvider = nullptr;
+
+ return NS_OK;
+}
+
+void
+BasicRenderingContext2D::RemoveShutdownObserver()
+{
+ if (mShutdownObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
+ mShutdownObserver = nullptr;
+ }
+}
+
+void
+BasicRenderingContext2D::OnShutdown()
+{
+ mShutdownObserver = nullptr;
+
+ RefPtr<PersistentBufferProvider> provider = mBufferProvider;
+
+ Reset();
+
+ if (provider) {
+ provider->OnShutdown();
+ }
+}
+
+BasicRenderingContext2D::RenderingMode
+BasicRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
+ RenderingMode aRenderingMode)
+{
+ if (AlreadyShutDown()) {
+ gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
+ SetErrorState();
+ return aRenderingMode;
+ }
+
+ // This would make no sense, so make sure we don't get ourselves in a mess
+ MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
+
+ RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
+
+ if (mTarget && mode == mRenderingMode) {
+ return mRenderingMode;
+ }
+
+ // Check that the dimensions are sane
+ if (mWidth > gfxPrefs::MaxCanvasSize() ||
+ mHeight > gfxPrefs::MaxCanvasSize() ||
+ mWidth < 0 || mHeight < 0) {
+
+ SetErrorState();
+ return aRenderingMode;
+ }
+
+ // If the next drawing command covers the entire canvas, we can skip copying
+ // from the previous frame and/or clearing the canvas.
+ gfx::Rect canvasRect(0, 0, mWidth, mHeight);
+ bool canDiscardContent = aCoveredRect &&
+ CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
+
+ // If a clip is active we don't know for sure that the next drawing command
+ // will really cover the entire canvas.
+ for (const auto& style : mStyleStack) {
+ if (!canDiscardContent) {
+ break;
+ }
+ for (const auto& clipOrTransform : style.clipsAndTransforms) {
+ if (clipOrTransform.IsClip()) {
+ canDiscardContent = false;
+ break;
+ }
+ }
+ }
+
+ ScheduleStableStateCallback();
+
+ IntRect persistedRect = canDiscardContent ? IntRect()
+ : IntRect(0, 0, mWidth, mHeight);
+
+ if (mBufferProvider && mode == mRenderingMode) {
+ mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
+
+ if (mTarget && !mBufferProvider->PreservesDrawingState()) {
+ RestoreClipsAndTransformToTarget();
+ }
+
+ if (mTarget) {
+ return mode;
+ }
+ }
+
+ RefPtr<DrawTarget> newTarget;
+ RefPtr<PersistentBufferProvider> newProvider;
+
+ if (mode == RenderingMode::OpenGLBackendMode &&
+ !TrySkiaGLTarget(newTarget, newProvider)) {
+ // Fall back to software.
+ mode = RenderingMode::SoftwareBackendMode;
+ }
+
+ if (mode == RenderingMode::SoftwareBackendMode &&
+ !TrySharedTarget(newTarget, newProvider) &&
+ !TryBasicTarget(newTarget, newProvider)) {
+
+ gfxCriticalError(
+ CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
+ ) << "Failed borrow shared and basic targets.";
+
+ SetErrorState();
+ return mode;
+ }
+
+
+ MOZ_ASSERT(newTarget);
+ MOZ_ASSERT(newProvider);
+
+ bool needsClear = !canDiscardContent;
+ if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
+ // Skia expects the unused X channel to contains 0xFF even for opaque operations
+ // so we can't skip clearing in that case, even if we are going to cover the
+ // entire canvas in the next drawing operation.
+ newTarget->ClearRect(canvasRect);
+ needsClear = false;
+ }
+
+ // Try to copy data from the previous buffer provider if there is one.
+ if (!canDiscardContent && mBufferProvider && CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
+ needsClear = false;
+ }
+
+ if (needsClear) {
+ newTarget->ClearRect(canvasRect);
+ }
+
+ mTarget = newTarget.forget();
+ mBufferProvider = newProvider.forget();
+
+ RegisterAllocation();
+
+ RestoreClipsAndTransformToTarget();
+
+ // Force a full layer transaction since we didn't have a layer before
+ // and now we might need one.
+ if (GetCanvasElement()) {
+ GetCanvasElement()->InvalidateCanvas();
+ }
+ // Calling Redraw() tells our invalidation machinery that the entire
+ // canvas is already invalid, which can speed up future drawing.
+ Redraw();
+
+ return mode;
+}
+
+void
+BasicRenderingContext2D::RegisterAllocation()
+{
+ // XXX - It would make more sense to track the allocation in
+ // PeristentBufferProvider, rather than here.
+ static bool registered = false;
+ // FIXME: Disable the reporter for now, see bug 1241865
+ if (!registered && false) {
+ registered = true;
+ RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
+ }
+
+ sCanvasAzureMemoryUsed += mWidth * mHeight * 4;
+ JSContext* context = nsContentUtils::GetCurrentJSContext();
+ if (context) {
+ JS_updateMallocCounter(context, mWidth * mHeight * 4);
+ }
+
+ JSObject* wrapper = GetWrapperPreserveColor();
+ if (wrapper) {
+ CycleCollectedJSContext::Get()->
+ AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
+ }
+}
+
+bool
+BasicRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider)
+{
+ aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
+ GetSurfaceFormat());
+ if (!aOutDT) {
+ return false;
+ }
+
+ aOutProvider = new PersistentBufferProviderBasic(aOutDT);
+ return true;
+}
+
+bool
+BasicRenderingContext2D::CopyBufferProvider(PersistentBufferProvider& aOld,
+ DrawTarget& aTarget,
+ IntRect aCopyRect)
+{
+ // Borrowing the snapshot must be done after ReturnTarget.
+ RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
+
+ if (!snapshot) {
+ return false;
+ }
+
+ aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
+ aOld.ReturnSnapshot(snapshot.forget());
+ return true;
+}
+
+void
+BasicRenderingContext2D::RestoreClipsAndTransformToTarget()
+{
+ // Restore clips and transform.
+ mTarget->SetTransform(Matrix());
+
+ if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
+ // Cairo doesn't play well with huge clips. When given a very big clip it
+ // will try to allocate big mask surface without taking the target
+ // size into account which can cause OOM. See bug 1034593.
+ // This limits the clip extents to the size of the canvas.
+ // A fix in Cairo would probably be preferable, but requires somewhat
+ // invasive changes.
+ mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
+ }
+
+ for (const auto& style : mStyleStack) {
+ for (const auto& clipOrTransform : style.clipsAndTransforms) {
+ if (clipOrTransform.IsClip()) {
+ mTarget->PushClip(clipOrTransform.clip);
+ } else {
+ mTarget->SetTransform(clipOrTransform.transform);
+ }
+ }
+ }
+}
+
+void
+BasicRenderingContext2D::ReturnTarget(bool aForceReset)
+{
+ if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
+ CurrentState().transform = mTarget->GetTransform();
+ if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
+ for (const auto& style : mStyleStack) {
+ for (const auto& clipOrTransform : style.clipsAndTransforms) {
+ if (clipOrTransform.IsClip()) {
+ mTarget->PopClip();
+ }
+ }
+ }
+
+ if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
+ // With the cairo backend we pushed an extra clip rect which we have to
+ // balance out here. See the comment in RestoreClipsAndTransformToTarget.
+ mTarget->PopClip();
+ }
+
+ mTarget->SetTransform(Matrix());
+ }
+
+ mBufferProvider->ReturnDrawTarget(mTarget.forget());
+ }
+}
+
+void
+BasicRenderingContext2D::ScheduleStableStateCallback()
+{
+ if (mHasPendingStableStateCallback) {
+ return;
+ }
+ mHasPendingStableStateCallback = true;
+
+ nsContentUtils::RunInStableState(
+ NewRunnableMethod(this, &BasicRenderingContext2D::OnStableState)
+ );
+}
+
+void
+BasicRenderingContext2D::OnStableState()
+{
+ if (!mHasPendingStableStateCallback) {
+ return;
+ }
+
+ ReturnTarget();
+
+ mHasPendingStableStateCallback = false;
+}
+
+void
+BasicRenderingContext2D::SetErrorState()
+{
+ EnsureErrorTarget();
+
+ if (mTarget && mTarget != sErrorTarget) {
+ sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+ }
+
+ mTarget = sErrorTarget;
+ mBufferProvider = nullptr;
+
+ // clear transforms, clips, etc.
+ SetInitialState();
+}
+
+void
+BasicRenderingContext2D::SetInitialState()
+{
+ // Set up the initial canvas defaults
+ mPathBuilder = nullptr;
+ mPath = nullptr;
+ mDSPathBuilder = nullptr;
+
+ mStyleStack.Clear();
+ ContextState *state = mStyleStack.AppendElement();
+ state->globalAlpha = 1.0;
+
+ state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
+ state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
+ state->shadowColor = NS_RGBA(0,0,0,0);
+}
+
+void
+BasicRenderingContext2D::EnsureErrorTarget()
+{
+ if (sErrorTarget) {
+ return;
+ }
+
+ RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+ MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
+
+ sErrorTarget = errorTarget;
+ NS_ADDREF(sErrorTarget);
+}
+
bool
BasicRenderingContext2D::PatternIsOpaque(BasicRenderingContext2D::Style aStyle) const
{
const ContextState& state = CurrentState();
if (state.globalAlpha < 1.0) {
return false;
}
@@ -93,17 +520,16 @@ BasicRenderingContext2D::PatternIsOpaque
if (!state.gradientStyles[aStyle]) {
// it's a color pattern.
return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
}
return false;
}
-
void
BasicRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
Style aWhichStyle)
{
const ContextState &state = CurrentState();
if (state.patternStyles[aWhichStyle]) {
aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
} else if (state.gradientStyles[aWhichStyle]) {
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BasicRenderingContext2D_h
#define BasicRenderingContext2D_h
#include "FilterSupport.h"
#include "gfxTextRun.h"
+#include "Layers.h"
#include "mozilla/dom/CanvasGradient.h"
#include "mozilla/dom/CanvasPattern.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/PatternHelpers.h"
#include "nsStyleStruct.h"
#include "nsSVGEffects.h"
@@ -30,33 +31,54 @@ namespace dom {
{ 0x89, 0x95, 0x1c, 0x6a, 0x82, 0xeb, 0xef, 0x9f} }
class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
CanvasImageSource;
extern const mozilla::gfx::Float SIGMA_MAX;
+class CanvasShutdownObserver final : public nsIObserver
+{
+public:
+ explicit CanvasShutdownObserver(BasicRenderingContext2D* aCanvas)
+ : mCanvas(aCanvas)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+private:
+ ~CanvasShutdownObserver() {}
+
+ BasicRenderingContext2D* mCanvas;
+};
+
/*
* BasicRenderingContext2D
*/
-class BasicRenderingContext2D : public nsISupports
+class BasicRenderingContext2D :
+ public nsISupports,
+ public nsWrapperCache
{
public:
enum RenderingMode {
SoftwareBackendMode,
OpenGLBackendMode,
DefaultBackendMode
};
// This is created lazily so it is necessary to call EnsureTarget before
// accessing it. In the event of an error it will be equal to
// sErrorTarget.
RefPtr<DrawTarget> mTarget;
+ void OnShutdown();
+
+ gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+
// this rect is in mTarget's current user space
virtual void RedrawUser(const gfxRect& aR) = 0;
virtual nsresult Redraw() = 0;
void LineTo(const mozilla::gfx::Point& aPoint)
{
if (mPathBuilder) {
@@ -80,25 +102,19 @@ public:
}
}
// Check the global setup, as well as the compositor type:
bool AllowOpenGLCanvas() const;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_BASICRENDERINGCONTEXT2D_IID)
protected:
- virtual ~BasicRenderingContext2D() {}
+ virtual ~BasicRenderingContext2D();
public:
- explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
- : mRenderingMode(RenderingMode::OpenGLBackendMode)
- , mCompositorBackend(aCompositorBackend)
- // these are the default values from the Canvas spec
- , mWidth(0), mHeight(0)
- , mPathTransformWillUpdate(false)
- , mIsSkiaGL(false) {}
+ explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend);
//
// CanvasState
//
void Save();
void Restore();
//
@@ -379,20 +395,21 @@ public:
void Arc(double aX, double aY, double aRadius, double aStartAngle,
double aEndAngle, bool aAnticlockwise,
mozilla::ErrorResult& aError);
void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
double aRotation, double aStartAngle, double aEndAngle,
bool aAnticlockwise, ErrorResult& aError);
protected:
+ friend class AdjustedTarget;
+ friend class AdjustedTargetForFilter;
+ friend class AdjustedTargetForShadow;
+ friend class Canvas2dPixelsReporter;
friend class CanvasGeneralPattern;
- friend class AdjustedTarget;
- friend class AdjustedTargetForShadow;
- friend class AdjustedTargetForFilter;
enum class Style : uint8_t {
STROKE = 0,
FILL,
MAX
};
enum class TextAlign : uint8_t {
@@ -578,16 +595,18 @@ protected:
RenderingMode mRenderingMode;
layers::LayersBackend mCompositorBackend;
AutoTArray<ContextState, 3> mStyleStack;
int32_t mWidth, mHeight;
+ RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
+
/**
* We also have a device space pathbuilder. The reason for this is as
* follows, when a path is being built, but the transform changes, we
* can no longer keep a single path in userspace, considering there's
* several 'user spaces' now. We therefore transform the current path
* into device space, and add all operations to this path in device
* space.
*
@@ -637,37 +656,107 @@ protected:
* mPath is always in user-space.
*/
RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
// True if the current DrawTarget is using skia-gl, used so we can avoid
// requesting the DT from mBufferProvider to check.
bool mIsSkiaGL;
+ /**
+ * The number of living nsCanvasRenderingContexts. When this goes down to
+ * 0, we free the premultiply and unpremultiply tables, if they exist.
+ */
+ static uint32_t sNumLivingContexts;
+ static int64_t sCanvasAzureMemoryUsed;
+
+ bool mHasPendingStableStateCallback;
+
+ static mozilla::gfx::DrawTarget* sErrorTarget;
+
protected:
virtual HTMLCanvasElement* GetCanvasElement() = 0;
- virtual bool AlreadyShutDown() const = 0;
+ NS_IMETHOD Reset();
+
+ RefPtr<CanvasShutdownObserver> mShutdownObserver;
+ void RemoveShutdownObserver();
+ bool AlreadyShutDown() const { return !mShutdownObserver; }
/**
* Create the backing surfacing, if it doesn't exist. If there is an error
* in creating the target then it will put sErrorTarget in place. If there
* is in turn an error in creating the sErrorTarget then they would both
* be null so IsTargetValid() would still return null.
*
* Returns the actual rendering mode being used by the created target.
*/
- virtual RenderingMode
+ RenderingMode
EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
- RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) = 0;
+ RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+
+ virtual bool
+ TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider) = 0;
+
+ virtual bool
+ TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider) = 0;
+
+ bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+
+ void RegisterAllocation();
+
+ bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
+ gfx::DrawTarget& aTarget,
+ gfx::IntRect aCopyRect);
+
+ void RestoreClipsAndTransformToTarget();
+
+ /*
+ * Returns the target to the buffer provider. i.e. this will queue a frame for
+ * rendering.
+ */
+ void ReturnTarget(bool aForceReset = false);
+
+ /**
+ * Cf. OnStableState.
+ */
+ void ScheduleStableStateCallback();
+
+ /**
+ * Returns the surface format this canvas should be allocated using. Takes
+ * into account mOpaque, platform requirements, etc.
+ */
+ virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const = 0;
+
+ /**
+ * This method is run at the end of the event-loop spin where
+ * ScheduleStableStateCallback was called.
+ *
+ * We use it to unlock resources that need to be locked while drawing.
+ */
+ void OnStableState();
+
+ /**
+ * Creates the error target, if it doesn't exist
+ */
+ static void EnsureErrorTarget();
+
+ void SetErrorState();
+
+ void SetInitialState();
/**
* Check if the target is valid after calling EnsureTarget.
*/
- virtual bool IsTargetValid() const = 0;
+ bool IsTargetValid() const {
+ return !!mTarget && mTarget != sErrorTarget;
+ }
inline ContextState& CurrentState() {
return mStyleStack[mStyleStack.Length() - 1];
}
inline const ContextState& CurrentState() const {
return mStyleStack[mStyleStack.Length() - 1];
}
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -52,17 +52,16 @@
#include "gfxBlur.h"
#include "gfxPrefs.h"
#include "nsFrameLoader.h"
#include "nsBidi.h"
#include "nsBidiPresUtils.h"
#include "Layers.h"
#include "LayerUserData.h"
-#include "nsIMemoryReporter.h"
#include "CanvasImageCache.h"
#include <algorithm>
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Conversions.h"
#include "js/HeapAPI.h"
@@ -148,72 +147,16 @@ using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::ipc;
using namespace mozilla::layers;
namespace mozilla {
namespace dom {
-/* Memory reporter stuff */
-static int64_t gCanvasAzureMemoryUsed = 0;
-
-// This is KIND_OTHER because it's not always clear where in memory the pixels
-// of a canvas are stored. Furthermore, this memory will be tracked by the
-// underlying surface implementations. See bug 655638 for details.
-class Canvas2dPixelsReporter final : public nsIMemoryReporter
-{
- ~Canvas2dPixelsReporter() {}
-public:
- NS_DECL_ISUPPORTS
-
- NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize) override
- {
- MOZ_COLLECT_REPORT(
- "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES, gCanvasAzureMemoryUsed,
- "Memory used by 2D canvases. Each canvas requires "
- "(width * height * 4) bytes.");
-
- return NS_OK;
- }
-};
-
-NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
-
-class CanvasShutdownObserver final : public nsIObserver
-{
-public:
- explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
- : mCanvas(aCanvas)
- {}
-
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
-private:
- ~CanvasShutdownObserver() {}
-
- CanvasRenderingContext2D* mCanvas;
-};
-
-NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
-
-NS_IMETHODIMP
-CanvasShutdownObserver::Observe(nsISupports *aSubject,
- const char *aTopic,
- const char16_t *aData)
-{
- if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
- mCanvas->OnShutdown();
- nsContentUtils::UnregisterShutdownObserver(this);
- }
-
- return NS_OK;
-}
-
class CanvasDrawObserver
{
public:
explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
// Only enumerate draw calls that could affect the heuristic
enum DrawCallType {
PutImageData,
@@ -475,67 +418,56 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_END
/**
** CanvasRenderingContext2D impl
**/
// Initialize our static variables.
-uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
-DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
static bool sMaxContextsInitialized = false;
static int32_t sMaxContexts = 0;
CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
: BasicRenderingContext2D(aCompositorBackend)
, mZero(false), mOpaque(false)
, mResetLayer(true)
, mIPC(false)
- , mHasPendingStableStateCallback(false)
, mDrawObserver(nullptr)
, mIsEntireFrameInvalid(false)
, mPredictManyRedrawCalls(false)
, mIsCapturedFrameInvalid(false)
, mInvalidateCount(0)
{
if (!sMaxContextsInitialized) {
sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
sMaxContextsInitialized = true;
}
- sNumLivingContexts++;
-
- mShutdownObserver = new CanvasShutdownObserver(this);
- nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
-
// The default is to use OpenGL mode
if (AllowOpenGLCanvas()) {
mDrawObserver = new CanvasDrawObserver(this);
} else {
mRenderingMode = RenderingMode::SoftwareBackendMode;
}
}
CanvasRenderingContext2D::~CanvasRenderingContext2D()
{
RemoveDrawObserver();
RemovePostRefreshObserver();
- RemoveShutdownObserver();
+
Reset();
// Drop references from all CanvasRenderingContext2DUserData to this context
for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
mUserDatas[i]->Forget();
}
- sNumLivingContexts--;
- if (!sNumLivingContexts) {
- NS_IF_RELEASE(sErrorTarget);
- }
+
RemoveDemotableContext(this);
}
JSObject*
CanvasRenderingContext2D::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return CanvasRenderingContext2DBinding::Wrap(aCx, this, aGivenProto);
}
@@ -581,17 +513,17 @@ CanvasRenderingContext2D::Reset()
{
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
// only do this for non-docshell created contexts,
// since those are the ones that we created a surface for
if (mTarget && IsTargetValid() && !mDocShell) {
- gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+ sCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
}
bool forceReset = true;
ReturnTarget(forceReset);
mTarget = nullptr;
mBufferProvider = nullptr;
// reset hit regions
@@ -602,39 +534,16 @@ CanvasRenderingContext2D::Reset()
mIsEntireFrameInvalid = false;
mPredictManyRedrawCalls = false;
mIsCapturedFrameInvalid = false;
return NS_OK;
}
void
-CanvasRenderingContext2D::OnShutdown()
-{
- mShutdownObserver = nullptr;
-
- RefPtr<PersistentBufferProvider> provider = mBufferProvider;
-
- Reset();
-
- if (provider) {
- provider->OnShutdown();
- }
-}
-
-void
-CanvasRenderingContext2D::RemoveShutdownObserver()
-{
- if (mShutdownObserver) {
- nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
- mShutdownObserver = nullptr;
- }
-}
-
-void
CanvasRenderingContext2D::DidImageDrawCall()
{
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
}
}
// static
@@ -751,33 +660,16 @@ bool CanvasRenderingContext2D::SwitchRen
}
// We succeeded, so update mRenderingMode to reflect reality
mRenderingMode = attemptedMode;
return true;
}
-bool
-CanvasRenderingContext2D::CopyBufferProvider(PersistentBufferProvider& aOld,
- DrawTarget& aTarget,
- IntRect aCopyRect)
-{
- // Borrowing the snapshot must be done after ReturnTarget.
- RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();
-
- if (!snapshot) {
- return false;
- }
-
- aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
- aOld.ReturnSnapshot(snapshot.forget());
- return true;
-}
-
void CanvasRenderingContext2D::Demote()
{
if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
RemoveDemotableContext(this);
}
}
std::vector<CanvasRenderingContext2D*>&
@@ -893,253 +785,16 @@ CanvasRenderingContext2D::CheckSizeForSk
double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
int32_t threshold = ceil(scale * scale * gScreenPixels);
// screen size acts as max threshold
return threshold < 0 || (aSize.width * aSize.height) <= threshold;
}
-void
-CanvasRenderingContext2D::ScheduleStableStateCallback()
-{
- if (mHasPendingStableStateCallback) {
- return;
- }
- mHasPendingStableStateCallback = true;
-
- nsContentUtils::RunInStableState(
- NewRunnableMethod(this, &CanvasRenderingContext2D::OnStableState)
- );
-}
-
-void
-CanvasRenderingContext2D::OnStableState()
-{
- if (!mHasPendingStableStateCallback) {
- return;
- }
-
- ReturnTarget();
-
- mHasPendingStableStateCallback = false;
-}
-
-void
-CanvasRenderingContext2D::RestoreClipsAndTransformToTarget()
-{
- // Restore clips and transform.
- mTarget->SetTransform(Matrix());
-
- if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
- // Cairo doesn't play well with huge clips. When given a very big clip it
- // will try to allocate big mask surface without taking the target
- // size into account which can cause OOM. See bug 1034593.
- // This limits the clip extents to the size of the canvas.
- // A fix in Cairo would probably be preferable, but requires somewhat
- // invasive changes.
- mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
- }
-
- for (const auto& style : mStyleStack) {
- for (const auto& clipOrTransform : style.clipsAndTransforms) {
- if (clipOrTransform.IsClip()) {
- mTarget->PushClip(clipOrTransform.clip);
- } else {
- mTarget->SetTransform(clipOrTransform.transform);
- }
- }
- }
-}
-
-CanvasRenderingContext2D::RenderingMode
-CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
- RenderingMode aRenderingMode)
-{
- if (AlreadyShutDown()) {
- gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
- SetErrorState();
- return aRenderingMode;
- }
-
- // This would make no sense, so make sure we don't get ourselves in a mess
- MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
-
- RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
-
- if (mTarget && mode == mRenderingMode) {
- return mRenderingMode;
- }
-
- // Check that the dimensions are sane
- if (mWidth > gfxPrefs::MaxCanvasSize() ||
- mHeight > gfxPrefs::MaxCanvasSize() ||
- mWidth < 0 || mHeight < 0) {
-
- SetErrorState();
- return aRenderingMode;
- }
-
- // If the next drawing command covers the entire canvas, we can skip copying
- // from the previous frame and/or clearing the canvas.
- gfx::Rect canvasRect(0, 0, mWidth, mHeight);
- bool canDiscardContent = aCoveredRect &&
- CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
-
- // If a clip is active we don't know for sure that the next drawing command
- // will really cover the entire canvas.
- for (const auto& style : mStyleStack) {
- if (!canDiscardContent) {
- break;
- }
- for (const auto& clipOrTransform : style.clipsAndTransforms) {
- if (clipOrTransform.IsClip()) {
- canDiscardContent = false;
- break;
- }
- }
- }
-
- ScheduleStableStateCallback();
-
- IntRect persistedRect = canDiscardContent ? IntRect()
- : IntRect(0, 0, mWidth, mHeight);
-
- if (mBufferProvider && mode == mRenderingMode) {
- mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
-
- if (mTarget && !mBufferProvider->PreservesDrawingState()) {
- RestoreClipsAndTransformToTarget();
- }
-
- if (mTarget) {
- return mode;
- }
- }
-
- RefPtr<DrawTarget> newTarget;
- RefPtr<PersistentBufferProvider> newProvider;
-
- if (mode == RenderingMode::OpenGLBackendMode &&
- !TrySkiaGLTarget(newTarget, newProvider)) {
- // Fall back to software.
- mode = RenderingMode::SoftwareBackendMode;
- }
-
- if (mode == RenderingMode::SoftwareBackendMode &&
- !TrySharedTarget(newTarget, newProvider) &&
- !TryBasicTarget(newTarget, newProvider)) {
-
- gfxCriticalError(
- CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))
- ) << "Failed borrow shared and basic targets.";
-
- SetErrorState();
- return mode;
- }
-
-
- MOZ_ASSERT(newTarget);
- MOZ_ASSERT(newProvider);
-
- bool needsClear = !canDiscardContent;
- if (newTarget->GetBackendType() == gfx::BackendType::SKIA) {
- // Skia expects the unused X channel to contains 0xFF even for opaque operations
- // so we can't skip clearing in that case, even if we are going to cover the
- // entire canvas in the next drawing operation.
- newTarget->ClearRect(canvasRect);
- needsClear = false;
- }
-
- // Try to copy data from the previous buffer provider if there is one.
- if (!canDiscardContent && mBufferProvider && CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
- needsClear = false;
- }
-
- if (needsClear) {
- newTarget->ClearRect(canvasRect);
- }
-
- mTarget = newTarget.forget();
- mBufferProvider = newProvider.forget();
-
- RegisterAllocation();
-
- RestoreClipsAndTransformToTarget();
-
- // Force a full layer transaction since we didn't have a layer before
- // and now we might need one.
- if (mCanvasElement) {
- mCanvasElement->InvalidateCanvas();
- }
- // Calling Redraw() tells our invalidation machinery that the entire
- // canvas is already invalid, which can speed up future drawing.
- Redraw();
-
- return mode;
-}
-
-void
-CanvasRenderingContext2D::SetInitialState()
-{
- // Set up the initial canvas defaults
- mPathBuilder = nullptr;
- mPath = nullptr;
- mDSPathBuilder = nullptr;
-
- mStyleStack.Clear();
- ContextState *state = mStyleStack.AppendElement();
- state->globalAlpha = 1.0;
-
- state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
- state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
- state->shadowColor = NS_RGBA(0,0,0,0);
-}
-
-void
-CanvasRenderingContext2D::SetErrorState()
-{
- EnsureErrorTarget();
-
- if (mTarget && mTarget != sErrorTarget) {
- gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
- }
-
- mTarget = sErrorTarget;
- mBufferProvider = nullptr;
-
- // clear transforms, clips, etc.
- SetInitialState();
-}
-
-void
-CanvasRenderingContext2D::RegisterAllocation()
-{
- // XXX - It would make more sense to track the allocation in
- // PeristentBufferProvider, rather than here.
- static bool registered = false;
- // FIXME: Disable the reporter for now, see bug 1241865
- if (!registered && false) {
- registered = true;
- RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
- }
-
- gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
- JSContext* context = nsContentUtils::GetCurrentJSContext();
- if (context) {
- JS_updateMallocCounter(context, mWidth * mHeight * 4);
- }
-
- JSObject* wrapper = GetWrapperPreserveColor();
- if (wrapper) {
- CycleCollectedJSContext::Get()->
- AddZoneWaitingForGC(JS::GetObjectZone(wrapper));
- }
-}
-
static already_AddRefed<LayerManager>
LayerManagerFromCanvasElement(nsINode* aCanvasElement)
{
if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
return nullptr;
}
return nsContentUtils::PersistentLayerManagerForDocument(aCanvasElement->OwnerDoc());
@@ -1228,30 +883,16 @@ CanvasRenderingContext2D::TrySharedTarge
// We can pass an empty persisted rect since we just created the buffer
// provider (nothing to restore).
aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
MOZ_ASSERT(aOutDT);
return !!aOutDT;
}
-bool
-CanvasRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
- RefPtr<layers::PersistentBufferProvider>& aOutProvider)
-{
- aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
- GetSurfaceFormat());
- if (!aOutDT) {
- return false;
- }
-
- aOutProvider = new PersistentBufferProviderBasic(aOutDT);
- return true;
-}
-
int32_t
CanvasRenderingContext2D::GetWidth() const
{
return mWidth;
}
int32_t
CanvasRenderingContext2D::GetHeight() const
@@ -1304,43 +945,16 @@ CanvasRenderingContext2D::ClearTarget()
if (wm.IsVertical() && !wm.IsSideways()) {
CurrentState().textBaseline = TextBaseline::MIDDLE;
}
}
}
}
}
-void
-CanvasRenderingContext2D::ReturnTarget(bool aForceReset)
-{
- if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
- CurrentState().transform = mTarget->GetTransform();
- if (aForceReset || !mBufferProvider->PreservesDrawingState()) {
- for (const auto& style : mStyleStack) {
- for (const auto& clipOrTransform : style.clipsAndTransforms) {
- if (clipOrTransform.IsClip()) {
- mTarget->PopClip();
- }
- }
- }
-
- if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
- // With the cairo backend we pushed an extra clip rect which we have to
- // balance out here. See the comment in RestoreClipsAndTransformToTarget.
- mTarget->PopClip();
- }
-
- mTarget->SetTransform(Matrix());
- }
-
- mBufferProvider->ReturnDrawTarget(mTarget.forget());
- }
-}
-
NS_IMETHODIMP
CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
NotNull<gfx::DrawTarget*> aTarget)
{
RemovePostRefreshObserver();
mDocShell = aShell;
AddPostRefreshObserverIfNecessary();
@@ -3263,30 +2877,16 @@ CanvasRenderingContext2D::GetImageDataAr
readback->Unmap();
}
*aRetval = darray;
return NS_OK;
}
void
-CanvasRenderingContext2D::EnsureErrorTarget()
-{
- if (sErrorTarget) {
- return;
- }
-
- RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
- MOZ_ASSERT(errorTarget, "Failed to allocate the error target!");
-
- sErrorTarget = errorTarget;
- NS_ADDREF(sErrorTarget);
-}
-
-void
CanvasRenderingContext2D::FillRuleChanged()
{
if (mPath) {
mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
mPath = nullptr;
}
}
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -42,25 +42,23 @@ class TextMetrics;
class CanvasFilterChainObserver;
class CanvasPath;
template<typename T> class Optional;
struct CanvasBidiProcessor;
class CanvasRenderingContext2DUserData;
class CanvasDrawObserver;
-class CanvasShutdownObserver;
/**
** CanvasRenderingContext2D
**/
class CanvasRenderingContext2D final :
public nsICanvasRenderingContextInternal,
- public BasicRenderingContext2D,
- public nsWrapperCache
+ public BasicRenderingContext2D
{
virtual ~CanvasRenderingContext2D();
public:
explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -159,17 +157,16 @@ public:
// Eventually this should be deprecated. Keeping for now to keep the binding functional.
void Demote();
virtual nsresult Redraw() override;
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
- gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
// nsICanvasRenderingContextInternal
/**
* Gets the pres shell from either the canvas element or the doc shell
*/
virtual nsIPresShell *GetPresShell() override {
if (mCanvasElement) {
return mCanvasElement->OwnerDoc()->GetShell();
@@ -248,138 +245,69 @@ public:
// Given a point, return hit region ID if it exists
nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
// return true and fills in the bound rect if element has a hit region.
bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
- void OnShutdown();
-
protected:
HTMLCanvasElement* GetCanvasElement() override { return mCanvasElement; }
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight,
JSObject** aRetval);
nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
dom::Uint8ClampedArray* aArray,
bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
int32_t aDirtyWidth, int32_t aDirtyHeight);
- bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
- gfx::DrawTarget& aTarget,
- gfx::IntRect aCopyRect);
-
/**
* Internal method to complete initialisation, expects mTarget to have been set
*/
nsresult Initialize(int32_t aWidth, int32_t aHeight);
nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface,
int32_t aWidth, int32_t aHeight);
- /**
- * The number of living nsCanvasRenderingContexts. When this goes down to
- * 0, we free the premultiply and unpremultiply tables, if they exist.
- */
- static uint32_t sNumLivingContexts;
-
- static mozilla::gfx::DrawTarget* sErrorTarget;
-
// Some helpers. Doesn't modify a color on failure.
void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
Style aWhichStyle);
// Returns whether a color was successfully parsed.
virtual bool ParseColor(const nsAString& aString, nscolor* aColor) override;
// Returns whether a filter was successfully parsed.
bool ParseFilter(const nsAString& aString,
nsTArray<nsStyleFilter>& aFilterChain,
ErrorResult& aError);
// Returns whether the font was successfully updated.
bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
-
- /**
- * Creates the error target, if it doesn't exist
- */
- static void EnsureErrorTarget();
-
// Report the fillRule has changed.
void FillRuleChanged();
- /**
- * Create the backing surfacing, if it doesn't exist. If there is an error
- * in creating the target then it will put sErrorTarget in place. If there
- * is in turn an error in creating the sErrorTarget then they would both
- * be null so IsTargetValid() would still return null.
- *
- * Returns the actual rendering mode being used by the created target.
- */
- virtual RenderingMode
- EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
- RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) override;
-
- void RestoreClipsAndTransformToTarget();
-
bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
- RefPtr<layers::PersistentBufferProvider>& aOutProvider);
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider) override;
bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
- RefPtr<layers::PersistentBufferProvider>& aOutProvider);
-
- bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
- RefPtr<layers::PersistentBufferProvider>& aOutProvider);
-
- void RegisterAllocation();
-
- void SetInitialState();
-
- void SetErrorState();
-
- /**
- * This method is run at the end of the event-loop spin where
- * ScheduleStableStateCallback was called.
- *
- * We use it to unlock resources that need to be locked while drawing.
- */
- void OnStableState();
-
- /**
- * Cf. OnStableState.
- */
- void ScheduleStableStateCallback();
+ RefPtr<layers::PersistentBufferProvider>& aOutProvider) override;
/**
* Disposes an old target and prepares to lazily create a new target.
*/
void ClearTarget();
- /*
- * Returns the target to the buffer provider. i.e. this will queue a frame for
- * rendering.
- */
- void ReturnTarget(bool aForceReset = false);
-
- /**
- * Check if the target is valid after calling EnsureTarget.
- */
- bool IsTargetValid() const override{
- return !!mTarget && mTarget != sErrorTarget;
- }
-
/**
* Returns the surface format this canvas should be allocated using. Takes
* into account mOpaque, platform requirements, etc.
*/
- mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
+ mozilla::gfx::SurfaceFormat GetSurfaceFormat() const override;
/**
* Update CurrentState().filter with the filter description for
* CurrentState().filterChain.
* Flushes the PresShell, so the world can change if you call this function.
*/
void UpdateFilter();
@@ -409,38 +337,30 @@ protected:
bool mOpaque;
// This is true when the next time our layer is retrieved we need to
// recreate it (i.e. our backing surface changed)
bool mResetLayer;
// This is needed for drawing in drawAsyncXULElement
bool mIPC;
- bool mHasPendingStableStateCallback;
-
nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
// If mCanvasElement is not provided, then a docshell is
nsCOMPtr<nsIDocShell> mDocShell;
- RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
-
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();
void DidImageDrawCall() override;
- RefPtr<CanvasShutdownObserver> mShutdownObserver;
- void RemoveShutdownObserver();
- virtual bool AlreadyShutDown() const override{ return !mShutdownObserver; }
-
/**
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
* Redraw is called, reset to false when Render is called.
*/
bool mIsEntireFrameInvalid;
/**
* When this is set, the first call to Redraw(gfxRect) should set
* mIsEntireFrameInvalid since we expect it will be followed by