ccpr: Cleanup on-flush path stats

Adds a wrapper struct to limit the number of free variables. Fixes a
bug where we preallocated draw instances for clip paths. Counts conic
weights and preallocates their buffer as well.

Bug: skia:
Change-Id: I72779c9017322a0dc64461c8cb927f975406b9af
Reviewed-on: https://skia-review.googlesource.com/126844
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCGeometry.h
index 571b3c0..5c5d1d2 100644
--- a/src/gpu/ccpr/GrCCGeometry.h
+++ b/src/gpu/ccpr/GrCCGeometry.h
@@ -49,9 +49,10 @@
         bool operator==(const PrimitiveTallies&);
     };
 
-    GrCCGeometry(int numSkPoints = 0, int numSkVerbs = 0)
+    GrCCGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
             : fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
-            , fVerbs(numSkVerbs * 3) {}
+            , fVerbs(numSkVerbs * 3)
+            , fConicWeights(numConicWeights * 3/2) {}
 
     const SkTArray<SkPoint, true>& points() const { SkASSERT(!fBuildingContour); return fPoints; }
     const SkTArray<Verb, true>& verbs() const { SkASSERT(!fBuildingContour); return fVerbs; }
@@ -124,8 +125,8 @@
     SkDEBUGCODE(bool fBuildingContour = false);
 
     SkSTArray<128, SkPoint, true> fPoints;
-    SkSTArray<32, float, true> fConicWeights;
     SkSTArray<128, Verb, true> fVerbs;
+    SkSTArray<32, float, true> fConicWeights;
 };
 
 inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp
index 1629a19..b29bec7 100644
--- a/src/gpu/ccpr/GrCCPathParser.cpp
+++ b/src/gpu/ccpr/GrCCPathParser.cpp
@@ -21,13 +21,13 @@
 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
 using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
 
-GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPoints,
-                               int numSkVerbs)
-        : fLocalDevPtsBuffer(maxPathPoints + 1)  // Overallocate by one point to accomodate for
-                                                 // overflow with Sk4f. (See parsePath.)
-        , fGeometry(numSkPoints, numSkVerbs)
-        , fPathsInfo(maxTotalPaths)
-        , fScissorSubBatches(maxTotalPaths)
+GrCCPathParser::GrCCPathParser(int numPaths, const PathStats& pathStats)
+          // Overallocate by one point to accomodate for overflow with Sk4f. (See parsePath.)
+        : fLocalDevPtsBuffer(pathStats.fMaxPointsPerPath + 1)
+        , fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
+                    pathStats.fNumTotalConicWeights)
+        , fPathsInfo(numPaths)
+        , fScissorSubBatches(numPaths)
         , fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
     // Batches decide what to draw by looking where the previous one ended. Define initial batches
     // that "end" at the beginning of the data. These will not be drawn, but will only be be read by
diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h
index 973c873..b48a0b8 100644
--- a/src/gpu/ccpr/GrCCPathParser.h
+++ b/src/gpu/ccpr/GrCCPathParser.h
@@ -10,9 +10,11 @@
 
 #include "GrMesh.h"
 #include "GrNonAtomicRef.h"
-#include "GrTessellator.h"
+#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkRect.h"
 #include "SkRefCnt.h"
+#include "GrTessellator.h"
 #include "ccpr/GrCCCoverageProcessor.h"
 #include "ccpr/GrCCGeometry.h"
 #include "ops/GrDrawOp.h"
@@ -32,7 +34,16 @@
     enum class ScissorMode : int { kNonScissored = 0, kScissored = 1 };
     static constexpr int kNumScissorModes = 2;
 
-    GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPoints, int numSkVerbs);
+    struct PathStats {
+        int fMaxPointsPerPath = 0;
+        int fNumTotalSkPoints = 0;
+        int fNumTotalSkVerbs = 0;
+        int fNumTotalConicWeights = 0;
+
+        void statPath(const SkPath&);
+    };
+
+    GrCCPathParser(int numPaths, const PathStats&);
 
     ~GrCCPathParser() {
         // Enforce the contract that the client always calls saveParsedPath or discardParsedPath.
@@ -151,4 +162,11 @@
     mutable SkSTArray<32, GrPipeline::DynamicState> fDynamicStatesScratchBuffer;
 };
 
+inline void GrCCPathParser::PathStats::statPath(const SkPath& path) {
+    fMaxPointsPerPath = SkTMax(fMaxPointsPerPath, path.countPoints());
+    fNumTotalSkPoints += path.countPoints();
+    fNumTotalSkVerbs += path.countVerbs();
+    fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
+}
+
 #endif
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index 2634896..6c2c00b 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -254,6 +254,7 @@
     using PathInstance = GrCCPathProcessor::Instance;
 
     SkASSERT(!fFlushing);
+    SkASSERT(fFlushingRTPathIters.empty());
     SkASSERT(!fPerFlushIndexBuffer);
     SkASSERT(!fPerFlushVertexBuffer);
     SkASSERT(!fPerFlushInstanceBuffer);
@@ -267,40 +268,38 @@
 
     fPerFlushResourcesAreValid = false;
 
-    // Count the paths that are being flushed.
-    int maxTotalPaths = 0, maxPathPoints = 0, numSkPoints = 0, numSkVerbs = 0;
-    SkDEBUGCODE(int numClipPaths = 0);
+    // Count up the paths about to be flushed so we can preallocate buffers.
+    int numPathDraws = 0;
+    int numClipPaths = 0;
+    GrCCPathParser::PathStats flushingPathStats;
+    fFlushingRTPathIters.reserve(numOpListIDs);
     for (int i = 0; i < numOpListIDs; ++i) {
-        auto it = fRTPendingPathsMap.find(opListIDs[i]);
-        if (fRTPendingPathsMap.end() == it) {
+        auto iter = fRTPendingPathsMap.find(opListIDs[i]);
+        if (fRTPendingPathsMap.end() == iter) {
             continue;
         }
-        const RTPendingPaths& rtPendingPaths = it->second;
+        const RTPendingPaths& rtPendingPaths = iter->second;
 
         SkTInternalLList<DrawPathsOp>::Iter drawOpsIter;
         drawOpsIter.init(rtPendingPaths.fDrawOps,
                          SkTInternalLList<DrawPathsOp>::Iter::kHead_IterStart);
         while (DrawPathsOp* op = drawOpsIter.get()) {
             for (const DrawPathsOp::SingleDraw* draw = op->head(); draw; draw = draw->fNext) {
-                ++maxTotalPaths;
-                maxPathPoints = SkTMax(draw->fPath.countPoints(), maxPathPoints);
-                numSkPoints += draw->fPath.countPoints();
-                numSkVerbs += draw->fPath.countVerbs();
+                ++numPathDraws;
+                flushingPathStats.statPath(draw->fPath);
             }
             drawOpsIter.next();
         }
 
-        maxTotalPaths += rtPendingPaths.fClipPaths.size();
-        SkDEBUGCODE(numClipPaths += rtPendingPaths.fClipPaths.size());
+        numClipPaths += rtPendingPaths.fClipPaths.size();
         for (const auto& clipsIter : rtPendingPaths.fClipPaths) {
-            const SkPath& path = clipsIter.second.deviceSpacePath();
-            maxPathPoints = SkTMax(path.countPoints(), maxPathPoints);
-            numSkPoints += path.countPoints();
-            numSkVerbs += path.countVerbs();
+            flushingPathStats.statPath(clipsIter.second.deviceSpacePath());
         }
+
+        fFlushingRTPathIters.push_back(std::move(iter));
     }
 
-    if (!maxTotalPaths) {
+    if (0 == numPathDraws + numClipPaths) {
         return;  // Nothing to draw.
     }
 
@@ -318,7 +317,7 @@
     }
 
     fPerFlushInstanceBuffer =
-            onFlushRP->makeBuffer(kVertex_GrBufferType, maxTotalPaths * sizeof(PathInstance));
+            onFlushRP->makeBuffer(kVertex_GrBufferType, numPathDraws * sizeof(PathInstance));
     if (!fPerFlushInstanceBuffer) {
         SkDebugf("WARNING: failed to allocate path instance buffer. No paths will be drawn.\n");
         return;
@@ -328,35 +327,31 @@
     SkASSERT(pathInstanceData);
     int pathInstanceIdx = 0;
 
-    fPerFlushPathParser = sk_make_sp<GrCCPathParser>(maxTotalPaths, maxPathPoints, numSkPoints,
-                                                     numSkVerbs);
-    SkDEBUGCODE(int skippedTotalPaths = 0);
+    fPerFlushPathParser = sk_make_sp<GrCCPathParser>(numPathDraws + numClipPaths,
+                                                     flushingPathStats);
+    SkDEBUGCODE(int numSkippedPaths = 0);
 
     // Allocate atlas(es) and fill out GPU instance buffers.
-    for (int i = 0; i < numOpListIDs; ++i) {
-        auto it = fRTPendingPathsMap.find(opListIDs[i]);
-        if (fRTPendingPathsMap.end() == it) {
-            continue;
-        }
-        RTPendingPaths& rtPendingPaths = it->second;
+    for (const auto& iter : fFlushingRTPathIters) {
+        RTPendingPaths* rtPendingPaths = &iter->second;
 
         SkTInternalLList<DrawPathsOp>::Iter drawOpsIter;
-        drawOpsIter.init(rtPendingPaths.fDrawOps,
+        drawOpsIter.init(rtPendingPaths->fDrawOps,
                          SkTInternalLList<DrawPathsOp>::Iter::kHead_IterStart);
         while (DrawPathsOp* op = drawOpsIter.get()) {
             pathInstanceIdx = op->setupResources(onFlushRP, pathInstanceData, pathInstanceIdx);
             drawOpsIter.next();
-            SkDEBUGCODE(skippedTotalPaths += op->numSkippedInstances_debugOnly());
+            SkDEBUGCODE(numSkippedPaths += op->numSkippedInstances_debugOnly());
         }
 
-        for (auto& clipsIter : rtPendingPaths.fClipPaths) {
+        for (auto& clipsIter : rtPendingPaths->fClipPaths) {
             clipsIter.second.placePathInAtlas(this, onFlushRP, fPerFlushPathParser.get());
         }
     }
 
     fPerFlushInstanceBuffer->unmap();
 
-    SkASSERT(pathInstanceIdx == maxTotalPaths - skippedTotalPaths - numClipPaths);
+    SkASSERT(pathInstanceIdx == numPathDraws - numSkippedPaths);
 
     if (!fPerFlushAtlases.empty()) {
         auto coverageCountBatchID = fPerFlushPathParser->closeCurrentBatch();
@@ -528,8 +523,9 @@
     fPerFlushVertexBuffer.reset();
     fPerFlushIndexBuffer.reset();
     // We wait to erase these until after flush, once Ops and FPs are done accessing their data.
-    for (int i = 0; i < numOpListIDs; ++i) {
-        fRTPendingPathsMap.erase(opListIDs[i]);
+    for (const auto& iter : fFlushingRTPathIters) {
+        fRTPendingPathsMap.erase(iter);
     }
+    fFlushingRTPathIters.reset();
     SkDEBUGCODE(fFlushing = false);
 }
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
index da3c2e7..0e9ed7b 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
@@ -220,6 +220,7 @@
 
     // A map from render target ID to the individual render target's pending paths.
     std::map<uint32_t, RTPendingPaths> fRTPendingPathsMap;
+    SkSTArray<4, std::map<uint32_t, RTPendingPaths>::iterator> fFlushingRTPathIters;
     SkDEBUGCODE(int fPendingDrawOpsCount = 0);
 
     sk_sp<const GrBuffer> fPerFlushIndexBuffer;