Reland "Avoid crash when surface CoW allocation fails"

On low memory machines a common cause of crashes is when a draw
operation requires a large layer (or many large layers) because an
implementation of SkSurface_Base::onCopyOnWrite does not actually have
the resources available to do the copy when required by a draw. Allow
this method to fail and percolate up the call chain so that the draw
simply does not happen instead of crashing.

It appears some users are creating their own subclasses of
SkSurface_Base for their tests. Add SK_SURFACE_COPY_ON_WRITE_CRASHES to
keep the old crashy behavior until they can be updated.

Bug: chromium:1116362
Revert: 5b19ebe0c5.
Revert-Change-Id: I2873589f996ded9c9fd6d27b19155ca18d5b5326
Revert-Reviewed-on: https://skia-review.googlesource.com/c/skia/+/463956
Change-Id: Id06d1e6d3aacb409a3b00b9a862bd8ddd1aaa22f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/464456
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2021-10-27 23:39:31 -04:00 committed by SkCQ
parent f2d016f12e
commit 4b38d9c437
10 changed files with 262 additions and 102 deletions

View File

@ -36,6 +36,10 @@ sk_sp<SkImage> Surface_Graphite::onNewImageSnapshot(const SkIRect* subset) {
void Surface_Graphite::onWritePixels(const SkPixmap&, int x, int y) {} void Surface_Graphite::onWritePixels(const SkPixmap&, int x, int y) {}
void Surface_Graphite::onCopyOnWrite(ContentChangeMode) {} #ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void Surface_Graphite::onCopyOnWrite(ContentChangeMode) { }
#else
bool Surface_Graphite::onCopyOnWrite(ContentChangeMode) { return true; }
#endif
} // namespace skgpu } // namespace skgpu

View File

@ -23,7 +23,11 @@ public:
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override; sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override;
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override; sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override;
void onWritePixels(const SkPixmap&, int x, int y) override; void onWritePixels(const SkPixmap&, int x, int y) override;
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override; void onCopyOnWrite(ContentChangeMode) override;
#else
bool onCopyOnWrite(ContentChangeMode) override;
#endif
private: private:
sk_sp<Device> fDevice; sk_sp<Device> fDevice;

View File

@ -37,6 +37,7 @@
#define SK_SUPPORT_LEGACY_GETTOTALMATRIX #define SK_SUPPORT_LEGACY_GETTOTALMATRIX
#endif #endif
class AutoLayerForImageFilter;
class GrBackendRenderTarget; class GrBackendRenderTarget;
class GrRecordingContext; class GrRecordingContext;
class SkBaseDevice; class SkBaseDevice;
@ -63,6 +64,10 @@ class SkSurface_Base;
class SkTextBlob; class SkTextBlob;
class SkVertices; class SkVertices;
namespace skstd {
template<typename T> class optional;
}
/** \class SkCanvas /** \class SkCanvas
SkCanvas provides an interface for drawing, and how the drawing is clipped and transformed. SkCanvas provides an interface for drawing, and how the drawing is clipped and transformed.
SkCanvas contains a stack of SkMatrix and clip values. SkCanvas contains a stack of SkMatrix and clip values.
@ -2283,8 +2288,21 @@ private:
// notify our surface (if we have one) that we are about to draw, so it // notify our surface (if we have one) that we are about to draw, so it
// can perform copy-on-write or invalidate any cached images // can perform copy-on-write or invalidate any cached images
void predrawNotify(bool willOverwritesEntireSurface = false); // returns false if the copy failed
void predrawNotify(const SkRect* rect, const SkPaint* paint, ShaderOverrideOpacity); bool SK_WARN_UNUSED_RESULT predrawNotify(bool willOverwritesEntireSurface = false);
bool SK_WARN_UNUSED_RESULT predrawNotify(const SkRect*, const SkPaint*, ShaderOverrideOpacity);
enum class CheckForOverwrite : bool {
kNo = false,
kYes = true
};
// call the appropriate predrawNotify and create a layer if needed.
skstd::optional<AutoLayerForImageFilter> aboutToDraw(
SkCanvas* canvas,
const SkPaint& paint,
const SkRect* rawBounds = nullptr,
CheckForOverwrite = CheckForOverwrite::kNo,
ShaderOverrideOpacity = kNone_ShaderOverrideOpacity);
// The bottom-most device in the stack, only changed by init(). Image properties and the final // The bottom-most device in the stack, only changed by init(). Image properties and the final
// canvas pixels are determined by this device. // canvas pixels are determined by this device.

View File

@ -19,6 +19,7 @@
#include "include/core/SkTextBlob.h" #include "include/core/SkTextBlob.h"
#include "include/core/SkVertices.h" #include "include/core/SkVertices.h"
#include "include/effects/SkRuntimeEffect.h" #include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkTOptional.h"
#include "include/private/SkTo.h" #include "include/private/SkTo.h"
#include "include/utils/SkNoDrawCanvas.h" #include "include/utils/SkNoDrawCanvas.h"
#include "src/core/SkArenaAlloc.h" #include "src/core/SkArenaAlloc.h"
@ -155,15 +156,18 @@ bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* pa
#define dec_canvas() #define dec_canvas()
#endif #endif
void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) { bool SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
if (fSurfaceBase) { if (fSurfaceBase) {
fSurfaceBase->aboutToDraw(willOverwritesEntireSurface if (!fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
? SkSurface::kDiscard_ContentChangeMode ? SkSurface::kDiscard_ContentChangeMode
: SkSurface::kRetain_ContentChangeMode); : SkSurface::kRetain_ContentChangeMode)) {
return false;
}
} }
return true;
} }
void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint, bool SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
ShaderOverrideOpacity overrideOpacity) { ShaderOverrideOpacity overrideOpacity) {
if (fSurfaceBase) { if (fSurfaceBase) {
SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode; SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
@ -176,21 +180,15 @@ void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
mode = SkSurface::kDiscard_ContentChangeMode; mode = SkSurface::kDiscard_ContentChangeMode;
} }
} }
fSurfaceBase->aboutToDraw(mode); if (!fSurfaceBase->aboutToDraw(mode)) {
return false;
}
} }
return true;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
namespace {
enum class CheckForOverwrite : bool {
kNo = false,
kYes = true
};
} // namespace
SkCanvas::Layer::Layer(sk_sp<SkBaseDevice> device, SkCanvas::Layer::Layer(sk_sp<SkBaseDevice> device,
sk_sp<SkImageFilter> imageFilter, sk_sp<SkImageFilter> imageFilter,
const SkPaint& paint) const SkPaint& paint)
@ -304,21 +302,12 @@ public:
// Draw functions should use layer->paint() instead of the passed-in paint. // Draw functions should use layer->paint() instead of the passed-in paint.
AutoLayerForImageFilter(SkCanvas* canvas, AutoLayerForImageFilter(SkCanvas* canvas,
const SkPaint& paint, const SkPaint& paint,
const SkRect* rawBounds = nullptr, const SkRect* rawBounds = nullptr)
CheckForOverwrite checkOverwrite = CheckForOverwrite::kNo,
SkCanvas::ShaderOverrideOpacity overrideOpacity =
SkCanvas::kNone_ShaderOverrideOpacity)
: fPaint(paint) : fPaint(paint)
, fCanvas(canvas) , fCanvas(canvas)
, fTempLayerForImageFilter(false) { , fTempLayerForImageFilter(false) {
SkDEBUGCODE(fSaveCount = canvas->getSaveCount();) SkDEBUGCODE(fSaveCount = canvas->getSaveCount();)
if (checkOverwrite == CheckForOverwrite::kYes) {
canvas->predrawNotify(rawBounds, &fPaint, overrideOpacity);
} else {
canvas->predrawNotify();
}
if (fPaint.getImageFilter() && !image_to_color_filter(&fPaint)) { if (fPaint.getImageFilter() && !image_to_color_filter(&fPaint)) {
// The draw paint has an image filter that couldn't be simplified to an equivalent // The draw paint has an image filter that couldn't be simplified to an equivalent
// color filter, so we have to inject an automatic saveLayer(). // color filter, so we have to inject an automatic saveLayer().
@ -344,6 +333,11 @@ public:
} }
} }
AutoLayerForImageFilter(const AutoLayerForImageFilter&) = delete;
AutoLayerForImageFilter& operator=(const AutoLayerForImageFilter&) = delete;
AutoLayerForImageFilter(AutoLayerForImageFilter&&) = default;
AutoLayerForImageFilter& operator=(AutoLayerForImageFilter&&) = default;
~AutoLayerForImageFilter() { ~AutoLayerForImageFilter() {
if (fTempLayerForImageFilter) { if (fTempLayerForImageFilter) {
fCanvas->internalRestore(); fCanvas->internalRestore();
@ -361,6 +355,25 @@ private:
SkDEBUGCODE(int fSaveCount;) SkDEBUGCODE(int fSaveCount;)
}; };
skstd::optional<AutoLayerForImageFilter> SkCanvas::aboutToDraw(
SkCanvas* canvas,
const SkPaint& paint,
const SkRect* rawBounds,
CheckForOverwrite checkOverwrite,
ShaderOverrideOpacity overrideOpacity)
{
if (checkOverwrite == CheckForOverwrite::kYes) {
if (!this->predrawNotify(rawBounds, &paint, overrideOpacity)) {
return skstd::nullopt;
}
} else {
if (!this->predrawNotify()) {
return skstd::nullopt;
}
}
return skstd::optional<AutoLayerForImageFilter>(canvas, paint, rawBounds);
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
void SkCanvas::resetForNextPicture(const SkIRect& bounds) { void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
@ -545,9 +558,10 @@ bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_
} }
// Tell our owning surface to bump its generation ID. // Tell our owning surface to bump its generation ID.
const bool completeOverwrite = const bool completeOverwrite = srcRect.size() == device->imageInfo().dimensions();
srcRect.size() == SkISize::Make(device->width(), device->height()); if (!this->predrawNotify(completeOverwrite)) {
this->predrawNotify(completeOverwrite); return false;
}
// This can still fail, most notably in the case of a invalid color type or alpha type // This can still fail, most notably in the case of a invalid color type or alpha type
// conversion. We could pull those checks into this function and avoid the unnecessary // conversion. We could pull those checks into this function and avoid the unnecessary
@ -1210,21 +1224,21 @@ void SkCanvas::internalRestore() {
// Don't go through AutoLayerForImageFilter since device draws are so closely tied to // Don't go through AutoLayerForImageFilter since device draws are so closely tied to
// internalSaveLayer and internalRestore. // internalSaveLayer and internalRestore.
this->predrawNotify(); if (this->predrawNotify()) {
SkBaseDevice* dstDev = this->topDevice();
SkBaseDevice* dstDev = this->topDevice(); if (layer->fImageFilter) {
if (layer->fImageFilter) { this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src
this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src dstDev, // dst
dstDev, // dst layer->fImageFilter.get(),
layer->fImageFilter.get(), layer->fPaint,
layer->fPaint, DeviceCompatibleWithFilter::kYes);
DeviceCompatibleWithFilter::kYes); } else {
} else { // NOTE: We don't just call internalDrawDeviceWithFilter with a null filter
// NOTE: We don't just call internalDrawDeviceWithFilter with a null filter // because we want to take advantage of overridden drawDevice functions for
// because we want to take advantage of overridden drawDevice functions for // document-based devices.
// document-based devices. SkSamplingOptions sampling;
SkSamplingOptions sampling; dstDev->drawDevice(layer->fDevice.get(), sampling, layer->fPaint);
dstDev->drawDevice(layer->fDevice.get(), sampling, layer->fPaint); }
} }
} }
@ -1895,7 +1909,9 @@ void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec
void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
// We don't test quickReject because the shadow outsets the path's bounds. // We don't test quickReject because the shadow outsets the path's bounds.
// TODO(michaelludwig): Is it worth calling SkDrawShadowMetrics::GetLocalBounds here? // TODO(michaelludwig): Is it worth calling SkDrawShadowMetrics::GetLocalBounds here?
this->predrawNotify(); if (!this->predrawNotify()) {
return;
}
this->topDevice()->drawShadow(path, rec); this->topDevice()->drawShadow(path, rec);
} }
@ -1924,7 +1940,7 @@ void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], i
void SkCanvas::onDiscard() { void SkCanvas::onDiscard() {
if (fSurfaceBase) { if (fSurfaceBase) {
fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode); sk_ignore_unused_variable(fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode));
} }
} }
@ -1939,8 +1955,10 @@ void SkCanvas::internalDrawPaint(const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, nullptr, CheckForOverwrite::kYes); auto layer = this->aboutToDraw(this, paint, nullptr, CheckForOverwrite::kYes);
this->topDevice()->drawPaint(layer.paint()); if (layer) {
this->topDevice()->drawPaint(layer->paint());
}
} }
void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
@ -1965,8 +1983,10 @@ void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
return; return;
} }
AutoLayerForImageFilter layer(this, strokePaint, &bounds); auto layer = this->aboutToDraw(this, strokePaint, &bounds);
this->topDevice()->drawPoints(mode, count, pts, layer.paint()); if (layer) {
this->topDevice()->drawPoints(mode, count, pts, layer->paint());
}
} }
void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
@ -1975,8 +1995,10 @@ void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &r, CheckForOverwrite::kYes); auto layer = this->aboutToDraw(this, paint, &r, CheckForOverwrite::kYes);
this->topDevice()->drawRect(r, layer.paint()); if (layer) {
this->topDevice()->drawRect(r, layer->paint());
}
} }
void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
@ -1985,8 +2007,10 @@ void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &bounds); auto layer = this->aboutToDraw(this, paint, &bounds);
this->topDevice()->drawRegion(region, layer.paint()); if (layer) {
this->topDevice()->drawRegion(region, layer->paint());
}
} }
void SkCanvas::onDrawBehind(const SkPaint& paint) { void SkCanvas::onDrawBehind(const SkPaint& paint) {
@ -2025,8 +2049,10 @@ void SkCanvas::onDrawBehind(const SkPaint& paint) {
// ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly. // ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly.
} }
AutoLayerForImageFilter layer(this, paint); auto layer = this->aboutToDraw(this, paint);
this->topDevice()->drawPaint(layer.paint()); if (layer) {
this->topDevice()->drawPaint(layer->paint());
}
dev->restore(fMCRec->fMatrix); dev->restore(fMCRec->fMatrix);
} }
@ -2037,8 +2063,10 @@ void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &oval); auto layer = this->aboutToDraw(this, paint, &oval);
this->topDevice()->drawOval(oval, layer.paint()); if (layer) {
this->topDevice()->drawOval(oval, layer->paint());
}
} }
void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
@ -2049,8 +2077,10 @@ void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &oval); auto layer = this->aboutToDraw(this, paint, &oval);
this->topDevice()->drawArc(oval, startAngle, sweepAngle, useCenter, layer.paint()); if (layer) {
this->topDevice()->drawArc(oval, startAngle, sweepAngle, useCenter, layer->paint());
}
} }
void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
@ -2071,8 +2101,10 @@ void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &bounds); auto layer = this->aboutToDraw(this, paint, &bounds);
this->topDevice()->drawRRect(rrect, layer.paint()); if (layer) {
this->topDevice()->drawRRect(rrect, layer->paint());
}
} }
void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
@ -2081,8 +2113,10 @@ void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const Sk
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &bounds); auto layer = this->aboutToDraw(this, paint, &bounds);
this->topDevice()->drawDRRect(outer, inner, layer.paint()); if (layer) {
this->topDevice()->drawDRRect(outer, inner, layer->paint());
}
} }
void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
@ -2099,8 +2133,10 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &pathBounds); auto layer = this->aboutToDraw(this, paint, &pathBounds);
this->topDevice()->drawPath(path, layer.paint()); if (layer) {
this->topDevice()->drawPath(path, layer->paint());
}
} }
bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h,
@ -2183,15 +2219,18 @@ void SkCanvas::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
layerToDevice.preTranslate(x, y); layerToDevice.preTranslate(x, y);
skif::Mapping mapping(layerToDevice, SkMatrix::Translate(-x, -y)); skif::Mapping mapping(layerToDevice, SkMatrix::Translate(-x, -y));
this->predrawNotify(); if (this->predrawNotify()) {
device->drawFilteredImage(mapping, special.get(), filter.get(), sampling, realPaint); device->drawFilteredImage(mapping, special.get(), filter.get(), sampling,realPaint);
}
return; return;
} // else fall through to regular drawing path } // else fall through to regular drawing path
} }
AutoLayerForImageFilter layer(this, realPaint, &bounds); auto layer = this->aboutToDraw(this, realPaint, &bounds);
this->topDevice()->drawImageRect(image, nullptr, bounds, sampling, if (layer) {
layer.paint(), kStrict_SrcRectConstraint); this->topDevice()->drawImageRect(image, nullptr, bounds, sampling,
layer->paint(), kStrict_SrcRectConstraint);
}
} }
void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst, void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
@ -2203,10 +2242,12 @@ void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const S
return; return;
} }
AutoLayerForImageFilter layer(this, realPaint, &dst, CheckForOverwrite::kYes, auto layer = this->aboutToDraw(this, realPaint, &dst, CheckForOverwrite::kYes,
image->isOpaque() ? kOpaque_ShaderOverrideOpacity image->isOpaque() ? kOpaque_ShaderOverrideOpacity
: kNotOpaque_ShaderOverrideOpacity); : kNotOpaque_ShaderOverrideOpacity);
this->topDevice()->drawImageRect(image, &src, dst, sampling, layer.paint(), constraint); if (layer) {
this->topDevice()->drawImageRect(image, &src, dst, sampling, layer->paint(), constraint);
}
} }
void SkCanvas::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, const SkRect& dst, void SkCanvas::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, const SkRect& dst,
@ -2217,8 +2258,10 @@ void SkCanvas::onDrawImageLattice2(const SkImage* image, const Lattice& lattice,
return; return;
} }
AutoLayerForImageFilter layer(this, realPaint, &dst); auto layer = this->aboutToDraw(this, realPaint, &dst);
this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer.paint()); if (layer) {
this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer->paint());
}
} }
void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y,
@ -2256,8 +2299,10 @@ void SkCanvas::onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPa
if (this->internalQuickReject(bounds, paint)) { if (this->internalQuickReject(bounds, paint)) {
return; return;
} }
AutoLayerForImageFilter layer(this, paint, &bounds); auto layer = this->aboutToDraw(this, paint, &bounds);
this->topDevice()->drawGlyphRunList(glyphRunList, layer.paint()); if (layer) {
this->topDevice()->drawGlyphRunList(glyphRunList, layer->paint());
}
} }
// These call the (virtual) onDraw... method // These call the (virtual) onDraw... method
@ -2369,8 +2414,10 @@ void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmod
return; return;
} }
AutoLayerForImageFilter layer(this, simplePaint, &bounds); auto layer = this->aboutToDraw(this, simplePaint, &bounds);
this->topDevice()->drawVertices(vertices, bmode, layer.paint()); if (layer) {
this->topDevice()->drawVertices(vertices, bmode, layer->paint());
}
} }
void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4], void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
@ -2398,8 +2445,10 @@ void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
return; return;
} }
AutoLayerForImageFilter layer(this, simplePaint, &bounds); auto layer = this->aboutToDraw(this, simplePaint, &bounds);
this->topDevice()->drawPatch(cubics, colors, texCoords, bmode, layer.paint()); if (layer) {
this->topDevice()->drawPatch(cubics, colors, texCoords, bmode, layer->paint());
}
} }
void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) { void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
@ -2429,8 +2478,9 @@ void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) { void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
// drawable bounds are no longer reliable (e.g. android displaylist) // drawable bounds are no longer reliable (e.g. android displaylist)
// so don't use them for quick-reject // so don't use them for quick-reject
this->predrawNotify(); if (this->predrawNotify()) {
this->topDevice()->drawDrawable(dr, matrix, this); this->topDevice()->drawDrawable(dr, matrix, this);
}
} }
void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
@ -2445,15 +2495,18 @@ void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const
return; return;
} }
AutoLayerForImageFilter layer(this, realPaint); auto layer = this->aboutToDraw(this, realPaint);
this->topDevice()->drawAtlas(xform, tex, colors, count, bmode, layer.paint()); if (layer) {
this->topDevice()->drawAtlas(xform, tex, colors, count, bmode, layer->paint());
}
} }
void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
SkASSERT(key); SkASSERT(key);
this->predrawNotify(); if (this->predrawNotify()) {
this->topDevice()->drawAnnotation(rect, key, value); this->topDevice()->drawAnnotation(rect, key, value);
}
} }
void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA, void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
@ -2466,8 +2519,9 @@ void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFl
return; return;
} }
this->predrawNotify(); if (this->predrawNotify()) {
this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode); this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
}
} }
void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count, void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
@ -2507,9 +2561,11 @@ void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
return; return;
} }
AutoLayerForImageFilter layer(this, realPaint, setBoundsValid ? &setBounds : nullptr); auto layer = this->aboutToDraw(this, realPaint, setBoundsValid ? &setBounds : nullptr);
this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices, sampling, if (layer) {
layer.paint(), constraint); this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices, sampling,
layer->paint(), constraint);
}
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -102,7 +102,7 @@ private:
size_t addDraw(DrawType drawType, size_t* size) { size_t addDraw(DrawType drawType, size_t* size) {
size_t offset = fWriter.bytesWritten(); size_t offset = fWriter.bytesWritten();
this->predrawNotify(); SkASSERT_RELEASE(this->predrawNotify());
SkASSERT(0 != *size); SkASSERT(0 != *size);
SkASSERT(((uint8_t) drawType) == drawType); SkASSERT(((uint8_t) drawType) == drawType);

View File

@ -117,7 +117,7 @@ bool SkSurface_Base::outstandingImageSnapshot() const {
return fCachedImage && !fCachedImage->unique(); return fCachedImage && !fCachedImage->unique();
} }
void SkSurface_Base::aboutToDraw(ContentChangeMode mode) { bool SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
this->dirtyGenerationID(); this->dirtyGenerationID();
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this); SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
@ -128,7 +128,13 @@ void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
// on the image (besides us). // on the image (besides us).
bool unique = fCachedImage->unique(); bool unique = fCachedImage->unique();
if (!unique) { if (!unique) {
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
this->onCopyOnWrite(mode); this->onCopyOnWrite(mode);
#else
if (!this->onCopyOnWrite(mode)) {
return false;
}
#endif
} }
// regardless of copy-on-write, we must drop our cached image now, so // regardless of copy-on-write, we must drop our cached image now, so
@ -144,6 +150,7 @@ void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
} else if (kDiscard_ContentChangeMode == mode) { } else if (kDiscard_ContentChangeMode == mode) {
this->onDiscard(); this->onDiscard();
} }
return true;
} }
uint32_t SkSurface_Base::newGenerationID() { uint32_t SkSurface_Base::newGenerationID() {
@ -191,7 +198,7 @@ uint32_t SkSurface::generationID() {
} }
void SkSurface::notifyContentWillChange(ContentChangeMode mode) { void SkSurface::notifyContentWillChange(ContentChangeMode mode) {
asSB(this)->aboutToDraw(mode); sk_ignore_unused_variable(asSB(this)->aboutToDraw(mode));
} }
SkCanvas* SkSurface::getCanvas() { SkCanvas* SkSurface::getCanvas() {
@ -297,7 +304,9 @@ void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {
if (srcR.contains(dstR)) { if (srcR.contains(dstR)) {
mode = kDiscard_ContentChangeMode; mode = kDiscard_ContentChangeMode;
} }
asSB(this)->aboutToDraw(mode); if (!asSB(this)->aboutToDraw(mode)) {
return;
}
asSB(this)->onWritePixels(pmap, x, y); asSB(this)->onWritePixels(pmap, x, y);
} }
} }
@ -386,7 +395,11 @@ protected:
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subsetOrNull) override { return nullptr; } sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subsetOrNull) override { return nullptr; }
void onWritePixels(const SkPixmap&, int x, int y) override {} void onWritePixels(const SkPixmap&, int x, int y) override {}
void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override {} void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override {}
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override {} void onCopyOnWrite(ContentChangeMode) override {}
#else
bool onCopyOnWrite(ContentChangeMode) override { return true; }
#endif
}; };
sk_sp<SkSurface> SkSurface::MakeNull(int width, int height) { sk_sp<SkSurface> SkSurface::MakeNull(int width, int height) {

View File

@ -107,8 +107,14 @@ public:
* If the surface is about to change, we call this so that our subclass * If the surface is about to change, we call this so that our subclass
* can optionally fork their backend (copy-on-write) in case it was * can optionally fork their backend (copy-on-write) in case it was
* being shared with the cachedImage. * being shared with the cachedImage.
*
* Returns false if the backing cannot be un-shared.
*/ */
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
virtual void onCopyOnWrite(ContentChangeMode) = 0; virtual void onCopyOnWrite(ContentChangeMode) = 0;
#else
virtual bool SK_WARN_UNUSED_RESULT onCopyOnWrite(ContentChangeMode) = 0;
#endif
/** /**
* Signal the surface to remind its backing store that it's mutable again. * Signal the surface to remind its backing store that it's mutable again.
@ -144,7 +150,8 @@ private:
std::unique_ptr<SkCanvas> fCachedCanvas; std::unique_ptr<SkCanvas> fCachedCanvas;
sk_sp<SkImage> fCachedImage; sk_sp<SkImage> fCachedImage;
void aboutToDraw(ContentChangeMode mode); // Returns false if drawing should not take place (allocation failure).
bool SK_WARN_UNUSED_RESULT aboutToDraw(ContentChangeMode mode);
// Returns true if there is an outstanding image-snapshot, indicating that a call to aboutToDraw // Returns true if there is an outstanding image-snapshot, indicating that a call to aboutToDraw
// would trigger a copy-on-write. // would trigger a copy-on-write.

View File

@ -187,6 +187,7 @@ void SkSurface_Gpu::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSp
// Create a new render target and, if necessary, copy the contents of the old // Create a new render target and, if necessary, copy the contents of the old
// render target into it. Note that this flushes the SkGpuDevice but // render target into it. Note that this flushes the SkGpuDevice but
// doesn't force an OpenGL flush. // doesn't force an OpenGL flush.
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) { void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView(); GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView();
@ -201,6 +202,25 @@ void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
this->SkSurface_Gpu::onDiscard(); this->SkSurface_Gpu::onDiscard();
} }
} }
#else
bool SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView();
// are we sharing our backing proxy with the image? Note this call should never create a new
// image because onCopyOnWrite is only called when there is a cached image.
sk_sp<SkImage> image = this->refCachedImage();
SkASSERT(image);
if (static_cast<SkImage_Gpu*>(image.get())->surfaceMustCopyOnWrite(readSurfaceView.proxy())) {
if (!fDevice->replaceBackingProxy(mode)) {
return false;
}
} else if (kDiscard_ContentChangeMode == mode) {
this->SkSurface_Gpu::onDiscard();
}
return true;
}
#endif
void SkSurface_Gpu::onDiscard() { fDevice->discard(); } void SkSurface_Gpu::onDiscard() { fDevice->discard(); }

View File

@ -44,8 +44,11 @@ public:
RescaleMode, RescaleMode,
ReadPixelsCallback callback, ReadPixelsCallback callback,
ReadPixelsContext context) override; ReadPixelsContext context) override;
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override; void onCopyOnWrite(ContentChangeMode) override;
#else
bool onCopyOnWrite(ContentChangeMode) override;
#endif
void onDiscard() override; void onDiscard() override;
GrSemaphoresSubmitted onFlush(BackendSurfaceAccess access, const GrFlushInfo& info, GrSemaphoresSubmitted onFlush(BackendSurfaceAccess access, const GrFlushInfo& info,
const GrBackendSurfaceMutableState*) override; const GrBackendSurfaceMutableState*) override;

View File

@ -24,7 +24,11 @@ public:
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override; sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override;
void onWritePixels(const SkPixmap&, int x, int y) override; void onWritePixels(const SkPixmap&, int x, int y) override;
void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override; void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override;
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override; void onCopyOnWrite(ContentChangeMode) override;
#else
bool onCopyOnWrite(ContentChangeMode) override;
#endif
void onRestoreBackingMutability() override; void onRestoreBackingMutability() override;
private: private:
@ -124,6 +128,7 @@ void SkSurface_Raster::onRestoreBackingMutability() {
} }
} }
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) { void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
// are we sharing pixelrefs with the image? // are we sharing pixelrefs with the image?
sk_sp<SkImage> cached(this->refCachedImage()); sk_sp<SkImage> cached(this->refCachedImage());
@ -147,6 +152,36 @@ void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
this->getCachedCanvas()->baseDevice()->replaceBitmapBackendForRasterSurface(fBitmap); this->getCachedCanvas()->baseDevice()->replaceBitmapBackendForRasterSurface(fBitmap);
} }
} }
#else
bool SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
// are we sharing pixelrefs with the image?
sk_sp<SkImage> cached(this->refCachedImage());
SkASSERT(cached);
if (SkBitmapImageGetPixelRef(cached.get()) == fBitmap.pixelRef()) {
SkASSERT(fWeOwnThePixels);
if (kDiscard_ContentChangeMode == mode) {
if (!fBitmap.tryAllocPixels()) {
return false;
}
} else {
SkBitmap prev(fBitmap);
if (!fBitmap.tryAllocPixels()) {
return false;
}
SkASSERT(prev.info() == fBitmap.info());
SkASSERT(prev.rowBytes() == fBitmap.rowBytes());
memcpy(fBitmap.getPixels(), prev.getPixels(), fBitmap.computeByteSize());
}
// Now fBitmap is a deep copy of itself (and therefore different from
// what is being used by the image. Next we update the canvas to use
// this as its backend, so we can't modify the image's pixels anymore.
SkASSERT(this->getCachedCanvas());
this->getCachedCanvas()->baseDevice()->replaceBitmapBackendForRasterSurface(fBitmap);
}
return true;
}
#endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////