Swap render target instead of creating a new gpu device for surface copy-on-write

Swap render target of the gpu device instead of creating a new gpu
device when making a copy-on-write upon surface modification.

This removes the SkCanvas::setRootDevice which contains problematic code
when trying to increase the use of SkImages internally in Skia.

BUG=skia:3388

Review URL: https://codereview.chromium.org/925343002
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 0ec167e..a57f4cd 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1306,15 +1306,6 @@
     SkBaseDevice* init(SkBaseDevice*, InitFlags);
 
     /**
-     *  DEPRECATED
-     *
-     *  Specify a device for this canvas to draw into. If it is not null, its
-     *  reference count is incremented. If the canvas was already holding a
-     *  device, its reference count is decremented. The new device is returned.
-     */
-    SkBaseDevice* setRootDevice(SkBaseDevice* device);
-
-    /**
      * Gets the size/origin of the top level layer in global canvas coordinates. We don't want this
      * to be public because it exposes decisions about layer sizes that are internal to the canvas.
      */
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index c523ca0..221ed93 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -608,59 +608,6 @@
     return fMCRec->fTopLayer->fDevice;
 }
 
-SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
-    // return root device
-    SkDeque::F2BIter iter(fMCStack);
-    MCRec*           rec = (MCRec*)iter.next();
-    SkASSERT(rec && rec->fLayer);
-    SkBaseDevice*    rootDevice = rec->fLayer->fDevice;
-
-    if (rootDevice == device) {
-        return device;
-    }
-
-    if (device) {
-        device->onAttachToCanvas(this);
-        device->initForRootLayer(fProps.pixelGeometry());
-    }
-    if (rootDevice) {
-        rootDevice->onDetachFromCanvas();
-    }
-
-    SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
-    rootDevice = device;
-
-    fDeviceCMDirty = true;
-
-    /*  Now we update our initial region to have the bounds of the new device,
-        and then intersect all of the clips in our stack with these bounds,
-        to ensure that we can't draw outside of the device's bounds (and trash
-                                                                     memory).
-
-    NOTE: this is only a partial-fix, since if the new device is larger than
-        the previous one, we don't know how to "enlarge" the clips in our stack,
-        so drawing may be artificially restricted. Without keeping a history of
-        all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
-        reconstruct the correct clips, so this approximation will have to do.
-        The caller really needs to restore() back to the base if they want to
-        accurately take advantage of the new device bounds.
-    */
-
-    SkIRect bounds;
-    if (device) {
-        bounds.set(0, 0, device->width(), device->height());
-    } else {
-        bounds.setEmpty();
-    }
-    // now jam our 1st clip to be bounds, and intersect the rest with that
-    rec->fRasterClip.setRect(bounds);
-    while ((rec = (MCRec*)iter.next()) != NULL) {
-        (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
-    }
-
-    return device;
-}
-
 bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
     if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
         return false;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 5781a58..ad63393 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -7,41 +7,40 @@
 
 #include "SkGpuDevice.h"
 
-#include "effects/GrBicubicEffect.h"
-#include "effects/GrDashingEffect.h"
-#include "effects/GrTextureDomain.h"
-#include "effects/GrSimpleTextureEffect.h"
-
-#include "GrContext.h"
 #include "GrBitmapTextContext.h"
+#include "GrContext.h"
 #include "GrDistanceFieldTextContext.h"
+#include "GrGpu.h"
+#include "GrGpuResourcePriv.h"
 #include "GrLayerHoister.h"
 #include "GrRecordReplaceDraw.h"
 #include "GrStrokeInfo.h"
 #include "GrTracing.h"
-#include "GrGpu.h"
-
-#include "SkGrTexturePixelRef.h"
-
 #include "SkCanvasPriv.h"
 #include "SkDeviceImageFilterProxy.h"
 #include "SkDrawProcs.h"
+#include "SkErrorInternals.h"
 #include "SkGlyphCache.h"
+#include "SkGrTexturePixelRef.h"
 #include "SkImageFilter.h"
 #include "SkLayerInfo.h"
 #include "SkMaskFilter.h"
 #include "SkPathEffect.h"
 #include "SkPicture.h"
 #include "SkPictureData.h"
-#include "SkRecord.h"
 #include "SkRRect.h"
+#include "SkRecord.h"
 #include "SkStroke.h"
 #include "SkSurface.h"
+#include "SkSurface_Gpu.h"
 #include "SkTLazy.h"
 #include "SkUtils.h"
 #include "SkVertState.h"
 #include "SkXfermode.h"
-#include "SkErrorInternals.h"
+#include "effects/GrBicubicEffect.h"
+#include "effects/GrDashingEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "effects/GrTextureDomain.h"
 
 #if SK_SUPPORT_GPU
 
@@ -164,9 +163,8 @@
     fTextContext = fContext->createTextContext(fRenderTarget, this->getLeakyProperties(), useDFT);
 }
 
-SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgeted,
-                                 const SkImageInfo& origInfo, int sampleCount,
-                                 const SkSurfaceProps* props, unsigned flags) {
+GrRenderTarget* SkGpuDevice::CreateRenderTarget(GrContext* context, SkSurface::Budgeted budgeted,
+                                                const SkImageInfo& origInfo, int sampleCount) {
     if (kUnknown_SkColorType == origInfo.colorType() ||
         origInfo.width() < 0 || origInfo.height() < 0) {
         return NULL;
@@ -195,13 +193,24 @@
     desc.fHeight = info.height();
     desc.fConfig = SkImageInfo2GrPixelConfig(info);
     desc.fSampleCnt = sampleCount;
+    GrTexture* texture = context->createTexture(desc, SkToBool(budgeted), NULL, 0);
+    if (NULL == texture) {
+        return NULL;
+    }
+    SkASSERT(NULL != texture->asRenderTarget());
+    return texture->asRenderTarget();
+}
 
-    SkAutoTUnref<GrTexture> texture(context->createTexture(desc, SkToBool(budgeted), NULL, 0));
-    if (!texture) {
+SkGpuDevice* SkGpuDevice::Create(GrContext* context, SkSurface::Budgeted budgeted,
+                                 const SkImageInfo& info, int sampleCount,
+                                 const SkSurfaceProps* props, unsigned flags) {
+
+    SkAutoTUnref<GrRenderTarget> rt(CreateRenderTarget(context, budgeted, info,  sampleCount));
+    if (NULL == rt) {
         return NULL;
     }
 
-    return SkNEW_ARGS(SkGpuDevice, (texture->asRenderTarget(), props, flags));
+    return SkNEW_ARGS(SkGpuDevice, (rt, props, flags));
 }
 
 SkGpuDevice::~SkGpuDevice() {
@@ -302,6 +311,38 @@
     fNeedClear = false;
 }
 
+void SkGpuDevice::replaceRenderTarget(bool shouldRetainContent) {
+    // Caller must have accessed the render target, because it knows the rt must be replaced.
+    SkASSERT(!fNeedClear);
+
+    SkSurface::Budgeted budgeted =
+            fRenderTarget->resourcePriv().isBudgeted() ? SkSurface::kYes_Budgeted
+                                                       : SkSurface::kNo_Budgeted;
+
+    SkAutoTUnref<GrRenderTarget> newRT(CreateRenderTarget(
+        fRenderTarget->getContext(), budgeted, this->imageInfo(), fRenderTarget->numSamples()));
+
+    if (NULL == newRT) {
+        return;
+    }
+
+    if (shouldRetainContent) {
+        if (fRenderTarget->wasDestroyed()) {
+            return;
+        }
+        this->context()->copySurface(newRT, fRenderTarget);
+    }
+
+    SkASSERT(fRenderTarget != newRT);
+
+    fRenderTarget->unref();
+    fRenderTarget = newRT.detach();
+
+    SkASSERT(fRenderTarget->surfacePriv().info() == fLegacyBitmap.info());
+    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (fRenderTarget->surfacePriv().info(), fRenderTarget));
+    fLegacyBitmap.setPixelRef(pr)->unref();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 4830c02..cd2dcdc 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -62,6 +62,8 @@
     // set all pixels to 0
     void clearAll();
 
+    void replaceRenderTarget(bool shouldRetainContent);
+
     GrRenderTarget* accessRenderTarget() SK_OVERRIDE;
 
     SkImageInfo imageInfo() const SK_OVERRIDE {
@@ -199,6 +201,9 @@
 
     static SkPicture::AccelData::Key ComputeAccelDataKey();
 
+    static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
+                                              int sampleCount);
+
     typedef SkBaseDevice INHERITED;
 };
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index a1c31f3..b94e4e3 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -7,7 +7,6 @@
 
 #include "SkSurface_Gpu.h"
 
-#include "GrGpuResourcePriv.h"
 #include "SkCanvas.h"
 #include "SkGpuDevice.h"
 #include "SkImage_Base.h"
@@ -24,7 +23,7 @@
 }
 
 SkSurface_Gpu::~SkSurface_Gpu() {
-    SkSafeUnref(fDevice);
+    fDevice->unref();
 }
 
 SkCanvas* SkSurface_Gpu::onNewCanvas() {
@@ -59,8 +58,8 @@
     canvas->drawBitmap(fDevice->accessBitmap(false), x, y, paint);
 }
 
-// Create a new SkGpuDevice and, if necessary, copy the contents of the old
-// device into it. Note that this flushes the SkGpuDevice but
+// 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.
 void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
     GrRenderTarget* rt = fDevice->accessRenderTarget();
@@ -69,22 +68,7 @@
     SkImage* image = this->getCachedImage(kNo_Budgeted);
     SkASSERT(image);
     if (rt->asTexture() == SkTextureImageGetTexture(image)) {
-        GrRenderTarget* oldRT = this->fDevice->accessRenderTarget();
-        SkSurface::Budgeted budgeted = oldRT->resourcePriv().isBudgeted() ? kYes_Budgeted :
-                                                                            kNo_Budgeted;
-        SkAutoTUnref<SkGpuDevice> newDevice(
-            SkGpuDevice::Create(oldRT->getContext(), budgeted, fDevice->imageInfo(),
-                                oldRT->numSamples(), &this->props(), 0));
-        if (kRetain_ContentChangeMode == mode && !oldRT->wasDestroyed() && newDevice) {
-            oldRT->getContext()->copySurface(newDevice->accessRenderTarget(), oldRT);
-        }
-
-        SkASSERT(this->getCachedCanvas());
-        SkASSERT(this->getCachedCanvas()->getDevice() == fDevice);
-
-        this->getCachedCanvas()->setRootDevice(newDevice);
-        SkRefCnt_SafeAssign(fDevice, newDevice.get());
-
+        this->fDevice->replaceRenderTarget(SkSurface::kRetain_ContentChangeMode == mode);
         SkTextureImageApplyBudgetedDecision(image);
     } else if (kDiscard_ContentChangeMode == mode) {
         this->SkSurface_Gpu::onDiscard();