Adding optimization to avoid image copy in SkSurface copy on write when content is discardable
This patch also adds code to SkDeferredCanvas to trigger the optimization.
TEST=DeferredSurfaceCopy bench, Surface unit test
R=reed@google.com
Author: junov@chromium.org
Review URL: https://chromiumcodereview.appspot.com/14063015
git-svn-id: http://skia.googlecode.com/svn/trunk@8797 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 2d8212d..1dc4fad 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -122,7 +122,7 @@
void SkCanvas::predrawNotify() {
if (fSurfaceBase) {
- fSurfaceBase->aboutToDraw();
+ fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
}
}
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 72507c8..e8b6d38 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -37,7 +37,7 @@
}
}
-void SkSurface_Base::aboutToDraw() {
+void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
this->dirtyGenerationID();
if (NULL != fCachedCanvas) {
@@ -51,7 +51,7 @@
// the cached image. Note: we only call if there is an outstanding owner
// on the image (besides us).
if (fCachedImage->getRefCnt() > 1) {
- this->onCopyOnWrite();
+ this->onCopyOnWrite(mode);
}
// regardless of copy-on-write, we must drop our cached image now, so
@@ -87,8 +87,8 @@
return fGenerationID;
}
-void SkSurface::notifyContentChanged() {
- asSB(this)->aboutToDraw();
+void SkSurface::notifyContentWillChange(ContentChangeMode mode) {
+ asSB(this)->aboutToDraw(mode);
}
SkCanvas* SkSurface::getCanvas() {
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index 32cfc88..6ea8d60 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -49,10 +49,8 @@
* 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.
- *
- * The default implementation does nothing.
*/
- virtual void onCopyOnWrite() = 0;
+ virtual void onCopyOnWrite(ContentChangeMode) = 0;
inline SkCanvas* getCachedCanvas();
inline SkImage* getCachedImage();
@@ -64,7 +62,7 @@
SkCanvas* fCachedCanvas;
SkImage* fCachedImage;
- void aboutToDraw();
+ void aboutToDraw(ContentChangeMode mode);
friend class SkCanvas;
friend class SkSurface;
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 098c92b..687ca6c 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -23,7 +23,7 @@
virtual SkImage* onNewImageSnapshot() SK_OVERRIDE;
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
const SkPaint*) SK_OVERRIDE;
- virtual void onCopyOnWrite() SK_OVERRIDE;
+ virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
private:
SkGpuDevice* fDevice;
@@ -86,9 +86,8 @@
// Create a new SkGpuDevice and, if necessary, copy the contents of the old
// device into it. Note that this flushes the SkGpuDevice but
// doesn't force an OpenGL flush.
-void SkSurface_Gpu::onCopyOnWrite() {
+void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
GrRenderTarget* rt = (GrRenderTarget*) fDevice->accessRenderTarget();
-
// are we sharing our render target with the image?
SkASSERT(NULL != this->getCachedImage());
if (rt->asTexture() == SkTextureImageGetTexture(this->getCachedImage())) {
@@ -96,8 +95,10 @@
fDevice->createCompatibleDevice(fDevice->config(), fDevice->width(),
fDevice->height(), fDevice->isOpaque()));
SkAutoTUnref<SkGpuDevice> aurd(newDevice);
- fDevice->context()->copyTexture(rt->asTexture(),
- (GrRenderTarget*)newDevice->accessRenderTarget());
+ if (kRetain_ContentChangeMode == mode) {
+ fDevice->context()->copyTexture(rt->asTexture(),
+ reinterpret_cast<GrRenderTarget*>(newDevice->accessRenderTarget()));
+ }
SkASSERT(NULL != this->getCachedCanvas());
SkASSERT(this->getCachedCanvas()->getDevice() == fDevice);
this->getCachedCanvas()->setDevice(newDevice);
diff --git a/src/image/SkSurface_Picture.cpp b/src/image/SkSurface_Picture.cpp
index affa05c..79812c4 100644
--- a/src/image/SkSurface_Picture.cpp
+++ b/src/image/SkSurface_Picture.cpp
@@ -24,7 +24,7 @@
virtual SkImage* onNewImageSnapshot() SK_OVERRIDE;
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
const SkPaint*) SK_OVERRIDE;
- virtual void onCopyOnWrite() SK_OVERRIDE;
+ virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
private:
SkPicture* fPicture;
@@ -75,7 +75,7 @@
SkImagePrivDrawPicture(canvas, fPicture, x, y, paint);
}
-void SkSurface_Picture::onCopyOnWrite() {
+void SkSurface_Picture::onCopyOnWrite(ContentChangeMode /*mode*/) {
// We always spawn a copy of the recording picture when we
// are asked for a snapshot, so we never need to do anything here.
}
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index 9a1f312..ccfdd27 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -25,7 +25,7 @@
virtual SkImage* onNewImageSnapshot() SK_OVERRIDE;
virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
const SkPaint*) SK_OVERRIDE;
- virtual void onCopyOnWrite() SK_OVERRIDE;
+ virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
private:
SkBitmap fBitmap;
@@ -124,13 +124,18 @@
return SkNewImageFromBitmap(fBitmap, fWeOwnThePixels);
}
-void SkSurface_Raster::onCopyOnWrite() {
+void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) {
// are we sharing pixelrefs with the image?
- SkASSERT(NULL !=this->getCachedImage());
+ SkASSERT(NULL != this->getCachedImage());
if (SkBitmapImageGetPixelRef(this->getCachedImage()) == fBitmap.pixelRef()) {
SkASSERT(fWeOwnThePixels);
- SkBitmap prev(fBitmap);
- prev.deepCopyTo(&fBitmap, prev.config());
+ if (kDiscard_ContentChangeMode == mode) {
+ fBitmap.setPixelRef(NULL, 0);
+ fBitmap.allocPixels();
+ } else {
+ SkBitmap prev(fBitmap);
+ prev.deepCopyTo(&fBitmap, prev.config());
+ }
// 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.
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 42e9537..c181818 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -248,6 +248,7 @@
SkSurface* fSurface;
SkDeferredCanvas::NotificationClient* fNotificationClient;
bool fFreshFrame;
+ bool fCanDiscardCanvasContents;
size_t fMaxRecordingStorageBytes;
size_t fPreviousStorageAllocated;
size_t fBitmapSizeThreshold;
@@ -281,6 +282,7 @@
void DeferredDevice::init() {
fRecordingCanvas = NULL;
fFreshFrame = true;
+ fCanDiscardCanvasContents = false;
fPreviousStorageAllocated = 0;
fBitmapSizeThreshold = kDeferredCanvasBitmapSizeThreshold;
fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
@@ -312,11 +314,14 @@
}
void DeferredDevice::skipPendingCommands() {
- if (!fRecordingCanvas->isDrawingToLayer() && fPipeController.hasPendingCommands()) {
- fFreshFrame = true;
- flushPendingCommands(kSilent_PlaybackMode);
- if (fNotificationClient) {
- fNotificationClient->skippedPendingDrawCommands();
+ if (!fRecordingCanvas->isDrawingToLayer()) {
+ fCanDiscardCanvasContents = true;
+ if (fPipeController.hasPendingCommands()) {
+ fFreshFrame = true;
+ flushPendingCommands(kSilent_PlaybackMode);
+ if (fNotificationClient) {
+ fNotificationClient->skippedPendingDrawCommands();
+ }
}
}
}
@@ -335,8 +340,18 @@
if (!fPipeController.hasPendingCommands()) {
return;
}
- if (playbackMode == kNormal_PlaybackMode && fNotificationClient) {
- fNotificationClient->prepareForDraw();
+ if (playbackMode == kNormal_PlaybackMode) {
+ if (NULL != fNotificationClient) {
+ fNotificationClient->prepareForDraw();
+ }
+ if (fCanDiscardCanvasContents) {
+ if (NULL != fSurface) {
+ // Pre-empt notifyContentChanged(false) calls that will happen
+ // during flush
+ fSurface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
+ }
+ fCanDiscardCanvasContents = false;
+ }
}
fPipeWriter.flushRecording(true);
fPipeController.playback(kSilent_PlaybackMode == playbackMode);