Move op memory storage to GrContext (take 2)

TBR=bsalomon@google.com

Change-Id: I4a448694d4114d83cd3a720cfc8bd37de51733d1
Reviewed-on: https://skia-review.googlesource.com/135707
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/atlastext.cpp b/gm/atlastext.cpp
index 226b83f..c30a675 100644
--- a/gm/atlastext.cpp
+++ b/gm/atlastext.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 
 #if SK_SUPPORT_ATLAS_TEXT
+#include "GrContext.h"
 
 #include "SkAtlasTextContext.h"
 #include "SkAtlasTextFont.h"
@@ -72,6 +73,7 @@
         if (!targetHandle) {
             return;
         }
+
         fTarget = SkAtlasTextTarget::Make(fContext, kSize, kSize, targetHandle);
 
         fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Italic());
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index dea6150..1c26989 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -80,7 +80,9 @@
                                           sk_sp<GrGeometryProcessor> gp,
                                           const SkRect& rect,
                                           GrColor color) {
-        return std::unique_ptr<GrDrawOp>(new BezierCubicTestOp(std::move(gp), rect, color));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<BezierCubicTestOp>(std::move(gp), rect, color);
     }
 
 private:
@@ -264,8 +266,9 @@
                                           const SkRect& rect,
                                           GrColor color,
                                           const SkMatrix& klm) {
-        return std::unique_ptr<GrMeshDrawOp>(
-                new BezierConicTestOp(std::move(gp), rect, color, klm));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<BezierConicTestOp>(std::move(gp), rect, color, klm);
     }
 
 private:
@@ -485,7 +488,9 @@
                                           const SkRect& rect,
                                           GrColor color,
                                           const GrPathUtils::QuadUVMatrix& devToUV) {
-        return std::unique_ptr<GrDrawOp>(new BezierQuadTestOp(std::move(gp), rect, color, devToUV));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<BezierQuadTestOp>(std::move(gp), rect, color, devToUV);
     }
 
 private:
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 8f8d1b4..18c5ebd 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -45,7 +45,9 @@
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           GrPaint&& paint,
                                           const SkRect& rect) {
-        return std::unique_ptr<GrDrawOp>(new PolyBoundsOp(std::move(paint), rect));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<PolyBoundsOp>(std::move(paint), rect);
     }
 
     const char* name() const override { return "PolyBoundsOp"; }
diff --git a/include/atlastext/SkAtlasTextTarget.h b/include/atlastext/SkAtlasTextTarget.h
index c29f381..7e4ae44 100644
--- a/include/atlastext/SkAtlasTextTarget.h
+++ b/include/atlastext/SkAtlasTextTarget.h
@@ -27,7 +27,9 @@
      * Creates a text drawing target. ‘handle’ is used to identify this rendering surface when
      * draws are flushed to the SkAtlasTextContext's SkAtlasTextRenderer.
      */
-    static std::unique_ptr<SkAtlasTextTarget> Make(sk_sp<SkAtlasTextContext>, int width, int height,
+    static std::unique_ptr<SkAtlasTextTarget> Make(sk_sp<SkAtlasTextContext>,
+                                                   int width,
+                                                   int height,
                                                    void* handle);
 
     /**
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 5a2ba47..517e23a 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -33,6 +33,7 @@
 class GrGpu;
 class GrIndexBuffer;
 struct GrMockOptions;
+class GrOpMemoryPool;
 class GrOvalRenderer;
 class GrPath;
 class GrProxyProvider;
@@ -307,6 +308,8 @@
     GrProxyProvider*                        fProxyProvider;
     std::unique_ptr<GrTextureStripAtlasManager> fTextureStripAtlasManager;
 
+    // All the GrOp-derived classes use this pool.
+    sk_sp<GrOpMemoryPool>                   fOpMemoryPool;
 
     GrGlyphCache*                           fGlyphCache;
     std::unique_ptr<GrTextBlobCache>        fTextBlobCache;
diff --git a/include/private/GrCCPerOpListPaths.h b/include/private/GrCCPerOpListPaths.h
index b94e82c..6e53a1f 100644
--- a/include/private/GrCCPerOpListPaths.h
+++ b/include/private/GrCCPerOpListPaths.h
@@ -25,7 +25,7 @@
 public:
     ~GrCCPerOpListPaths();
 
-    SkTInternalLList<GrCCDrawPathsOp> fDrawOps;
+    SkTInternalLList<GrCCDrawPathsOp> fDrawOps; // This class does not own these ops.
     std::map<uint32_t, GrCCClipPath> fClipPaths;
     SkSTArenaAlloc<10 * 1024> fAllocator{10 * 1024 * 2};
     sk_sp<const GrCCPerFlushResources> fFlushResources;
diff --git a/include/private/GrOpList.h b/include/private/GrOpList.h
index 846a583..0d5a1a2 100644
--- a/include/private/GrOpList.h
+++ b/include/private/GrOpList.h
@@ -17,6 +17,7 @@
 class GrAuditTrail;
 class GrCaps;
 class GrOpFlushState;
+class GrOpMemoryPool;
 class GrRenderTargetOpList;
 class GrResourceAllocator;
 class GrResourceProvider;
@@ -28,7 +29,7 @@
 
 class GrOpList : public SkRefCnt {
 public:
-    GrOpList(GrResourceProvider*, GrSurfaceProxy*, GrAuditTrail*);
+    GrOpList(GrResourceProvider*, sk_sp<GrOpMemoryPool>, GrSurfaceProxy*, GrAuditTrail*);
     ~GrOpList() override;
 
     // These four methods are invoked at flush time
@@ -102,12 +103,16 @@
 protected:
     bool isInstantiated() const;
 
-    GrSurfaceProxyRef fTarget;
-    GrAuditTrail*     fAuditTrail;
+    // This is a backpointer to the GrOpMemoryPool that holds the memory for this opLists' ops.
+    // In the DDL case, these back pointers keep the DDL's GrOpMemoryPool alive as long as its
+    // constituent opLists survive.
+    sk_sp<GrOpMemoryPool> fOpMemoryPool;
+    GrSurfaceProxyRef     fTarget;
+    GrAuditTrail*         fAuditTrail;
 
-    GrLoadOp          fColorLoadOp    = GrLoadOp::kLoad;
-    GrColor           fLoadClearColor = 0x0;
-    GrLoadOp          fStencilLoadOp  = GrLoadOp::kLoad;
+    GrLoadOp              fColorLoadOp    = GrLoadOp::kLoad;
+    GrColor               fLoadClearColor = 0x0;
+    GrLoadOp              fStencilLoadOp  = GrLoadOp::kLoad;
 
     // List of texture proxies whose contents are being prepared on a worker thread
     SkTArray<GrTextureProxy*, true> fDeferredProxies;
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index 0fb9a79..f4cda54 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -83,6 +83,11 @@
                               void* handle)
             : GrTextUtils::Target(width, height, kColorSpaceInfo)
             , SkAtlasTextTarget(std::move(context), width, height, handle) {
+        fOpMemoryPool = fContext->internal().grContext()->contextPriv().refOpMemoryPool();
+    }
+
+    ~SkInternalAtlasTextTarget() override {
+        this->deleteOps();
     }
 
     /** GrTextUtils::Target overrides */
@@ -110,10 +115,13 @@
     void flush() override;
 
 private:
+    void deleteOps();
+
     uint32_t fColor;
     using SkAtlasTextTarget::fWidth;
     using SkAtlasTextTarget::fHeight;
     SkTArray<std::unique_ptr<GrAtlasTextOp>, true> fOps;
+    sk_sp<GrOpMemoryPool> fOpMemoryPool;
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -162,6 +170,7 @@
     for (int i = 0; i < n; ++i) {
         GrAtlasTextOp* other = fOps.fromBack(i).get();
         if (other->combineIfPossible(op.get(), caps)) {
+            fOpMemoryPool->release(std::move(op));
             return;
         }
         if (GrRectsOverlap(op->bounds(), other->bounds())) {
@@ -172,12 +181,21 @@
     fOps.emplace_back(std::move(op));
 }
 
+void SkInternalAtlasTextTarget::deleteOps() {
+    for (int i = 0; i < fOps.count(); ++i) {
+        if (fOps[i]) {
+            fOpMemoryPool->release(std::move(fOps[i]));
+        }
+    }
+    fOps.reset();
+}
+
 void SkInternalAtlasTextTarget::flush() {
     for (int i = 0; i < fOps.count(); ++i) {
         fOps[i]->executeForTextTarget(this);
     }
     this->context()->internal().flush();
-    fOps.reset();
+    this->deleteOps();
 }
 
 void GrAtlasTextOp::finalizeForTextTarget(uint32_t color, const GrCaps& caps) {
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index f987536..4b1af99 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -12,6 +12,7 @@
 #include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrGpu.h"
+#include "GrMemoryPool.h"
 #include "GrProxyProvider.h"
 #include "GrRenderTargetContext.h"
 #include "GrRenderTargetProxy.h"
@@ -811,6 +812,22 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+sk_sp<GrOpMemoryPool> GrContextPriv::refOpMemoryPool() {
+    if (!fContext->fOpMemoryPool) {
+        // DDL TODO: should the size of the memory pool be decreased in DDL mode? CPU-side memory
+        // consumed in DDL mode vs. normal mode for a single skp might be a good metric of wasted
+        // memory.
+        fContext->fOpMemoryPool = sk_sp<GrOpMemoryPool>(new GrOpMemoryPool(16384, 16384));
+    }
+
+    SkASSERT(fContext->fOpMemoryPool);
+    return fContext->fOpMemoryPool;
+}
+
+GrOpMemoryPool* GrContextPriv::opMemoryPool() {
+    return this->refOpMemoryPool().get();
+}
+
 sk_sp<GrSurfaceContext> GrContextPriv::makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy> proxy,
                                                                  sk_sp<SkColorSpace> colorSpace,
                                                                  const SkSurfaceProps* props) {
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 84de056..8d4b1ed 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -13,6 +13,7 @@
 #include "text/GrAtlasManager.h"
 
 class GrBackendRenderTarget;
+class GrOpMemoryPool;
 class GrOnFlushCallbackObject;
 class GrSemaphore;
 class GrSurfaceProxy;
@@ -32,6 +33,9 @@
 
     const GrCaps* caps() const { return fContext->fCaps.get(); }
 
+    sk_sp<GrOpMemoryPool> refOpMemoryPool();
+    GrOpMemoryPool* opMemoryPool();
+
     GrDrawingManager* drawingManager() { return fContext->fDrawingManager.get(); }
 
     sk_sp<GrSurfaceContext> makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy>,
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 99eb04b..7c6338f 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -10,6 +10,7 @@
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrGpu.h"
+#include "GrMemoryPool.h"
 #include "GrOnFlushResourceProvider.h"
 #include "GrOpList.h"
 #include "GrRenderTargetContext.h"
@@ -236,6 +237,15 @@
 #endif
     fOpLists.reset();
 
+#ifdef SK_DEBUG
+    // In non-DDL mode this checks that all the flushed ops have been freed from the memory pool.
+    // When we move to partial flushes this assert will no longer be valid.
+    // In DDL mode this check is somewhat superfluous since the memory for most of the ops/opLists
+    // will be stored in the DDL's GrOpMemoryPools.
+    GrOpMemoryPool* opMemoryPool = fContext->contextPriv().opMemoryPool();
+    opMemoryPool->isEmpty();
+#endif
+
     GrSemaphoresSubmitted result = gpu->finishFlush(numSemaphores, backendSemaphores);
 
     flushState.uninstantiateProxyTracker()->uninstantiateAllProxies();
@@ -420,6 +430,7 @@
 
     sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(
                                                         resourceProvider,
+                                                        fContext->contextPriv().refOpMemoryPool(),
                                                         rtp,
                                                         fContext->contextPriv().getAuditTrail()));
     SkASSERT(rtp->getLastOpList() == opList.get());
@@ -442,6 +453,7 @@
     }
 
     sk_sp<GrTextureOpList> opList(new GrTextureOpList(fContext->contextPriv().resourceProvider(),
+                                                      fContext->contextPriv().refOpMemoryPool(),
                                                       textureProxy,
                                                       fContext->contextPriv().getAuditTrail()));
 
diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h
index 67991d3..15399b6 100644
--- a/src/gpu/GrMemoryPool.h
+++ b/src/gpu/GrMemoryPool.h
@@ -146,6 +146,8 @@
 
     void release(std::unique_ptr<GrOp> op);
 
+    bool isEmpty() const { return fMemoryPool.isEmpty(); }
+
 private:
     GrMemoryPool fMemoryPool;
 };
diff --git a/src/gpu/GrOpList.cpp b/src/gpu/GrOpList.cpp
index 6fed34a..b9f4255 100644
--- a/src/gpu/GrOpList.cpp
+++ b/src/gpu/GrOpList.cpp
@@ -9,6 +9,7 @@
 
 #include "GrContext.h"
 #include "GrDeferredProxyUploader.h"
+#include "GrMemoryPool.h"
 #include "GrSurfaceProxy.h"
 #include "GrTextureProxyPriv.h"
 
@@ -24,11 +25,13 @@
     return id;
 }
 
-GrOpList::GrOpList(GrResourceProvider* resourceProvider,
+GrOpList::GrOpList(GrResourceProvider* resourceProvider, sk_sp<GrOpMemoryPool> opMemoryPool,
                    GrSurfaceProxy* surfaceProxy, GrAuditTrail* auditTrail)
-    : fAuditTrail(auditTrail)
-    , fUniqueID(CreateUniqueID())
-    , fFlags(0) {
+        : fOpMemoryPool(std::move(opMemoryPool))
+        , fAuditTrail(auditTrail)
+        , fUniqueID(CreateUniqueID())
+        , fFlags(0) {
+    SkASSERT(fOpMemoryPool);
     fTarget.setProxy(sk_ref_sp(surfaceProxy), kWrite_GrIOType);
     fTarget.get()->setLastOpList(this);
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index ebb0054..1dbde17 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1726,6 +1726,7 @@
 uint32_t GrRenderTargetContext::addDrawOp(const GrClip& clip, std::unique_ptr<GrDrawOp> op) {
     ASSERT_SINGLE_OWNER
     if (this->drawingManager()->wasAbandoned()) {
+        fContext->contextPriv().opMemoryPool()->release(std::move(op));
         return SK_InvalidUniqueID;
     }
     SkDEBUGCODE(this->validate();)
@@ -1739,6 +1740,7 @@
     if (!clip.apply(fContext, this, fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesHWAA,
                     fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil, &appliedClip,
                     &bounds)) {
+        fContext->contextPriv().opMemoryPool()->release(std::move(op));
         return SK_InvalidUniqueID;
     }
 
@@ -1755,6 +1757,7 @@
     if (GrDrawOp::RequiresDstTexture::kYes == op->finalize(*this->caps(), &appliedClip,
                                                            dstIsClamped)) {
         if (!this->setupDstProxy(this->asRenderTargetProxy(), clip, op->bounds(), &dstProxy)) {
+            fContext->contextPriv().opMemoryPool()->release(std::move(op));
             return SK_InvalidUniqueID;
         }
     }
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index b04ec36..423b71c 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -26,14 +26,29 @@
 static const int kMaxOpLookahead = 10;
 
 GrRenderTargetOpList::GrRenderTargetOpList(GrResourceProvider* resourceProvider,
+                                           sk_sp<GrOpMemoryPool> opMemoryPool,
                                            GrRenderTargetProxy* proxy,
                                            GrAuditTrail* auditTrail)
-        : INHERITED(resourceProvider, proxy, auditTrail)
+        : INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail)
         , fLastClipStackGenID(SK_InvalidUniqueID)
         SkDEBUGCODE(, fNumClips(0)) {
 }
 
+void GrRenderTargetOpList::RecordedOp::deleteOp(GrOpMemoryPool* opMemoryPool) {
+    opMemoryPool->release(std::move(fOp));
+}
+
+void GrRenderTargetOpList::deleteOps() {
+    for (int i = 0; i < fRecordedOps.count(); ++i) {
+        if (fRecordedOps[i].fOp) {
+            fRecordedOps[i].deleteOp(fOpMemoryPool.get());
+        }
+    }
+    fRecordedOps.reset();
+}
+
 GrRenderTargetOpList::~GrRenderTargetOpList() {
+    this->deleteOps();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -188,7 +203,7 @@
 
 void GrRenderTargetOpList::endFlush() {
     fLastClipStackGenID = SK_InvalidUniqueID;
-    fRecordedOps.reset();
+    this->deleteOps();
     fClipAllocator.reset();
     INHERITED::endFlush();
 }
@@ -210,7 +225,7 @@
     // Beware! If we ever add any ops that have a side effect beyond modifying the stencil
     // buffer we will need a more elaborate tracking system (skbug.com/7002).
     if (this->isEmpty() || !fTarget.get()->asRenderTargetProxy()->needsStencil()) {
-        fRecordedOps.reset();
+        this->deleteOps();
         fDeferredProxies.reset();
         fColorLoadOp = GrLoadOp::kClear;
         fLoadClearColor = color;
@@ -257,7 +272,7 @@
         recordedOp.visitProxies(checkInstantiation);
         if (hasUninstantiatedProxy) {
             // When instantiation of the proxy fails we drop the Op
-            recordedOp.fOp = nullptr;
+            recordedOp.deleteOp(fOpMemoryPool.get());
         }
     }
 }
@@ -360,6 +375,7 @@
                 GrOP_INFO("\t\t\tBackward: Combined op info:\n");
                 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get());
+                fOpMemoryPool->release(std::move(op));
                 return SK_InvalidUniqueID;
             }
             // Stop going backwards if we would cause a painter's order violation.
@@ -405,6 +421,7 @@
                           i, op->name(), op->uniqueID(),
                           candidate.fOp->name(), candidate.fOp->uniqueID());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get());
+                fOpMemoryPool->release(std::move(fRecordedOps[j].fOp));
                 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp);
                 break;
             }
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index 00b9f5e..8629a7c 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -32,7 +32,8 @@
     using DstProxy = GrXferProcessor::DstProxy;
 
 public:
-    GrRenderTargetOpList(GrResourceProvider*, GrRenderTargetProxy*, GrAuditTrail*);
+    GrRenderTargetOpList(GrResourceProvider*, sk_sp<GrOpMemoryPool>,
+                         GrRenderTargetProxy*, GrAuditTrail*);
 
     ~GrRenderTargetOpList() override;
 
@@ -122,6 +123,8 @@
 private:
     friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive
 
+    void deleteOps();
+
     struct RecordedOp {
         RecordedOp(std::unique_ptr<GrOp> op, GrAppliedClip* appliedClip, const DstProxy* dstProxy)
                 : fOp(std::move(op)), fAppliedClip(appliedClip) {
@@ -130,7 +133,12 @@
             }
         }
 
-        ~RecordedOp() { }
+        ~RecordedOp() {
+            // The ops are stored in a GrMemoryPool so had better have been handled separately
+            SkASSERT(!fOp);
+        }
+
+        void deleteOp(GrOpMemoryPool* opMemoryPool);
 
         void visitProxies(const GrOp::VisitProxyFunc& func) const {
             if (fOp) {
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index a868b5d..6a601aa 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -11,6 +11,7 @@
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrGpu.h"
+#include "GrMemoryPool.h"
 #include "GrResourceAllocator.h"
 #include "GrTextureProxy.h"
 #include "SkStringUtils.h"
@@ -19,12 +20,28 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GrTextureOpList::GrTextureOpList(GrResourceProvider* resourceProvider,
+                                 sk_sp<GrOpMemoryPool> opMemoryPool,
                                  GrTextureProxy* proxy,
                                  GrAuditTrail* auditTrail)
-    : INHERITED(resourceProvider, proxy, auditTrail) {
+        : INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail) {
+    SkASSERT(fOpMemoryPool);
+}
+
+void GrTextureOpList::deleteOp(int index) {
+    SkASSERT(index >= 0 && index < fRecordedOps.count());
+    fOpMemoryPool->release(std::move(fRecordedOps[index]));
+}
+
+void GrTextureOpList::deleteOps() {
+    for (int i = 0; i < fRecordedOps.count(); ++i) {
+        fOpMemoryPool->release(std::move(fRecordedOps[i]));
+    }
+    fRecordedOps.reset();
+    fOpMemoryPool = nullptr;
 }
 
 GrTextureOpList::~GrTextureOpList() {
+    this->deleteOps();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -107,7 +124,7 @@
 }
 
 void GrTextureOpList::endFlush() {
-    fRecordedOps.reset();
+    this->deleteOps();
     INHERITED::endFlush();
 }
 
@@ -152,7 +169,7 @@
         }
         if (hasUninstantiatedProxy) {
             // When instantiation of the proxy fails we drop the Op
-            fRecordedOps[i] = nullptr;
+            this->deleteOp(i);
         }
     }
 }
diff --git a/src/gpu/GrTextureOpList.h b/src/gpu/GrTextureOpList.h
index d3e3e87..83ec22c 100644
--- a/src/gpu/GrTextureOpList.h
+++ b/src/gpu/GrTextureOpList.h
@@ -23,7 +23,7 @@
 
 class GrTextureOpList final : public GrOpList {
 public:
-    GrTextureOpList(GrResourceProvider*, GrTextureProxy*, GrAuditTrail*);
+    GrTextureOpList(GrResourceProvider*, sk_sp<GrOpMemoryPool>, GrTextureProxy*, GrAuditTrail*);
     ~GrTextureOpList() override;
 
     /**
@@ -61,12 +61,16 @@
     SkDEBUGCODE(int numOps() const override { return fRecordedOps.count(); })
 
 private:
+    void deleteOp(int index);
+    void deleteOps();
+
     void purgeOpsWithUninstantiatedProxies() override;
 
     void gatherProxyIntervals(GrResourceAllocator*) const override;
 
     void recordOp(std::unique_ptr<GrOp>);
 
+    // The memory for the ops in 'fRecordedOps' is actually stored in 'fOpMemoryPool'
     SkSTArray<2, std::unique_ptr<GrOp>, true> fRecordedOps;
 
     typedef GrOpList INHERITED;
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index ad078b2..e3010f9 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -22,9 +22,12 @@
     return false;
 }
 
-std::unique_ptr<GrCCDrawPathsOp> GrCCDrawPathsOp::Make(GrContext*, const SkIRect& clipIBounds,
-                                                       const SkMatrix& m, const GrShape& shape,
-                                                       const SkRect& devBounds, GrPaint&& paint) {
+std::unique_ptr<GrCCDrawPathsOp> GrCCDrawPathsOp::Make(GrContext* context,
+                                                       const SkIRect& clipIBounds,
+                                                       const SkMatrix& m,
+                                                       const GrShape& shape,
+                                                       const SkRect& devBounds,
+                                                       GrPaint&& paint) {
     bool canStashPathMask = true;
     SkIRect looseClippedIBounds;
     devBounds.roundOut(&looseClippedIBounds);  // GrCCPathParser might find slightly tighter bounds.
@@ -34,9 +37,11 @@
             return nullptr;
         }
     }
-    return std::unique_ptr<GrCCDrawPathsOp>(new GrCCDrawPathsOp(looseClippedIBounds, m, shape,
-                                                                canStashPathMask, devBounds,
-                                                                std::move(paint)));
+
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrCCDrawPathsOp>(looseClippedIBounds, m, shape, canStashPathMask,
+                                           devBounds, std::move(paint));
 }
 
 GrCCDrawPathsOp::GrCCDrawPathsOp(const SkIRect& looseClippedIBounds, const SkMatrix& m,
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index 84f905c..a1cecb3 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -53,8 +53,10 @@
                                           sk_sp<const GrCCPerFlushResources> resources,
                                           sk_sp<GrTextureProxy> copyProxy, int baseInstance,
                                           int endInstance, const SkISize& drawBounds) {
-        return std::unique_ptr<GrDrawOp>(new CopyAtlasOp(std::move(resources), std::move(copyProxy),
-                                                         baseInstance, endInstance, drawBounds));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<CopyAtlasOp>(std::move(resources), std::move(copyProxy),
+                                           baseInstance, endInstance, drawBounds);
     }
 
     const char* name() const override { return "CopyAtlasOp (CCPR)"; }
@@ -96,8 +98,9 @@
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           sk_sp<const GrCCPerFlushResources> resources,
                                           CoverageCountBatchID batchID, const SkISize& drawBounds) {
-        return std::unique_ptr<GrDrawOp>(new RenderAtlasOp(std::move(resources), batchID,
-                                                           drawBounds));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<RenderAtlasOp>(std::move(resources), batchID, drawBounds);
     }
 
     // GrDrawOp interface.
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index eb39079..1ea3ddf 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -8,6 +8,7 @@
 #include "GrAtlasTextOp.h"
 
 #include "GrContext.h"
+#include "GrContextPriv.h"
 #include "GrMemoryPool.h"
 #include "GrOpFlushState.h"
 #include "GrResourceProvider.h"
@@ -27,7 +28,9 @@
                                                          GrMaskFormat maskFormat,
                                                          int glyphCount,
                                                          bool needsTransform) {
-        std::unique_ptr<GrAtlasTextOp> op(new GrAtlasTextOp(std::move(paint)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
 
         switch (maskFormat) {
             case kA8_GrMaskFormat:
@@ -57,7 +60,9 @@
                                             const SkSurfaceProps& props,
                                             bool isAntiAliased,
                                             bool useLCD) {
-        std::unique_ptr<GrAtlasTextOp> op(new GrAtlasTextOp(std::move(paint)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        std::unique_ptr<GrAtlasTextOp> op = pool->allocate<GrAtlasTextOp>(std::move(paint));
 
         bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
         bool isLCD = useLCD && SkPixelGeometryIsH(props.pixelGeometry());
diff --git a/src/gpu/ops/GrClearOp.cpp b/src/gpu/ops/GrClearOp.cpp
index 70fc160..612ff20 100644
--- a/src/gpu/ops/GrClearOp.cpp
+++ b/src/gpu/ops/GrClearOp.cpp
@@ -21,7 +21,9 @@
         return nullptr;
     }
 
-    return std::unique_ptr<GrClearOp>(new GrClearOp(clip, color, dstProxy));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrClearOp>(clip, color, dstProxy);
 }
 
 std::unique_ptr<GrClearOp> GrClearOp::Make(GrContext* context,
@@ -30,7 +32,9 @@
                                            bool fullScreen) {
     SkASSERT(fullScreen || !rect.isEmpty());
 
-    return std::unique_ptr<GrClearOp>(new GrClearOp(rect, color, fullScreen));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrClearOp>(rect, color, fullScreen);
 }
 
 GrClearOp::GrClearOp(const GrFixedClip& clip, GrColor color, GrSurfaceProxy* proxy)
diff --git a/src/gpu/ops/GrClearStencilClipOp.cpp b/src/gpu/ops/GrClearStencilClipOp.cpp
index 9a0e75a..64985b7 100644
--- a/src/gpu/ops/GrClearStencilClipOp.cpp
+++ b/src/gpu/ops/GrClearStencilClipOp.cpp
@@ -14,7 +14,9 @@
                                                  const GrFixedClip& clip,
                                                  bool insideStencilMask,
                                                  GrRenderTargetProxy* proxy) {
-    return std::unique_ptr<GrOp>(new GrClearStencilClipOp(clip, insideStencilMask, proxy));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrClearStencilClipOp>(clip, insideStencilMask, proxy);
 }
 
 void GrClearStencilClipOp::onExecute(GrOpFlushState* state) {
diff --git a/src/gpu/ops/GrCopySurfaceOp.cpp b/src/gpu/ops/GrCopySurfaceOp.cpp
index da8e910..a90579b 100644
--- a/src/gpu/ops/GrCopySurfaceOp.cpp
+++ b/src/gpu/ops/GrCopySurfaceOp.cpp
@@ -78,8 +78,9 @@
         return nullptr;
     }
 
-    return std::unique_ptr<GrOp>(new GrCopySurfaceOp(dstProxy, srcProxy,
-                                                     clippedSrcRect, clippedDstPoint));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrCopySurfaceOp>(dstProxy, srcProxy, clippedSrcRect, clippedDstPoint);
 }
 
 void GrCopySurfaceOp::onExecute(GrOpFlushState* state) {
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index b784b05..cafb3e5 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -271,8 +271,10 @@
                                           SkPaint::Cap cap,
                                           AAMode aaMode, bool fullDash,
                                           const GrUserStencilSettings* stencilSettings) {
-        return std::unique_ptr<GrDrawOp>(
-                new DashOp(std::move(paint), geometry, cap, aaMode, fullDash, stencilSettings));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<DashOp>(std::move(paint), geometry, cap,
+                                      aaMode, fullDash, stencilSettings);
     }
 
     const char* name() const override { return "DashOp"; }
diff --git a/src/gpu/ops/GrDebugMarkerOp.cpp b/src/gpu/ops/GrDebugMarkerOp.cpp
index ed54965..712e625 100644
--- a/src/gpu/ops/GrDebugMarkerOp.cpp
+++ b/src/gpu/ops/GrDebugMarkerOp.cpp
@@ -17,7 +17,9 @@
 std::unique_ptr<GrOp> GrDebugMarkerOp::Make(GrContext* context,
                                             GrRenderTargetProxy* proxy,
                                             const SkString& str) {
-    return std::unique_ptr<GrOp>(new GrDebugMarkerOp(proxy, str));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrDebugMarkerOp>(proxy, str);
 }
 
 void GrDebugMarkerOp::onExecute(GrOpFlushState* state) {
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index a5a98f2..a8081fc 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -69,8 +69,9 @@
                                              GrPaint&& paint,
                                              GrAAType aaType,
                                              GrPath* path) {
-    return std::unique_ptr<GrDrawOp>(
-                new GrDrawPathOp(viewMatrix, std::move(paint), aaType, path));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrDrawPathOp>(viewMatrix, std::move(paint), aaType, path);
 }
 
 void GrDrawPathOp::onExecute(GrOpFlushState* state) {
diff --git a/src/gpu/ops/GrOp.cpp b/src/gpu/ops/GrOp.cpp
index f020cde..cab8ca1 100644
--- a/src/gpu/ops/GrOp.cpp
+++ b/src/gpu/ops/GrOp.cpp
@@ -7,57 +7,27 @@
 
 #include "GrOp.h"
 
-#include "GrMemoryPool.h"
-#include "SkSpinlock.h"
-#include "SkTo.h"
-
-// TODO I noticed a small benefit to using a larger exclusive pool for ops. Its very small, but
-// seems to be mostly consistent.  There is a lot in flux right now, but we should really revisit
-// this.
-
-
-// We use a global pool protected by a mutex(spinlock). Chrome may use the same GrContext on
-// different threads. The GrContext is not used concurrently on different threads and there is a
-// memory barrier between accesses of a context on different threads. Also, there may be multiple
-// GrContexts and those contexts may be in use concurrently on different threads.
-namespace {
-#if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
-static SkSpinlock gOpPoolSpinLock;
-#endif
-class MemoryPoolAccessor {
-public:
-
-// We know in the Android framework there is only one GrContext.
-#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
-    MemoryPoolAccessor() {}
-    ~MemoryPoolAccessor() {}
-#else
-    MemoryPoolAccessor() { gOpPoolSpinLock.acquire(); }
-    ~MemoryPoolAccessor() { gOpPoolSpinLock.release(); }
-#endif
-
-    GrMemoryPool* pool() const {
-        static GrMemoryPool gPool(16384, 16384);
-        return &gPool;
-    }
-};
-}
-
 int32_t GrOp::gCurrOpClassID = GrOp::kIllegalOpID;
 
 int32_t GrOp::gCurrOpUniqueID = GrOp::kIllegalOpID;
 
+#ifdef SK_DEBUG
 void* GrOp::operator new(size_t size) {
-    return MemoryPoolAccessor().pool()->allocate(size);
+    // All GrOp-derived class should be allocated in a GrMemoryPool
+    SkASSERT(0);
+    return ::operator new(size);
 }
 
 void GrOp::operator delete(void* target) {
-    return MemoryPoolAccessor().pool()->release(target);
+    // All GrOp-derived class should be released from their owning GrMemoryPool
+    SkASSERT(0);
+    ::operator delete(target);
 }
+#endif
 
 GrOp::GrOp(uint32_t classID)
-    : fClassID(classID)
-    , fUniqueID(kIllegalOpID) {
+        : fClassID(classID)
+        , fUniqueID(kIllegalOpID) {
     SkASSERT(classID == SkToU32(fClassID));
     SkDEBUGCODE(fBoundsFlags = kUninitialized_BoundsFlag);
 }
diff --git a/src/gpu/ops/GrOp.h b/src/gpu/ops/GrOp.h
index 5d79223..5c776fc 100644
--- a/src/gpu/ops/GrOp.h
+++ b/src/gpu/ops/GrOp.h
@@ -104,6 +104,8 @@
         return SkToBool(fBoundsFlags & kZeroArea_BoundsFlag);
     }
 
+#ifdef SK_DEBUG
+    // All GrOp-derived classes should be allocated in and deleted from a GrMemoryPool
     void* operator new(size_t size);
     void operator delete(void* target);
 
@@ -113,6 +115,7 @@
     void operator delete(void* target, void* placement) {
         ::operator delete(target, placement);
     }
+#endif
 
     /**
      * Helper for safely down-casting to a GrOp subclass
diff --git a/src/gpu/ops/GrSemaphoreOp.cpp b/src/gpu/ops/GrSemaphoreOp.cpp
index fedf6b7..9beb2a6 100644
--- a/src/gpu/ops/GrSemaphoreOp.cpp
+++ b/src/gpu/ops/GrSemaphoreOp.cpp
@@ -21,9 +21,9 @@
                                       sk_sp<GrSemaphore> semaphore,
                                       GrRenderTargetProxy* proxy,
                                       bool forceFlush) {
-        return std::unique_ptr<GrSignalSemaphoreOp>(new GrSignalSemaphoreOp(std::move(semaphore),
-                                                                            proxy,
-                                                                            forceFlush));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<GrSignalSemaphoreOp>(std::move(semaphore), proxy, forceFlush);
     }
 
     const char* name() const override { return "SignalSemaphore"; }
@@ -51,8 +51,9 @@
     static std::unique_ptr<GrOp> Make(GrContext* context,
                                       sk_sp<GrSemaphore> semaphore,
                                       GrRenderTargetProxy* proxy) {
-        return std::unique_ptr<GrWaitSemaphoreOp>(new GrWaitSemaphoreOp(std::move(semaphore),
-                                                                        proxy));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<GrWaitSemaphoreOp>(std::move(semaphore), proxy);
     }
 
     const char* name() const override { return "WaitSemaphore"; }
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index a2ec230..f61d11d 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -678,12 +678,14 @@
     SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
     SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
 
-    return std::unique_ptr<GrDrawOp>(new ShadowCircularRRectOp(color, bounds,
-                                                               scaledRadius,
-                                                               rrect.isOval(),
-                                                               blurWidth,
-                                                               scaledInsetWidth,
-                                                               blurClamp));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<ShadowCircularRRectOp>(color, bounds,
+                                                 scaledRadius,
+                                                 rrect.isOval(),
+                                                 blurWidth,
+                                                 scaledInsetWidth,
+                                                 blurClamp);
 }
 }
 
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index b63728f..e5229a7 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -174,19 +174,22 @@
 std::unique_ptr<GrDrawOp> GrSimpleMeshDrawOpHelper::FactoryHelper(GrContext* context,
                                                                   GrPaint&& paint,
                                                                   OpArgs... opArgs) {
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
     MakeArgs makeArgs;
     makeArgs.fSRGBFlags = GrPipeline::SRGBFlagsFromPaint(paint);
     GrColor color = paint.getColor();
 
     if (paint.isTrivial()) {
         makeArgs.fProcessorSet = nullptr;
-        return std::unique_ptr<GrDrawOp>(new Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+        return pool->allocate<Op>(makeArgs, color, std::forward<OpArgs>(opArgs)...);
     } else {
-        char* mem = (char*)GrOp::operator new(sizeof(Op) + sizeof(GrProcessorSet));
+        char* mem = (char*) pool->allocate(sizeof(Op) + sizeof(GrProcessorSet));
         char* setMem = mem + sizeof(Op);
         makeArgs.fProcessorSet = new (setMem) GrProcessorSet(std::move(paint));
-        return std::unique_ptr<GrDrawOp>(
-                new (mem) Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+
+        return std::unique_ptr<GrDrawOp>(new (mem) Op(makeArgs, color,
+                                                      std::forward<OpArgs>(opArgs)...));
     }
 }
 
diff --git a/src/gpu/ops/GrStencilPathOp.cpp b/src/gpu/ops/GrStencilPathOp.cpp
index 5b712cf..25dbf31 100644
--- a/src/gpu/ops/GrStencilPathOp.cpp
+++ b/src/gpu/ops/GrStencilPathOp.cpp
@@ -21,8 +21,10 @@
                                             bool hasStencilClip,
                                             const GrScissorState& scissor,
                                             const GrPath* path) {
-    return std::unique_ptr<GrOp>(new GrStencilPathOp(viewMatrix, useHWAA, fillType,
-                                                     hasStencilClip, scissor, path));
+    GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+    return pool->allocate<GrStencilPathOp>(viewMatrix, useHWAA, fillType,
+                                           hasStencilClip, scissor, path);
 }
 
 void GrStencilPathOp::onExecute(GrOpFlushState* state) {
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 80174bc..e06d94f 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -621,9 +621,11 @@
                                           SkCanvas::SrcRectConstraint constraint,
                                           const SkMatrix& viewMatrix,
                                           sk_sp<GrColorSpaceXform> csxf) {
-        return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
-                                                       dstRect, aaType, constraint, viewMatrix,
-                                                       std::move(csxf)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<TextureOp>(std::move(proxy), filter, color,
+                                         srcRect, dstRect, aaType, constraint,
+                                         viewMatrix, std::move(csxf));
     }
 
     ~TextureOp() override {
diff --git a/tests/DrawOpAtlasTest.cpp b/tests/DrawOpAtlasTest.cpp
index 0a5f991..e065bf0 100644
--- a/tests/DrawOpAtlasTest.cpp
+++ b/tests/DrawOpAtlasTest.cpp
@@ -13,6 +13,7 @@
 #include "GrDeferredUpload.h"
 #include "GrDrawOpAtlas.h"
 #include "GrDrawingManager.h"
+#include "GrMemoryPool.h"
 #include "GrOnFlushResourceProvider.h"
 #include "GrOpFlushState.h"
 #include "GrRenderTargetContext.h"
@@ -181,6 +182,7 @@
     auto resourceProvider = context->contextPriv().resourceProvider();
     auto drawingManager = context->contextPriv().drawingManager();
     auto textContext = drawingManager->getTextContext();
+    auto opMemoryPool = context->contextPriv().opMemoryPool();
 
     auto rtc =  context->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
                                                                        32, 32,
@@ -222,4 +224,5 @@
     flushState.setOpArgs(&opArgs);
     op->prepare(&flushState);
     flushState.setOpArgs(nullptr);
+    opMemoryPool->release(std::move(op));
 }
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
index 4535f13..4d9773b 100644
--- a/tests/GrMeshTest.cpp
+++ b/tests/GrMeshTest.cpp
@@ -258,7 +258,9 @@
 
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           std::function<void(DrawMeshHelper*)> testFn) {
-        return std::unique_ptr<GrDrawOp>(new GrMeshTestOp(testFn));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<GrMeshTestOp>(testFn);
     }
 
 private:
diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp
index 0f786e7..a197650 100644
--- a/tests/GrPipelineDynamicStateTest.cpp
+++ b/tests/GrPipelineDynamicStateTest.cpp
@@ -109,8 +109,9 @@
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           ScissorState scissorState,
                                           sk_sp<const GrBuffer> vbuff) {
-        return std::unique_ptr<GrDrawOp>(new GrPipelineDynamicStateTestOp(scissorState,
-                                                                          std::move(vbuff)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<GrPipelineDynamicStateTestOp>(scissorState, std::move(vbuff));
     }
 
 private:
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index 9349457..aaf1ab9 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -59,7 +59,9 @@
                                               GrProxyProvider* proxyProvider,
                                               LazyProxyTest* test,
                                               bool nullTexture) {
-            return std::unique_ptr<GrDrawOp>(new Op(proxyProvider, test, nullTexture));
+            GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+            return pool->allocate<Op>(proxyProvider, test, nullTexture);
         }
 
         void visitProxies(const VisitProxyFunc& func) const override {
@@ -274,8 +276,11 @@
                                           GrProxyProvider* proxyProvider,
                                           int* testExecuteValue,
                                           bool shouldFailInstantiation) {
-        return std::unique_ptr<GrDrawOp>(new LazyFailedInstantiationTestOp(
-            proxyProvider, testExecuteValue, shouldFailInstantiation));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<LazyFailedInstantiationTestOp>(proxyProvider,
+                                                             testExecuteValue,
+                                                             shouldFailInstantiation);
     }
 
     void visitProxies(const VisitProxyFunc& func) const override {
@@ -371,7 +376,9 @@
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrDrawOp> Make(GrContext* context, sk_sp<GrTextureProxy> proxy) {
-        return std::unique_ptr<GrDrawOp>(new LazyUninstantiateTestOp(std::move(proxy)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<LazyUninstantiateTestOp>(std::move(proxy));
     }
 
     void visitProxies(const VisitProxyFunc& func) const override {
diff --git a/tests/PrimitiveProcessorTest.cpp b/tests/PrimitiveProcessorTest.cpp
index 3a76e6c..74a73a6 100644
--- a/tests/PrimitiveProcessorTest.cpp
+++ b/tests/PrimitiveProcessorTest.cpp
@@ -33,7 +33,9 @@
     const char* name() const override { return "Dummy Op"; }
 
     static std::unique_ptr<GrDrawOp> Make(GrContext* context, int numAttribs) {
-        return std::unique_ptr<GrDrawOp>(new Op(numAttribs));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<Op>(numAttribs);
     }
 
     FixedFunctionFlags fixedFunctionFlags() const override {
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index 3916436..e52070b 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -29,7 +29,9 @@
     DEFINE_OP_CLASS_ID
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           std::unique_ptr<GrFragmentProcessor> fp) {
-        return std::unique_ptr<GrDrawOp>(new TestOp(std::move(fp)));
+        GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
+
+        return pool->allocate<TestOp>(std::move(fp));
     }
 
     const char* name() const override { return "TestOp"; }
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index e859886..a827442 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -12,6 +12,7 @@
 #include "GrDrawingManager.h"
 #include "GrGpu.h"
 #include "GrGpuResourceCacheAccess.h"
+#include "GrMemoryPool.h"
 #include "GrRenderTargetContext.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrRenderTargetProxy.h"
@@ -260,6 +261,7 @@
                                                           std::unique_ptr<GrDrawOp> op) {
     ASSERT_SINGLE_OWNER
     if (fRenderTargetContext->drawingManager()->wasAbandoned()) {
+        fRenderTargetContext->fContext->contextPriv().opMemoryPool()->release(std::move(op));
         return SK_InvalidUniqueID;
     }
     SkDEBUGCODE(fRenderTargetContext->validate());