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::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

View File

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

View File

@ -37,6 +37,7 @@
#define SK_SUPPORT_LEGACY_GETTOTALMATRIX
#endif
class AutoLayerForImageFilter;
class GrBackendRenderTarget;
class GrRecordingContext;
class SkBaseDevice;
@ -63,6 +64,10 @@ class SkSurface_Base;
class SkTextBlob;
class SkVertices;
namespace skstd {
template<typename T> class optional;
}
/** \class SkCanvas
SkCanvas provides an interface for drawing, and how the drawing is clipped and transformed.
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
// can perform copy-on-write or invalidate any cached images
void predrawNotify(bool willOverwritesEntireSurface = false);
void predrawNotify(const SkRect* rect, const SkPaint* paint, ShaderOverrideOpacity);
// returns false if the copy failed
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
// canvas pixels are determined by this device.

View File

@ -19,6 +19,7 @@
#include "include/core/SkTextBlob.h"
#include "include/core/SkVertices.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkTOptional.h"
#include "include/private/SkTo.h"
#include "include/utils/SkNoDrawCanvas.h"
#include "src/core/SkArenaAlloc.h"
@ -155,15 +156,18 @@ bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* pa
#define dec_canvas()
#endif
void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
bool SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
if (fSurfaceBase) {
fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
? SkSurface::kDiscard_ContentChangeMode
: SkSurface::kRetain_ContentChangeMode);
if (!fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
? SkSurface::kDiscard_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) {
if (fSurfaceBase) {
SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
@ -176,21 +180,15 @@ void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
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,
sk_sp<SkImageFilter> imageFilter,
const SkPaint& paint)
@ -304,21 +302,12 @@ public:
// Draw functions should use layer->paint() instead of the passed-in paint.
AutoLayerForImageFilter(SkCanvas* canvas,
const SkPaint& paint,
const SkRect* rawBounds = nullptr,
CheckForOverwrite checkOverwrite = CheckForOverwrite::kNo,
SkCanvas::ShaderOverrideOpacity overrideOpacity =
SkCanvas::kNone_ShaderOverrideOpacity)
const SkRect* rawBounds = nullptr)
: fPaint(paint)
, fCanvas(canvas)
, fTempLayerForImageFilter(false) {
SkDEBUGCODE(fSaveCount = canvas->getSaveCount();)
if (checkOverwrite == CheckForOverwrite::kYes) {
canvas->predrawNotify(rawBounds, &fPaint, overrideOpacity);
} else {
canvas->predrawNotify();
}
if (fPaint.getImageFilter() && !image_to_color_filter(&fPaint)) {
// 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().
@ -344,6 +333,11 @@ public:
}
}
AutoLayerForImageFilter(const AutoLayerForImageFilter&) = delete;
AutoLayerForImageFilter& operator=(const AutoLayerForImageFilter&) = delete;
AutoLayerForImageFilter(AutoLayerForImageFilter&&) = default;
AutoLayerForImageFilter& operator=(AutoLayerForImageFilter&&) = default;
~AutoLayerForImageFilter() {
if (fTempLayerForImageFilter) {
fCanvas->internalRestore();
@ -361,6 +355,25 @@ private:
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) {
@ -545,9 +558,10 @@ bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_
}
// Tell our owning surface to bump its generation ID.
const bool completeOverwrite =
srcRect.size() == SkISize::Make(device->width(), device->height());
this->predrawNotify(completeOverwrite);
const bool completeOverwrite = srcRect.size() == device->imageInfo().dimensions();
if (!this->predrawNotify(completeOverwrite)) {
return false;
}
// 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
@ -1210,21 +1224,21 @@ void SkCanvas::internalRestore() {
// Don't go through AutoLayerForImageFilter since device draws are so closely tied to
// internalSaveLayer and internalRestore.
this->predrawNotify();
SkBaseDevice* dstDev = this->topDevice();
if (layer->fImageFilter) {
this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src
dstDev, // dst
layer->fImageFilter.get(),
layer->fPaint,
DeviceCompatibleWithFilter::kYes);
} else {
// NOTE: We don't just call internalDrawDeviceWithFilter with a null filter
// because we want to take advantage of overridden drawDevice functions for
// document-based devices.
SkSamplingOptions sampling;
dstDev->drawDevice(layer->fDevice.get(), sampling, layer->fPaint);
if (this->predrawNotify()) {
SkBaseDevice* dstDev = this->topDevice();
if (layer->fImageFilter) {
this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src
dstDev, // dst
layer->fImageFilter.get(),
layer->fPaint,
DeviceCompatibleWithFilter::kYes);
} else {
// NOTE: We don't just call internalDrawDeviceWithFilter with a null filter
// because we want to take advantage of overridden drawDevice functions for
// document-based devices.
SkSamplingOptions sampling;
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) {
// We don't test quickReject because the shadow outsets the path's bounds.
// TODO(michaelludwig): Is it worth calling SkDrawShadowMetrics::GetLocalBounds here?
this->predrawNotify();
if (!this->predrawNotify()) {
return;
}
this->topDevice()->drawShadow(path, rec);
}
@ -1924,7 +1940,7 @@ void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], i
void SkCanvas::onDiscard() {
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;
}
AutoLayerForImageFilter layer(this, paint, nullptr, CheckForOverwrite::kYes);
this->topDevice()->drawPaint(layer.paint());
auto layer = this->aboutToDraw(this, paint, nullptr, CheckForOverwrite::kYes);
if (layer) {
this->topDevice()->drawPaint(layer->paint());
}
}
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;
}
AutoLayerForImageFilter layer(this, strokePaint, &bounds);
this->topDevice()->drawPoints(mode, count, pts, layer.paint());
auto layer = this->aboutToDraw(this, strokePaint, &bounds);
if (layer) {
this->topDevice()->drawPoints(mode, count, pts, layer->paint());
}
}
void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
@ -1975,8 +1995,10 @@ void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
return;
}
AutoLayerForImageFilter layer(this, paint, &r, CheckForOverwrite::kYes);
this->topDevice()->drawRect(r, layer.paint());
auto layer = this->aboutToDraw(this, paint, &r, CheckForOverwrite::kYes);
if (layer) {
this->topDevice()->drawRect(r, layer->paint());
}
}
void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
@ -1985,8 +2007,10 @@ void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
return;
}
AutoLayerForImageFilter layer(this, paint, &bounds);
this->topDevice()->drawRegion(region, layer.paint());
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
this->topDevice()->drawRegion(region, layer->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.
}
AutoLayerForImageFilter layer(this, paint);
this->topDevice()->drawPaint(layer.paint());
auto layer = this->aboutToDraw(this, paint);
if (layer) {
this->topDevice()->drawPaint(layer->paint());
}
dev->restore(fMCRec->fMatrix);
}
@ -2037,8 +2063,10 @@ void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
return;
}
AutoLayerForImageFilter layer(this, paint, &oval);
this->topDevice()->drawOval(oval, layer.paint());
auto layer = this->aboutToDraw(this, paint, &oval);
if (layer) {
this->topDevice()->drawOval(oval, layer->paint());
}
}
void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
@ -2049,8 +2077,10 @@ void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
return;
}
AutoLayerForImageFilter layer(this, paint, &oval);
this->topDevice()->drawArc(oval, startAngle, sweepAngle, useCenter, layer.paint());
auto layer = this->aboutToDraw(this, paint, &oval);
if (layer) {
this->topDevice()->drawArc(oval, startAngle, sweepAngle, useCenter, layer->paint());
}
}
void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
@ -2071,8 +2101,10 @@ void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
return;
}
AutoLayerForImageFilter layer(this, paint, &bounds);
this->topDevice()->drawRRect(rrect, layer.paint());
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
this->topDevice()->drawRRect(rrect, layer->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;
}
AutoLayerForImageFilter layer(this, paint, &bounds);
this->topDevice()->drawDRRect(outer, inner, layer.paint());
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
this->topDevice()->drawDRRect(outer, inner, layer->paint());
}
}
void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
@ -2099,8 +2133,10 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
return;
}
AutoLayerForImageFilter layer(this, paint, &pathBounds);
this->topDevice()->drawPath(path, layer.paint());
auto layer = this->aboutToDraw(this, paint, &pathBounds);
if (layer) {
this->topDevice()->drawPath(path, layer->paint());
}
}
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);
skif::Mapping mapping(layerToDevice, SkMatrix::Translate(-x, -y));
this->predrawNotify();
device->drawFilteredImage(mapping, special.get(), filter.get(), sampling, realPaint);
if (this->predrawNotify()) {
device->drawFilteredImage(mapping, special.get(), filter.get(), sampling,realPaint);
}
return;
} // else fall through to regular drawing path
}
AutoLayerForImageFilter layer(this, realPaint, &bounds);
this->topDevice()->drawImageRect(image, nullptr, bounds, sampling,
layer.paint(), kStrict_SrcRectConstraint);
auto layer = this->aboutToDraw(this, realPaint, &bounds);
if (layer) {
this->topDevice()->drawImageRect(image, nullptr, bounds, sampling,
layer->paint(), kStrict_SrcRectConstraint);
}
}
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;
}
AutoLayerForImageFilter layer(this, realPaint, &dst, CheckForOverwrite::kYes,
image->isOpaque() ? kOpaque_ShaderOverrideOpacity
: kNotOpaque_ShaderOverrideOpacity);
this->topDevice()->drawImageRect(image, &src, dst, sampling, layer.paint(), constraint);
auto layer = this->aboutToDraw(this, realPaint, &dst, CheckForOverwrite::kYes,
image->isOpaque() ? kOpaque_ShaderOverrideOpacity
: kNotOpaque_ShaderOverrideOpacity);
if (layer) {
this->topDevice()->drawImageRect(image, &src, dst, sampling, layer->paint(), constraint);
}
}
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;
}
AutoLayerForImageFilter layer(this, realPaint, &dst);
this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer.paint());
auto layer = this->aboutToDraw(this, realPaint, &dst);
if (layer) {
this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer->paint());
}
}
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)) {
return;
}
AutoLayerForImageFilter layer(this, paint, &bounds);
this->topDevice()->drawGlyphRunList(glyphRunList, layer.paint());
auto layer = this->aboutToDraw(this, paint, &bounds);
if (layer) {
this->topDevice()->drawGlyphRunList(glyphRunList, layer->paint());
}
}
// These call the (virtual) onDraw... method
@ -2369,8 +2414,10 @@ void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmod
return;
}
AutoLayerForImageFilter layer(this, simplePaint, &bounds);
this->topDevice()->drawVertices(vertices, bmode, layer.paint());
auto layer = this->aboutToDraw(this, simplePaint, &bounds);
if (layer) {
this->topDevice()->drawVertices(vertices, bmode, layer->paint());
}
}
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;
}
AutoLayerForImageFilter layer(this, simplePaint, &bounds);
this->topDevice()->drawPatch(cubics, colors, texCoords, bmode, layer.paint());
auto layer = this->aboutToDraw(this, simplePaint, &bounds);
if (layer) {
this->topDevice()->drawPatch(cubics, colors, texCoords, bmode, layer->paint());
}
}
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) {
// drawable bounds are no longer reliable (e.g. android displaylist)
// so don't use them for quick-reject
this->predrawNotify();
this->topDevice()->drawDrawable(dr, matrix, this);
if (this->predrawNotify()) {
this->topDevice()->drawDrawable(dr, matrix, this);
}
}
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;
}
AutoLayerForImageFilter layer(this, realPaint);
this->topDevice()->drawAtlas(xform, tex, colors, count, bmode, layer.paint());
auto layer = this->aboutToDraw(this, realPaint);
if (layer) {
this->topDevice()->drawAtlas(xform, tex, colors, count, bmode, layer->paint());
}
}
void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
SkASSERT(key);
this->predrawNotify();
this->topDevice()->drawAnnotation(rect, key, value);
if (this->predrawNotify()) {
this->topDevice()->drawAnnotation(rect, key, value);
}
}
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;
}
this->predrawNotify();
this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
if (this->predrawNotify()) {
this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
}
}
void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
@ -2507,9 +2561,11 @@ void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
return;
}
AutoLayerForImageFilter layer(this, realPaint, setBoundsValid ? &setBounds : nullptr);
this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices, sampling,
layer.paint(), constraint);
auto layer = this->aboutToDraw(this, realPaint, setBoundsValid ? &setBounds : nullptr);
if (layer) {
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 offset = fWriter.bytesWritten();
this->predrawNotify();
SkASSERT_RELEASE(this->predrawNotify());
SkASSERT(0 != *size);
SkASSERT(((uint8_t) drawType) == drawType);

View File

@ -117,7 +117,7 @@ bool SkSurface_Base::outstandingImageSnapshot() const {
return fCachedImage && !fCachedImage->unique();
}
void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
bool SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
this->dirtyGenerationID();
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
@ -128,7 +128,13 @@ void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
// on the image (besides us).
bool unique = fCachedImage->unique();
if (!unique) {
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
this->onCopyOnWrite(mode);
#else
if (!this->onCopyOnWrite(mode)) {
return false;
}
#endif
}
// 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) {
this->onDiscard();
}
return true;
}
uint32_t SkSurface_Base::newGenerationID() {
@ -191,7 +198,7 @@ uint32_t SkSurface::generationID() {
}
void SkSurface::notifyContentWillChange(ContentChangeMode mode) {
asSB(this)->aboutToDraw(mode);
sk_ignore_unused_variable(asSB(this)->aboutToDraw(mode));
}
SkCanvas* SkSurface::getCanvas() {
@ -297,7 +304,9 @@ void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {
if (srcR.contains(dstR)) {
mode = kDiscard_ContentChangeMode;
}
asSB(this)->aboutToDraw(mode);
if (!asSB(this)->aboutToDraw(mode)) {
return;
}
asSB(this)->onWritePixels(pmap, x, y);
}
}
@ -386,7 +395,11 @@ protected:
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subsetOrNull) override { return nullptr; }
void onWritePixels(const SkPixmap&, int x, int y) override {}
void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override {}
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override {}
#else
bool onCopyOnWrite(ContentChangeMode) override { return true; }
#endif
};
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
* can optionally fork their backend (copy-on-write) in case it was
* 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;
#else
virtual bool SK_WARN_UNUSED_RESULT onCopyOnWrite(ContentChangeMode) = 0;
#endif
/**
* Signal the surface to remind its backing store that it's mutable again.
@ -144,7 +150,8 @@ private:
std::unique_ptr<SkCanvas> fCachedCanvas;
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
// 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
// render target into it. Note that this flushes the SkGpuDevice but
// doesn't force an OpenGL flush.
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView();
@ -201,6 +202,25 @@ void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
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(); }

View File

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

View File

@ -24,7 +24,11 @@ public:
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override;
void onWritePixels(const SkPixmap&, int x, int y) override;
void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override;
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void onCopyOnWrite(ContentChangeMode) override;
#else
bool onCopyOnWrite(ContentChangeMode) override;
#endif
void onRestoreBackingMutability() override;
private:
@ -124,6 +128,7 @@ void SkSurface_Raster::onRestoreBackingMutability() {
}
}
#ifdef SK_SURFACE_COPY_ON_WRITE_CRASHES
void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
// are we sharing pixelrefs with the image?
sk_sp<SkImage> cached(this->refCachedImage());
@ -147,6 +152,36 @@ void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
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
///////////////////////////////////////////////////////////////////////////////