Add creation-time POD memory pool for GrOps

This CL begins pulling some of the work forward into onPrePrepare.

Change-Id: If049e0662db51b465b8b82aafebeef2323bddfd4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/249802
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/include/private/GrRecordingContext.h b/include/private/GrRecordingContext.h
index 1f44bee..39d851c 100644
--- a/include/private/GrRecordingContext.h
+++ b/include/private/GrRecordingContext.h
@@ -22,6 +22,7 @@
 class GrSurfaceProxy;
 class GrTextBlobCache;
 class GrTextureContext;
+class SkArenaAlloc;
 
 class GrRecordingContext : public GrImageContext {
 public:
@@ -49,6 +50,11 @@
     sk_sp<GrOpMemoryPool> refOpMemoryPool();
     GrOpMemoryPool* opMemoryPool();
 
+    SkArenaAlloc* opPODAllocator();
+    // This entry point should only be used for DDL creation where we want the ops' POD lifetime
+    // to match that of the DDL.
+    std::unique_ptr<SkArenaAlloc> detachOpPOD();
+
     GrStrikeCache* getGrStrikeCache() { return fStrikeCache.get(); }
     GrTextBlobCache* getTextBlobCache();
     const GrTextBlobCache* getTextBlobCache() const;
@@ -125,6 +131,7 @@
     std::unique_ptr<GrDrawingManager> fDrawingManager;
     // All the GrOp-derived classes use this pool.
     sk_sp<GrOpMemoryPool>             fOpMemoryPool;
+    std::unique_ptr<SkArenaAlloc>     fOpPODAllocator;
 
     std::unique_ptr<GrStrikeCache>    fStrikeCache;
     std::unique_ptr<GrTextBlobCache>  fTextBlobCache;
diff --git a/include/private/SkDeferredDisplayList.h b/include/private/SkDeferredDisplayList.h
index e8fc35b..b90995c 100644
--- a/include/private/SkDeferredDisplayList.h
+++ b/include/private/SkDeferredDisplayList.h
@@ -59,7 +59,7 @@
     const SkDeferredDisplayListPriv priv() const;
 
 private:
-    friend class GrDrawingManager; // for access to 'fRenderTasks' and 'fLazyProxyData'
+    friend class GrDrawingManager; // for access to 'fRenderTasks', 'fLazyProxyData', 'fOpPOD'
     friend class SkDeferredDisplayListRecorder; // for access to 'fLazyProxyData'
     friend class SkDeferredDisplayListPriv;
 
@@ -71,6 +71,7 @@
 
     SkTArray<sk_sp<GrRenderTask>>   fRenderTasks;
     PendingPathsMap                 fPendingPaths;  // This is the path data from CCPR.
+    std::unique_ptr<SkArenaAlloc>   fOpPOD;
 #endif
     sk_sp<LazyProxyData>            fLazyProxyData;
 };
diff --git a/src/core/SkDeferredDisplayList.cpp b/src/core/SkDeferredDisplayList.cpp
index c89cd99..c718433 100644
--- a/src/core/SkDeferredDisplayList.cpp
+++ b/src/core/SkDeferredDisplayList.cpp
@@ -8,6 +8,7 @@
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkTypes.h"
 #include "include/private/SkDeferredDisplayList.h"
+#include "src/core/SkArenaAlloc.h"
 #include <utility>
 class SkSurfaceCharacterization;
 
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 6b2990d..ba89eca 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -581,6 +581,8 @@
         renderTask->prePrepare(fContext);
     }
 
+    ddl->fOpPOD = fContext->priv().detachOpPOD();
+
     if (fPathRendererChain) {
         if (auto ccpr = fPathRendererChain->getCoverageCountingPathRenderer()) {
             ddl->fPendingPaths = ccpr->detachPendingPaths();
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index 63ff79f..4306ad0 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -406,7 +406,7 @@
 
     for (const auto& chain : fOpChains) {
         if (chain.shouldExecute()) {
-            chain.head()->prePrepare(context);
+            chain.head()->prePrepare(context, chain.appliedClip());
         }
     }
 }
diff --git a/src/gpu/GrRecordingContext.cpp b/src/gpu/GrRecordingContext.cpp
index 081b693..58eb8c5 100644
--- a/src/gpu/GrRecordingContext.cpp
+++ b/src/gpu/GrRecordingContext.cpp
@@ -8,6 +8,7 @@
 #include "include/private/GrRecordingContext.h"
 
 #include "include/gpu/GrContext.h"
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrAuditTrail.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrDrawingManager.h"
@@ -115,6 +116,9 @@
     return fDrawingManager.get();
 }
 
+// This entry point exists bc the GrOpsTask (and SkAtlasTextTarget) take refs on the memory pool.
+// Ostensibly, this is to keep the op's data alive in DDL mode but the back pointer is also
+// used for deletion.
 sk_sp<GrOpMemoryPool> GrRecordingContext::refOpMemoryPool() {
     if (!fOpMemoryPool) {
         // DDL TODO: should the size of the memory pool be decreased in DDL mode? CPU-side memory
@@ -131,6 +135,22 @@
     return this->refOpMemoryPool().get();
 }
 
+// Stored in this arena:
+//     GrTextureOp's DynamicStateArrays and FixedDynamicState
+SkArenaAlloc* GrRecordingContext::opPODAllocator() {
+    if (!fOpPODAllocator) {
+        // TODO: empirically determine a better number for SkArenaAlloc's firstHeapAllocation param
+        fOpPODAllocator = std::unique_ptr<SkArenaAlloc>(new SkArenaAlloc(sizeof(GrPipeline) * 100));
+    }
+
+    SkASSERT(fOpPODAllocator);
+    return fOpPODAllocator.get();
+}
+
+std::unique_ptr<SkArenaAlloc> GrRecordingContext::detachOpPOD() {
+    return std::move(fOpPODAllocator);
+}
+
 GrTextBlobCache* GrRecordingContext::getTextBlobCache() {
     return fTextBlobCache.get();
 }
@@ -302,6 +322,10 @@
     return fContext->refCaps();
 }
 
+std::unique_ptr<SkArenaAlloc> GrRecordingContextPriv::detachOpPOD() {
+    return fContext->detachOpPOD();
+}
+
 sk_sp<GrSkSLFPFactoryCache> GrRecordingContextPriv::fpFactoryCache() {
     return fContext->fpFactoryCache();
 }
diff --git a/src/gpu/GrRecordingContextPriv.h b/src/gpu/GrRecordingContextPriv.h
index 2987778..dfaf914 100644
--- a/src/gpu/GrRecordingContextPriv.h
+++ b/src/gpu/GrRecordingContextPriv.h
@@ -46,6 +46,9 @@
     sk_sp<GrOpMemoryPool> refOpMemoryPool();
     GrOpMemoryPool* opMemoryPool() { return fContext->opMemoryPool(); }
 
+    SkArenaAlloc* opPODAllocator() { return fContext->opPODAllocator(); }
+    std::unique_ptr<SkArenaAlloc> detachOpPOD();
+
     GrStrikeCache* getGrStrikeCache() { return fContext->getGrStrikeCache(); }
     GrTextBlobCache* getTextBlobCache() { return fContext->getTextBlobCache(); }
 
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 0ba8e43..77dcf6d 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -75,32 +75,37 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrPipeline::DynamicStateArrays* GrMeshDrawOp::Target::allocDynamicStateArrays(
-        int numMeshes, int numPrimitiveProcessorTextures, bool allocScissors) {
-    auto result = this->allocator()->make<GrPipeline::DynamicStateArrays>();
+GrPipeline::DynamicStateArrays* GrMeshDrawOp::Target::AllocDynamicStateArrays(
+        SkArenaAlloc* arena, int numMeshes, int numPrimitiveProcessorTextures,
+        bool allocScissors) {
+
+    auto result = arena->make<GrPipeline::DynamicStateArrays>();
+
     if (allocScissors) {
-        result->fScissorRects = this->allocator()->makeArray<SkIRect>(numMeshes);
+        result->fScissorRects = arena->makeArray<SkIRect>(numMeshes);
     }
+
     if (numPrimitiveProcessorTextures) {
-        result->fPrimitiveProcessorTextures =
-                this->allocator()->makeArrayDefault<GrTextureProxy*>(
+        result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrTextureProxy*>(
                         numPrimitiveProcessorTextures * numMeshes);
     }
+
     return result;
 }
 
-GrPipeline::FixedDynamicState* GrMeshDrawOp::Target::makeFixedDynamicState(
-        int numPrimProcTextures) {
-    const GrAppliedClip* clip = this->appliedClip();
+GrPipeline::FixedDynamicState* GrMeshDrawOp::Target::MakeFixedDynamicState(
+        SkArenaAlloc* arena, const GrAppliedClip* clip, int numPrimProcTextures) {
+
     if ((clip && clip->scissorState().enabled()) || numPrimProcTextures) {
         const SkIRect& scissor = (clip) ? clip->scissorState().rect() : SkIRect::MakeEmpty();
-        auto fixedDynamicState =
-                this->allocator()->make<GrPipeline::FixedDynamicState>(scissor);
+
+        auto result = arena->make<GrPipeline::FixedDynamicState>(scissor);
+
         if (numPrimProcTextures) {
-            fixedDynamicState->fPrimitiveProcessorTextures =
-                    this->allocator()->makeArrayDefault<GrTextureProxy*>(numPrimProcTextures);
+            result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrTextureProxy*>(
+                        numPrimProcTextures);
         }
-        return fixedDynamicState;
+        return result;
     }
     return nullptr;
 }
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 6bdb637..91286d1 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -72,11 +72,13 @@
     };
 
 private:
-    void onPrePrepare(GrRecordingContext* context) final { this->onPrePrepareDraws(context); }
+    void onPrePrepare(GrRecordingContext* context, const GrAppliedClip* clip) final {
+        this->onPrePrepareDraws(context, clip);
+    }
     void onPrepare(GrOpFlushState* state) final;
 
     // Only the GrTextureOp currently overrides this virtual
-    virtual void onPrePrepareDraws(GrRecordingContext*) {}
+    virtual void onPrePrepareDraws(GrRecordingContext*, const GrAppliedClip*) {}
 
     virtual void onPrepareDraws(Target*) = 0;
     typedef GrDrawOp INHERITED;
@@ -146,11 +148,20 @@
 
     GrMesh* allocMeshes(int n) { return this->allocator()->makeArray<GrMesh>(n); }
 
-    GrPipeline::DynamicStateArrays* allocDynamicStateArrays(int numMeshes,
-                                                            int numPrimitiveProcessorTextures,
-                                                            bool allocScissors);
+    static GrPipeline::DynamicStateArrays* AllocDynamicStateArrays(SkArenaAlloc*,
+                                                                   int numMeshes,
+                                                                   int numPrimitiveProcTextures,
+                                                                   bool allocScissors);
 
-    GrPipeline::FixedDynamicState* makeFixedDynamicState(int numPrimitiveProcessorTextures);
+    static GrPipeline::FixedDynamicState* MakeFixedDynamicState(SkArenaAlloc*,
+                                                                const GrAppliedClip* clip,
+                                                                int numPrimitiveProcessorTextures);
+
+
+    GrPipeline::FixedDynamicState* makeFixedDynamicState(int numPrimitiveProcessorTextures) {
+        return MakeFixedDynamicState(this->allocator(), this->appliedClip(),
+                                     numPrimitiveProcessorTextures);
+    }
 
     virtual GrRenderTargetProxy* proxy() const = 0;
 
@@ -174,7 +185,6 @@
 
     virtual GrDeferredUploadTarget* deferredUploadTarget() = 0;
 
-private:
     virtual SkArenaAlloc* allocator() = 0;
 };
 
diff --git a/src/gpu/ops/GrOp.h b/src/gpu/ops/GrOp.h
index 0ad0a2b..9a250a3 100644
--- a/src/gpu/ops/GrOp.h
+++ b/src/gpu/ops/GrOp.h
@@ -18,6 +18,7 @@
 #include <atomic>
 #include <new>
 
+class GrAppliedClip;
 class GrCaps;
 class GrOpFlushState;
 class GrOpsRenderPass;
@@ -158,7 +159,9 @@
      * onPrePrepare must be prepared to handle both cases (when onPrePrepare has been called
      * ahead of time and when it has not been called).
      */
-    void prePrepare(GrRecordingContext* context) { this->onPrePrepare(context); }
+    void prePrepare(GrRecordingContext* context, GrAppliedClip* clip) {
+        this->onPrePrepare(context, clip);
+    }
 
     /**
      * Called prior to executing. The op should perform any resource creation or data transfers
@@ -289,7 +292,7 @@
     }
 
     // Only GrMeshDrawOp currently overrides this virtual
-    virtual void onPrePrepare(GrRecordingContext*) {}
+    virtual void onPrePrepare(GrRecordingContext*, const GrAppliedClip*) {}
     virtual void onPrepare(GrOpFlushState*) = 0;
     // If this op is chained then chainBounds is the union of the bounds of all ops in the chain.
     // Otherwise, this op's bounds.
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 9da40bd..3cbefea 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -427,11 +427,32 @@
         }
     }
 
-    void onPrePrepareDraws(GrRecordingContext* context) override {
-        SkASSERT(!fPrePrepared);
-        // Pull forward the tessellation of the quads to here
+    void onPrePrepareDraws(GrRecordingContext* context, const GrAppliedClip* clip) override {
+        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
 
-        //GrOpMemoryPool* pool = context->priv().opMemoryPool();
+        SkDEBUGCODE(this->validate();)
+        SkASSERT(!fPrePrepared);
+
+        int numProxies, numTotalQuads;
+
+        SkArenaAlloc* arena = context->priv().opPODAllocator();
+
+        const VertexSpec vertexSpec = this->characterize(&numProxies, &numTotalQuads);
+        (void) vertexSpec;
+
+        // We'll use a dynamic state array for the GP textures when there are multiple ops.
+        // Otherwise, we use fixed dynamic state to specify the single op's proxy.
+        // Note: these are being allocated in the opPOD arena not the flush state!
+        SkASSERT(!fDynamicStateArrays && !fFixedDynamicState);
+        if (numProxies > 1) {
+            fDynamicStateArrays = Target::AllocDynamicStateArrays(arena, numProxies, 1, false);
+            fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 0);
+        } else {
+            fFixedDynamicState = Target::MakeFixedDynamicState(arena, clip, 1);
+            fFixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
+        }
+
+        // Pull forward the tessellation of the quads to here
 
         fPrePrepared = true;
     }
@@ -509,12 +530,22 @@
         // Otherwise, we use fixed dynamic state to specify the single op's proxy.
         GrPipeline::DynamicStateArrays* dynamicStateArrays = nullptr;
         GrPipeline::FixedDynamicState* fixedDynamicState;
-        if (numProxies > 1) {
-            dynamicStateArrays = target->allocDynamicStateArrays(numProxies, 1, false);
-            fixedDynamicState = target->makeFixedDynamicState(0);
+
+        if (fPrePrepared) {
+            dynamicStateArrays = fDynamicStateArrays;
+            fixedDynamicState =  fFixedDynamicState;
         } else {
-            fixedDynamicState = target->makeFixedDynamicState(1);
-            fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
+            SkArenaAlloc* arena = target->allocator();
+
+            SkASSERT(!fDynamicStateArrays && !fFixedDynamicState);
+
+            if (numProxies > 1) {
+                dynamicStateArrays = Target::AllocDynamicStateArrays(arena, numProxies, 1, false);
+                fixedDynamicState = Target::MakeFixedDynamicState(arena, target->appliedClip(), 0);
+            } else {
+                fixedDynamicState = Target::MakeFixedDynamicState(arena, target->appliedClip(), 1);
+                fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxies[0].fProxy;
+            }
         }
 
         size_t vertexSize = vertexSpec.vertexSize();
@@ -667,6 +698,11 @@
 
     GrQuadBuffer<ColorDomainAndAA> fQuads;
     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+    // fDynamicStateArrays and fFixedDynamicState are only filled in when this op has been
+    // prePrepared. In that case they've been allocated in the opPOD arena not in the
+    // FlushState arena.
+    GrPipeline::DynamicStateArrays* fDynamicStateArrays = nullptr;
+    GrPipeline::FixedDynamicState* fFixedDynamicState = nullptr;
     unsigned fSaturate : 1;
     unsigned fFilter : 2;
     unsigned fAAType : 2;
diff --git a/tests/OpChainTest.cpp b/tests/OpChainTest.cpp
index 8a523b1..c662843 100644
--- a/tests/OpChainTest.cpp
+++ b/tests/OpChainTest.cpp
@@ -205,9 +205,9 @@
                 GrOpFlushState flushState(context->priv().getGpu(),
                                           context->priv().resourceProvider(),
                                           &tracker);
-                GrOpsTask opsTask(sk_ref_sp(context->priv().opMemoryPool()),
-                                            sk_ref_sp(proxy->asRenderTargetProxy()),
-                                            context->priv().auditTrail());
+                GrOpsTask opsTask(context->priv().refOpMemoryPool(),
+                                  sk_ref_sp(proxy->asRenderTargetProxy()),
+                                  context->priv().auditTrail());
                 // This assumes the particular values of kRanges.
                 std::fill_n(result, result_width(), -1);
                 std::fill_n(validResult, result_width(), -1);