Add pre-Flush callback to GrDrawingManager (take 2)

This will allow internal systems (e.g., fonts & path renderers) to create pre-flush atlases.

Depends on: https://skia-review.googlesource.com/c/8988/ (Allow GrSurfaceProxy-derived classes to use flags when instantiating)

Change-Id: I7ffc1b69defda625b6d4311e96776de4cf2abb87
Reviewed-on: https://skia-review.googlesource.com/9903
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index c0d63a3..bcb93b3 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -711,6 +711,11 @@
                                                            surfaceProps);
 }
 
+void GrContextPriv::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
+    fContext->fDrawingManager->addPreFlushCallbackObject(std::move(preFlushCBObject));
+}
+
+
 static inline GrPixelConfig GrPixelConfigFallback(GrPixelConfig config) {
     switch (config) {
         case kAlpha_8_GrPixelConfig:
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index c70381f..3357636 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -13,6 +13,7 @@
 
 class GrSemaphore;
 class GrSurfaceProxy;
+class GrPreFlushCallbackObject;
 
 /** Class that adds methods to GrContext that are only intended for use internal to Skia.
     This class is purely a privileged window into GrContext. It should never have additional
@@ -58,9 +59,15 @@
 
     bool disableGpuYUVConversion() const { return fContext->fDisableGpuYUVConversion; }
 
+    /*
+     * A ref will be taken on the preFlushCallbackObject which will be removed when the
+     * context is destroyed.
+     */
+    void addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject>);
+
 private:
     explicit GrContextPriv(GrContext* context) : fContext(context) {}
-    GrContextPriv(const GrContextPriv&) {} // unimpl
+    GrContextPriv(const GrContextPriv&); // unimpl
     GrContextPriv& operator=(const GrContextPriv&); // unimpl
 
     // No taking addresses of this type.
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index d670e16..449cae6 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -75,10 +75,53 @@
     }
     fFlushing = true;
     bool flushed = false;
+
+    for (int i = 0; i < fOpLists.count(); ++i) {
+        // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
+        // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
+        // but need to be flushed anyway. Closing such GrOpLists here will mean new
+        // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
+        fOpLists[i]->makeClosed();
+    }
+
     SkDEBUGCODE(bool result =)
                         SkTTopoSort<GrOpList, GrOpList::TopoSortTraits>(&fOpLists);
     SkASSERT(result);
 
+    GrPreFlushResourceProvider preFlushProvider(this);
+
+    if (fPreFlushCBObjects.count()) {
+        // MDB TODO: pre-MDB '1' is the correct pre-allocated size. Post-MDB it will need
+        // to be larger.
+        SkAutoSTArray<1, uint32_t> opListIds(fOpLists.count());
+        for (int i = 0; i < fOpLists.count(); ++i) {
+            opListIds[i] = fOpLists[i]->uniqueID();
+        }
+
+        SkSTArray<1, sk_sp<GrRenderTargetContext>> renderTargetContexts;
+        for (int i = 0; i < fPreFlushCBObjects.count(); ++i) {
+            fPreFlushCBObjects[i]->preFlush(&preFlushProvider,
+                                            opListIds.get(), opListIds.count(),
+                                            &renderTargetContexts);
+            if (!renderTargetContexts.count()) {
+                continue;       // This is fine. No atlases of this type are required for this flush
+            }
+
+            for (int j = 0; j < renderTargetContexts.count(); ++j) {
+                GrRenderTargetOpList* opList = renderTargetContexts[j]->getOpList();
+                if (!opList) {
+                    continue;   // Odd - but not a big deal
+                }
+                SkDEBUGCODE(opList->validateTargetsSingleRenderTarget());
+                opList->prepareOps(&fFlushState);
+                if (!opList->executeOps(&fFlushState)) {
+                    continue;         // This is bad
+                }
+            }
+            renderTargetContexts.reset();
+        }
+    }
+
     for (int i = 0; i < fOpLists.count(); ++i) {
         fOpLists[i]->prepareOps(&fFlushState);
     }
@@ -145,6 +188,10 @@
     }
 }
 
+void GrDrawingManager::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
+    fPreFlushCBObjects.push_back(preFlushCBObject);
+}
+
 GrRenderTargetOpList* GrDrawingManager::newOpList(GrRenderTargetProxy* rtp) {
     SkASSERT(fContext);
 
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index 061e878..d914b03 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -11,9 +11,10 @@
 #include "GrOpFlushState.h"
 #include "GrPathRenderer.h"
 #include "GrPathRendererChain.h"
+#include "GrPreFlushResourceProvider.h"
 #include "GrRenderTargetOpList.h"
 #include "GrResourceCache.h"
-#include "SkTDArray.h"
+#include "SkTArray.h"
 #include "text/GrAtlasTextContext.h"
 
 class GrContext;
@@ -67,6 +68,8 @@
 
     void prepareSurfaceForExternalIO(GrSurface*);
 
+    void addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject);
+
 private:
     GrDrawingManager(GrContext* context,
                      const GrRenderTargetOpList::Options& optionsForOpLists,
@@ -92,6 +95,7 @@
     void internalFlush(GrResourceCache::FlushType);
 
     friend class GrContext;  // for access to: ctor, abandon, reset & flush
+    friend class GrPreFlushResourceProvider; // this is just a shallow wrapper around this class
 
     static const int kNumPixelGeometries = 5; // The different pixel geometries
     static const int kNumDFTOptions = 2;      // DFT or no DFT
@@ -115,6 +119,8 @@
     bool                              fFlushing;
 
     bool                              fIsImmediateMode;
+
+    SkTArray<sk_sp<GrPreFlushCallbackObject>> fPreFlushCBObjects;
 };
 
 #endif
diff --git a/src/gpu/GrPreFlushResourceProvider.cpp b/src/gpu/GrPreFlushResourceProvider.cpp
new file mode 100644
index 0000000..e907f39
--- /dev/null
+++ b/src/gpu/GrPreFlushResourceProvider.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "GrPreFlushResourceProvider.h"
+
+#include "GrDrawingManager.h"
+#include "GrSurfaceProxy.h"
+
+sk_sp<GrRenderTargetContext> GrPreFlushResourceProvider::makeRenderTargetContext(
+                                                        const GrSurfaceDesc& desc,
+                                                        sk_sp<SkColorSpace> colorSpace,
+                                                        const SkSurfaceProps* props) {
+    GrSurfaceDesc tmpDesc = desc;
+    tmpDesc.fFlags |= kRenderTarget_GrSurfaceFlag;
+
+    // Because this is being allocated at the start of a flush we must ensure the proxy
+    // will, when instantiated, have no pending IO.
+    // TODO: fold the kNoPendingIO_Flag into GrSurfaceFlags?
+    sk_sp<GrSurfaceProxy> proxy = GrSurfaceProxy::MakeDeferred(
+                                                    fDrawingMgr->getContext()->resourceProvider(),
+                                                    tmpDesc,
+                                                    SkBackingFit::kExact,
+                                                    SkBudgeted::kYes,
+                                                    GrResourceProvider::kNoPendingIO_Flag);
+    if (!proxy->asRenderTargetProxy()) {
+        return nullptr;
+    }
+
+    sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(
+                                                    proxy->asRenderTargetProxy(),
+                                                    fDrawingMgr->fContext->getGpu(),
+                                                    fDrawingMgr->fContext->resourceProvider(),
+                                                    fDrawingMgr->fContext->getAuditTrail(),
+                                                    fDrawingMgr->fOptionsForOpLists));
+    proxy->setLastOpList(opList.get());
+
+    return fDrawingMgr->makeRenderTargetContext(std::move(proxy),
+                                                std::move(colorSpace),
+                                                props);
+}
+
+// TODO: we only need this entry point as long as we have to pre-allocate the atlas.
+// Remove it ASAP.
+sk_sp<GrRenderTargetContext> GrPreFlushResourceProvider::makeRenderTargetContext(
+                                                        sk_sp<GrSurfaceProxy> proxy,
+                                                        sk_sp<SkColorSpace> colorSpace,
+                                                        const SkSurfaceProps* props) {
+
+    sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(
+                                                    proxy->asRenderTargetProxy(),
+                                                    fDrawingMgr->fContext->getGpu(),
+                                                    fDrawingMgr->fContext->resourceProvider(),
+                                                    fDrawingMgr->fContext->getAuditTrail(),
+                                                    fDrawingMgr->fOptionsForOpLists));
+    proxy->setLastOpList(opList.get());
+
+    return fDrawingMgr->makeRenderTargetContext(std::move(proxy),
+                                                std::move(colorSpace),
+                                                props);
+}
+
diff --git a/src/gpu/GrPreFlushResourceProvider.h b/src/gpu/GrPreFlushResourceProvider.h
new file mode 100644
index 0000000..86a299a
--- /dev/null
+++ b/src/gpu/GrPreFlushResourceProvider.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPreFlushResourceProvider_DEFINED
+#define GrPreFlushResourceProvider_DEFINED
+
+#include "GrTypes.h"
+#include "GrNonAtomicRef.h"
+
+// These two are just for GrPreFlushCallbackObject
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+class GrDrawingManager;
+class GrOpList;
+class GrPreFlushResourceProvider;
+class GrRenderTargetOpList;
+class GrRenderTargetContext;
+class GrSurfaceProxy;
+
+class SkColorSpace;
+class SkSurfaceProps;
+
+/*
+ * This is the base class from which all per-flush callback objects must be derived. It
+ * provides the "preFlush" interface.
+ */
+class GrPreFlushCallbackObject : public GrNonAtomicRef<GrPreFlushCallbackObject> {
+public:
+    virtual ~GrPreFlushCallbackObject() { }
+
+    /*
+     * The preFlush callback allows subsystems (e.g., text, path renderers) to create atlases
+     * for a specific flush. All the GrOpList IDs required for the flush are passed into the
+     * callback. The callback should return the render target contexts used to render the atlases
+     * in 'results'.
+     */
+    virtual void preFlush(GrPreFlushResourceProvider*,
+                          const uint32_t* opListIDs, int numOpListIDs,
+                          SkTArray<sk_sp<GrRenderTargetContext>>* results) = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+/*
+ * This class is a shallow wrapper around the drawing manager. It is passed into the
+ * preFlush callbacks and is intended to limit the functionality available to them.
+ * It should never have additional data members or virtual methods.
+ */
+class GrPreFlushResourceProvider {
+public:
+    sk_sp<GrRenderTargetContext> makeRenderTargetContext(const GrSurfaceDesc& desc,
+                                                         sk_sp<SkColorSpace> colorSpace,
+                                                         const SkSurfaceProps* props);
+
+    // TODO: we only need this entry point as long as we have to pre-allocate the atlas.
+    // Remove it ASAP.
+    sk_sp<GrRenderTargetContext> makeRenderTargetContext(sk_sp<GrSurfaceProxy> proxy,
+                                                         sk_sp<SkColorSpace> colorSpace,
+                                                         const SkSurfaceProps* props);
+
+private:
+    explicit GrPreFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
+    GrPreFlushResourceProvider(const GrPreFlushResourceProvider&); // unimpl
+    GrPreFlushResourceProvider& operator=(const GrPreFlushResourceProvider&); // unimpl
+
+    GrDrawingManager* fDrawingMgr;
+
+    friend class GrDrawingManager; // to construct this type.
+};
+
+#endif
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 1c98353..8d47fff 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -70,14 +70,25 @@
         }
     }
 }
+
+void GrRenderTargetOpList::validateTargetsSingleRenderTarget() const {
+    GrRenderTarget* rt = nullptr;
+    for (int i = 0; i < fRecordedOps.count(); ++i) {
+        if (!fRecordedOps[i].fOp) {
+            continue;       // combined forward
+        }
+
+        if (!rt) {
+            rt = fRecordedOps[i].fRenderTarget.get();
+        } else {
+            SkASSERT(fRecordedOps[i].fRenderTarget.get() == rt);
+        }
+    }
+}
 #endif
 
 void GrRenderTargetOpList::prepareOps(GrOpFlushState* flushState) {
-    // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
-    // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
-    // but need to be flushed anyway. Closing such GrOpLists here will mean new
-    // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
-    this->makeClosed();
+    // MDB TODO: add SkASSERT(this->isClosed());
 
     // Loop over the ops that haven't yet been prepared.
     for (int i = 0; i < fRecordedOps.count(); ++i) {
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index f4458b5..ab744f3 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -102,6 +102,8 @@
 
     SkDEBUGCODE(void dump() const override;)
 
+    SkDEBUGCODE(void validateTargetsSingleRenderTarget() const;)
+
 private:
     friend class GrRenderTargetContextPriv; // for clearStencilClip and stencil clip state.
 
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index d70daa2..d396b2a 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -45,11 +45,7 @@
 #endif
 
 void GrTextureOpList::prepareOps(GrOpFlushState* flushState) {
-    // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
-    // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
-    // but need to be flushed anyway. Closing such GrOpLists here will mean new
-    // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
-    this->makeClosed();
+    // MDB TODO: add SkASSERT(this->isClosed());
 
     // Loop over the ops that haven't yet generated their geometry
     for (int i = 0; i < fRecordedOps.count(); ++i) {
diff --git a/src/gpu/ops/GrTestMeshDrawOp.h b/src/gpu/ops/GrTestMeshDrawOp.h
index d78d3e9..039f88d 100644
--- a/src/gpu/ops/GrTestMeshDrawOp.h
+++ b/src/gpu/ops/GrTestMeshDrawOp.h
@@ -19,7 +19,7 @@
  */
 class GrTestMeshDrawOp : public GrMeshDrawOp {
 public:
-    virtual const char* name() const override = 0;
+    const char* name() const override = 0;
 
 protected:
     GrTestMeshDrawOp(uint32_t classID, const SkRect& bounds, GrColor color)