Merge latest Skia into master (4 commits)

https://skia.googlesource.com/skia.git/+log/b357dea..c013892

Test: Presubmit checks will test this change.
Change-Id: Idc38bf2fd7627410f4cc8f407abe00b04d391f07
diff --git a/Android.bp b/Android.bp
index cf1e609..aeeea79 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1038,6 +1038,7 @@
         "gm/fontscaler.cpp",
         "gm/fontscalerdistortable.cpp",
         "gm/gamma.cpp",
+        "gm/gammaencodedpremul.cpp",
         "gm/gammatext.cpp",
         "gm/gamut.cpp",
         "gm/gaussianedge.cpp",
@@ -1905,6 +1906,7 @@
         "gm/fontscaler.cpp",
         "gm/fontscalerdistortable.cpp",
         "gm/gamma.cpp",
+        "gm/gammaencodedpremul.cpp",
         "gm/gammatext.cpp",
         "gm/gamut.cpp",
         "gm/gaussianedge.cpp",
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index 220738b..d4fe29d 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -314,7 +314,9 @@
     bool serial() const override { return !fThreaded; }
     const char* fileExtension() const override { return "png"; }
     SinkFlags flags() const override {
-        return SinkFlags{ SinkFlags::kGPU, SinkFlags::kDirect, SinkFlags::kMultisampled };
+        SinkFlags::Multisampled ms = fSampleCount > 0 ? SinkFlags::kMultisampled
+                                                      : SinkFlags::kNotMultisampled;
+        return SinkFlags{ SinkFlags::kGPU, SinkFlags::kDirect, ms };
     }
 private:
     sk_gpu_test::GrContextFactory::ContextType        fContextType;
diff --git a/gm/gammaencodedpremul.cpp b/gm/gammaencodedpremul.cpp
new file mode 100644
index 0000000..327368f
--- /dev/null
+++ b/gm/gammaencodedpremul.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColorSpaceXform.h"
+#include "SkColorSpaceXformPriv.h"
+#include "SkOpts.h"
+#include "SkUtils.h"
+
+static void clamp_to_alpha(uint32_t* pixels, int count) {
+    for (int i = 0; i < count; i++) {
+        uint8_t a = SkGetPackedA32(pixels[i]);
+        uint8_t r = SkGetPackedR32(pixels[i]);
+        uint8_t g = SkGetPackedG32(pixels[i]);
+        uint8_t b = SkGetPackedB32(pixels[i]);
+        pixels[i] = SkPackARGB32(a,
+                                 SkTMin(a, r),
+                                 SkTMin(a, g),
+                                 SkTMin(a, b));
+    }
+}
+
+class GammaEncodedPremulGM : public skiagm::GM {
+public:
+    GammaEncodedPremulGM(sk_sp<SkColorSpace> dst, sk_sp<SkColorSpace> src, const char* desc)
+        : fDstSpace(dst)
+        , fSrcSpace(src)
+        , fXform(SkColorSpaceXform::New(src.get(), dst.get()))
+        , fName(SkStringPrintf("gamma_encoded_premul_dst-v-src_%s", desc))
+    {
+        int i = 0;
+        for (int r = 0; r < kColorSteps; r++) {
+            for (int g = 0; g < kColorSteps; g++) {
+                for (int b = 0; b < kColorSteps; b++) {
+                    fColors[i++] = SkColorSetARGBInline(0xFF,
+                                                        r * kColorScale,
+                                                        g * kColorScale,
+                                                        b * kColorScale);
+                }
+            }
+        }
+
+    }
+
+protected:
+    virtual SkISize onISize() override {
+        return SkISize::Make(kAlphaMax, kNumColors * 2 * kStripeHeight);
+    }
+
+    SkString onShortName() override {
+        return fName;
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        if (canvas->imageInfo().isOpaque()) {
+            return;
+        }
+
+        SkBitmap bitmap;
+        SkImageInfo bitmapInfo = SkImageInfo::MakeN32Premul(kAlphaMax, 1,
+                canvas->imageInfo().refColorSpace());
+        bitmap.allocPixels(bitmapInfo);
+        uint32_t* pixels = bitmap.getAddr32(0, 0);
+
+        for (int i = 0; i < kNumColors; i++) {
+            // Create an entire row of the same color, with the alpha from 0 to kAlphaMax.
+            uint32_t row[kAlphaMax];
+            sk_memset32(row, fColors[i], kAlphaMax);
+            for (int a = 0; a < kAlphaMax; a++) {
+                row[a] = (row[a] & 0x00FFFFFF) | (a << 24);
+            }
+
+            // Tranform row to dst, then premultiply.
+            fXform->apply(select_xform_format(kN32_SkColorType), pixels,
+                          SkColorSpaceXform::kBGRA_8888_ColorFormat, row, kAlphaMax,
+                          kUnpremul_SkAlphaType);
+            SkOpts::RGBA_to_rgbA(pixels, pixels, kAlphaMax);
+
+            // Write the dst space premultiplied row to the canvas.
+            for (int j = 0; j < kStripeHeight; j++) {
+                canvas->drawBitmap(bitmap, 0, 2 * i * kStripeHeight + j);
+            }
+
+            // Premultiply, then transform the row to dst.
+            SkOpts::RGBA_to_rgbA(pixels, row, kAlphaMax);
+            fXform->apply(select_xform_format(kN32_SkColorType), pixels,
+                          SkColorSpaceXform::kBGRA_8888_ColorFormat, pixels, kAlphaMax,
+                          kUnpremul_SkAlphaType);
+            clamp_to_alpha(pixels, kAlphaMax);
+
+            // Write the src space premultiplied row to the canvas.
+            for (int j = 0; j < kStripeHeight; j++) {
+                canvas->drawBitmap(bitmap, 0, (2 * i + 1) * kStripeHeight + j);
+            }
+        }
+    }
+
+private:
+    static constexpr int kColorSteps = 4;
+    static constexpr int kNumColors = kColorSteps * kColorSteps * kColorSteps;
+    static constexpr int kColorScale = 255 / (kColorSteps - 1);
+    static constexpr int kStripeHeight = 10;
+    static constexpr int kAlphaMax = 255;
+
+    sk_sp<SkColorSpace>                fDstSpace;
+    sk_sp<SkColorSpace>                fSrcSpace;
+    std::unique_ptr<SkColorSpaceXform> fXform;
+    SkString                           fName;
+    SkColor                            fColors[kNumColors];
+
+    typedef GM INHERITED;
+};
+
+DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(),
+        SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kRec2020_Gamut),
+        "toWideGamut");)
+DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+        SkColorSpace::kRec2020_Gamut), SkColorSpace::MakeSRGB(), "fromWideGamut");)
+DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(),
+        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut),
+        "toLinear");)
+DEF_GM(return new GammaEncodedPremulGM(
+        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut),
+        SkColorSpace::MakeSRGB(), "fromLinear");)
+DEF_GM(return new GammaEncodedPremulGM(
+        SkColorSpace::MakeRGB({ 1.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+        SkColorSpace::kSRGB_Gamut), SkColorSpace::MakeSRGB(), "from1.8");)
diff --git a/gn/gm.gni b/gn/gm.gni
index 32c22d7..3b94be5 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -129,6 +129,7 @@
   "$_gm/fontscaler.cpp",
   "$_gm/fontscalerdistortable.cpp",
   "$_gm/gamma.cpp",
+  "$_gm/gammaencodedpremul.cpp",
   "$_gm/gammatext.cpp",
   "$_gm/gamut.cpp",
   "$_gm/gaussianedge.cpp",
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 89a0265..b775783 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -130,14 +130,6 @@
     return new SkBitmapDevice(bitmap, surfaceProps, hndl);
 }
 
-void SkBitmapDevice::setNewSize(const SkISize& size) {
-    SkASSERT(!fBitmap.pixelRef());
-    fBitmap.setInfo(fBitmap.info().makeWH(size.fWidth, size.fHeight));
-    this->privateResize(fBitmap.info().width(), fBitmap.info().height());
-
-    fRCStack.setNewSize(size.fWidth, size.fHeight);
-}
-
 void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
     SkASSERT(bm.width() == fBitmap.width());
     SkASSERT(bm.height() == fBitmap.height());
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index a97ad01..d6b5127 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -163,8 +163,6 @@
     void*       fRasterHandle = nullptr;
     SkRasterClipStack  fRCStack;
 
-    void setNewSize(const SkISize&);  // Used by SkCanvas for resetForNextPicture().
-
     typedef SkBaseDevice INHERITED;
 };
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index daefeeb..c3e6aa6 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -53,6 +53,57 @@
 
 #define RETURN_ON_NULL(ptr)     do { if (nullptr == (ptr)) return; } while (0)
 
+class SkNoPixelsDevice : public SkBaseDevice {
+public:
+    SkNoPixelsDevice(const SkIRect& bounds, const SkSurfaceProps& props)
+        : SkBaseDevice(SkImageInfo::MakeUnknown(bounds.width(), bounds.height()), props)
+    {}
+
+    void resetForNextPicture(const SkIRect& bounds) {
+        this->privateResize(bounds.width(), bounds.height());
+    }
+
+protected:
+    // We don't track the clip at all (for performance), but we have to respond to some queries.
+    // We pretend to be wide-open. We could pretend to always be empty, but that *seems* worse.
+    void onSave() override {}
+    void onRestore() override {}
+    void onClipRect(const SkRect& rect, SkClipOp, bool aa) override {}
+    void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override {}
+    void onClipPath(const SkPath& path, SkClipOp, bool aa) override {}
+    void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override {}
+    void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) override {}
+    bool onClipIsAA() const override { return false; }
+    void onAsRgnClip(SkRegion* rgn) const override {
+        rgn->setRect(SkIRect::MakeWH(this->width(), this->height()));
+    }
+    ClipType onGetClipType() const override {
+        return kRect_ClipType;
+    }
+
+    void drawPaint(const SkPaint& paint) override {}
+    void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {}
+    void drawRect(const SkRect&, const SkPaint&) override {}
+    void drawOval(const SkRect&, const SkPaint&) override {}
+    void drawRRect(const SkRRect&, const SkPaint&) override {}
+    void drawPath(const SkPath&, const SkPaint&, const SkMatrix*, bool) override {}
+    void drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) override {}
+    void drawSprite(const SkBitmap&, int, int, const SkPaint&) override {}
+    void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint&,
+                        SkCanvas::SrcRectConstraint) override {}
+    void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {}
+    void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
+                     const SkPaint&) override {}
+    void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
+    void drawVertices(SkCanvas::VertexMode, int, const SkPoint[], const SkPoint[], const SkColor[],
+                      SkBlendMode, const uint16_t[], int, const SkPaint&) override {}
+
+private:
+    typedef SkBaseDevice INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 /*
  *  Return true if the drawing this rect would hit every pixels in the canvas.
  *
@@ -578,8 +629,8 @@
     fMCRec->reset(bounds);
 
     // We're peering through a lot of structs here.  Only at this scope do we
-    // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
-    static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
+    // know that the device is a SkNoPixelsDevice.
+    static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice)->resetForNextPicture(bounds);
     fDeviceClipBounds = qr_clip_bounds(bounds);
     fIsScaleTranslate = true;
 }
@@ -631,32 +682,13 @@
     this->init(nullptr, kDefault_InitFlags);
 }
 
-static SkBitmap make_nopixels(int width, int height) {
-    SkBitmap bitmap;
-    bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
-    return bitmap;
-}
-
-class SkNoPixelsBitmapDevice : public SkBitmapDevice {
-public:
-    SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
-        : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
-    {
-        this->setOrigin(SkMatrix::I(), bounds.x(), bounds.y());
-    }
-
-private:
-
-    typedef SkBitmapDevice INHERITED;
-};
-
 SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
     : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
     , fProps(SkSurfacePropsCopyOrDefault(props))
 {
     inc_canvas();
 
-    this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
+    this->init(new SkNoPixelsDevice(SkIRect::MakeWH(width, height), fProps),
                kDefault_InitFlags)->unref();
 }
 
@@ -666,7 +698,7 @@
 {
     inc_canvas();
 
-    this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
+    this->init(new SkNoPixelsDevice(bounds, fProps), flags)->unref();
 }
 
 SkCanvas::SkCanvas(SkBaseDevice* device)
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index cb727ae..a8a68b6 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -376,6 +376,7 @@
 
     virtual SkImageFilterCache* getImageFilterCache() { return NULL; }
 
+    friend class SkNoPixelsDevice;
     friend class SkBitmapDevice;
     void privateResize(int w, int h) {
         *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeWH(w, h);
diff --git a/src/gpu/GrOpList.cpp b/src/gpu/GrOpList.cpp
index d354e59..1f90f43 100644
--- a/src/gpu/GrOpList.cpp
+++ b/src/gpu/GrOpList.cpp
@@ -11,17 +11,23 @@
 #include "GrSurface.h"
 #include "GrSurfaceProxy.h"
 
-GrOpList::GrOpList(GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail) 
-    : fFlags(0)
+uint32_t GrOpList::CreateUniqueID() {
+    static int32_t gUniqueID = SK_InvalidUniqueID;
+    uint32_t id;
+    // Loop in case our global wraps around, as we never want to return a 0.
+    do {
+        id = static_cast<uint32_t>(sk_atomic_inc(&gUniqueID) + 1);
+    } while (id == SK_InvalidUniqueID);
+    return id;
+}
+
+GrOpList::GrOpList(GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail)
+    : fUniqueID(CreateUniqueID())
+    , fFlags(0)
     , fTarget(surfaceProxy)
     , fAuditTrail(auditTrail) {
 
     surfaceProxy->setLastOpList(this);
-
-#ifdef SK_DEBUG
-    static int debugID = 0;
-    fDebugID = debugID++;
-#endif
 }
 
 GrOpList::~GrOpList() {
@@ -62,10 +68,10 @@
 #ifdef SK_DEBUG
 void GrOpList::dump() const {
     SkDebugf("--------------------------------------------------------------\n");
-    SkDebugf("node: %d -> RT: %d\n", fDebugID, fTarget ? fTarget->uniqueID().asUInt() : -1);
+    SkDebugf("node: %d -> RT: %d\n", fUniqueID, fTarget ? fTarget->uniqueID().asUInt() : -1);
     SkDebugf("relies On (%d): ", fDependencies.count());
     for (int i = 0; i < fDependencies.count(); ++i) {
-        SkDebugf("%d, ", fDependencies[i]->fDebugID);
+        SkDebugf("%d, ", fDependencies[i]->fUniqueID);
     }
     SkDebugf("\n");
 }
diff --git a/src/gpu/GrOpList.h b/src/gpu/GrOpList.h
index 75fc5e6..5571266 100644
--- a/src/gpu/GrOpList.h
+++ b/src/gpu/GrOpList.h
@@ -73,6 +73,8 @@
      */
     virtual GrRenderTargetOpList* asRenderTargetOpList() { return nullptr; }
 
+    int32_t uniqueID() const { return fUniqueID; }
+
     /*
      * Dump out the GrOpList dependency DAG
      */
@@ -81,6 +83,8 @@
 private:
     friend class GrDrawingManager; // for resetFlag & TopoSortTraits
 
+    static uint32_t CreateUniqueID();
+
     enum Flags {
         kClosed_Flag    = 0x01,   //!< This GrOpList can't accept any more ops
 
@@ -126,15 +130,15 @@
 
     void addDependency(GrOpList* dependedOn);
 
-    SkDEBUGCODE(int                                 fDebugID;)
-    uint32_t                                        fFlags;
-    GrSurfaceProxy*                                 fTarget;
+    uint32_t             fUniqueID;
+    uint32_t             fFlags;
+    GrSurfaceProxy*      fTarget;
 
     // 'this' GrOpList relies on the output of the GrOpLists in 'fDependencies'
-    SkTDArray<GrOpList*>                            fDependencies;
+    SkTDArray<GrOpList*> fDependencies;
 
 protected:
-    GrAuditTrail*                                   fAuditTrail;
+    GrAuditTrail*        fAuditTrail;
 
     typedef SkRefCnt INHERITED;
 };
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index bae2ea6..1d891df 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1673,10 +1673,13 @@
     }
 }
 
-void GrRenderTargetContext::addDrawOp(const GrPipelineBuilder& pipelineBuilder, const GrClip& clip,
-                                      std::unique_ptr<GrDrawOp> op) {
+uint32_t GrRenderTargetContext::addDrawOp(const GrPipelineBuilder& pipelineBuilder,
+                                          const GrClip& clip,
+                                          std::unique_ptr<GrDrawOp> op) {
     ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
+    if (this->drawingManager()->wasAbandoned()) {
+        return SK_InvalidUniqueID;
+    }
     SkDEBUGCODE(this->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::addDrawOp");
 
@@ -1686,21 +1689,21 @@
     GrAppliedClip appliedClip(bounds);
     if (!clip.apply(fContext, this, pipelineBuilder.isHWAntialias(),
                     pipelineBuilder.hasUserStencilSettings(), &appliedClip)) {
-        return;
+        return SK_InvalidUniqueID;
     }
 
     // This forces instantiation of the render target. Pipeline creation is moving to flush time
     // by which point instantiation must have occurred anyway.
     GrRenderTarget* rt = this->accessRenderTarget();
     if (!rt) {
-        return;
+        return SK_InvalidUniqueID;
     }
 
     GrResourceProvider* resourceProvider = fContext->resourceProvider();
     if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) {
         if (!resourceProvider->attachStencilAttachment(this->accessRenderTarget())) {
             SkDebugf("ERROR creating stencil attachment. Draw skipped.\n");
-            return;
+            return SK_InvalidUniqueID;
         }
     }
 
@@ -1717,13 +1720,13 @@
     if (pipelineBuilder.willXPNeedDstTexture(*this->caps(), analysis)) {
         this->setupDstTexture(rt, clip, bounds, &args.fDstTexture);
         if (!args.fDstTexture.texture()) {
-            return;
+            return SK_InvalidUniqueID;
         }
     }
     op->initPipeline(args);
     // TODO: We need to add pipeline dependencies on textures, etc before recording this op.
     op->setClippedBounds(appliedClip.clippedDrawBounds());
-    this->getOpList()->addOp(std::move(op), this);
+    return this->getOpList()->addOp(std::move(op), this);
 }
 
 void GrRenderTargetContext::setupDstTexture(GrRenderTarget* rt, const GrClip& clip,
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index d711c81..1126c76 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -473,8 +473,9 @@
                        size_t srcRowBytes, int x, int y, uint32_t flags) override;
 
     // This performs processing specific to GrDrawOp-derived ops before recording them into the
-    // op list.
-    void addDrawOp(const GrPipelineBuilder&, const GrClip&, std::unique_ptr<GrDrawOp>);
+    // op list. It returns the id of the opList to which the op was added, or 0, if it was
+    // dropped (e.g., due to clipping).
+    uint32_t addDrawOp(const GrPipelineBuilder&, const GrClip&, std::unique_ptr<GrDrawOp>);
 
     // Makes a copy of the dst if it is necessary for the draw and returns the texture that should
     // be used by GrXferProcessor to access the destination color. If the texture is nullptr then
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index 8a1061c..842e23c 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -108,11 +108,11 @@
         return fRenderTargetContext->fRenderTargetProxy->uniqueID();
     }
 
-    void testingOnly_addDrawOp(GrPaint&&,
-                               GrAAType,
-                               std::unique_ptr<GrDrawOp>,
-                               const GrUserStencilSettings* = nullptr,
-                               bool snapToCenters = false);
+    uint32_t testingOnly_addDrawOp(GrPaint&&,
+                                   GrAAType,
+                                   std::unique_ptr<GrDrawOp>,
+                                   const GrUserStencilSettings* = nullptr,
+                                   bool snapToCenters = false);
 
     bool refsWrappedObjects() const {
         return fRenderTargetContext->fRenderTargetProxy->refsWrappedObjects();
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index e232547..3929c5c 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -67,8 +67,9 @@
      */
     const GrCaps* caps() const { return fGpu->caps(); }
 
-    void addOp(std::unique_ptr<GrOp> op, GrRenderTargetContext* renderTargetContext) {
+    uint32_t addOp(std::unique_ptr<GrOp> op, GrRenderTargetContext* renderTargetContext) {
         this->recordOp(std::move(op), renderTargetContext);
+        return this->uniqueID();
     }
 
     /** Clears the entire render target */
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 55529d8..7e27583 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -233,15 +233,16 @@
 
 #define ASSERT_SINGLE_OWNER \
     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fRenderTargetContext->fSingleOwner);)
-#define RETURN_IF_ABANDONED if (fRenderTargetContext->drawingManager()->wasAbandoned()) { return; }
 
-void GrRenderTargetContextPriv::testingOnly_addDrawOp(GrPaint&& paint,
-                                                      GrAAType aaType,
-                                                      std::unique_ptr<GrDrawOp> op,
-                                                      const GrUserStencilSettings* uss,
-                                                      bool snapToCenters) {
+uint32_t GrRenderTargetContextPriv::testingOnly_addDrawOp(GrPaint&& paint,
+                                                          GrAAType aaType,
+                                                          std::unique_ptr<GrDrawOp> op,
+                                                          const GrUserStencilSettings* uss,
+                                                          bool snapToCenters) {
     ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
+    if (fRenderTargetContext->drawingManager()->wasAbandoned()) {
+        return SK_InvalidUniqueID;
+    }
     SkDEBUGCODE(fRenderTargetContext->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fRenderTargetContext->fAuditTrail,
                               "GrRenderTargetContext::testingOnly_addDrawOp");
@@ -252,11 +253,10 @@
     }
     pipelineBuilder.setSnapVerticesToPixelCenters(snapToCenters);
 
-    fRenderTargetContext->addDrawOp(pipelineBuilder, GrNoClip(), std::move(op));
+    return fRenderTargetContext->addDrawOp(pipelineBuilder, GrNoClip(), std::move(op));
 }
 
 #undef ASSERT_SINGLE_OWNER
-#undef RETURN_IF_ABANDONED
 
 ///////////////////////////////////////////////////////////////////////////////