CCPR: Rewrite path parsing

Creates a GrCCPRGeometry class that chops contours up into simple
segments that ccpr can render, and rewrites the GPU buffer creation to
be able to handle arbitrary lengths of ccpr geometry.

Bug: skia:
Change-Id: Iaa173a02729e177b0ed7ef7fbb9195d349be689d
Reviewed-on: https://skia-review.googlesource.com/41963
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index 596ec5e..2bec4ff 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -72,6 +72,7 @@
         , fOwningRTPendingOps(nullptr) {
     SkDEBUGCODE(fBaseInstance = -1);
     SkDEBUGCODE(fDebugInstanceCount = 1;)
+    SkDEBUGCODE(fDebugSkippedInstances = 0;)
 
     GrRenderTargetContext* const rtc = args.fRenderTargetContext;
 
@@ -115,9 +116,12 @@
         SkASSERT(owningRTPendingOps == fOwningRTPendingOps);
         owningRTPendingOps->fOpList.remove(that);
     } else {
-        // wasRecorded is not called when the op gets combined first. Count path items here instead.
-        SingleDraw& onlyDraw = that->getOnlyPathDraw();
-        fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
+        // The Op is being combined immediately after creation, before a call to wasRecorded. In
+        // this case wasRecorded will not be called. So we count its path here instead.
+        const SingleDraw& onlyDraw = that->getOnlyPathDraw();
+        ++fOwningRTPendingOps->fNumTotalPaths;
+        fOwningRTPendingOps->fNumSkPoints += onlyDraw.fPath.countPoints();
+        fOwningRTPendingOps->fNumSkVerbs += onlyDraw.fPath.countVerbs();
     }
 
     fTailDraw->fNext = &fOwningRTPendingOps->fDrawsAllocator.push_back(that->fHeadDraw);
@@ -132,21 +136,17 @@
 
 void DrawPathsOp::wasRecorded(GrRenderTargetOpList* opList) {
     SkASSERT(!fOwningRTPendingOps);
-    SingleDraw& onlyDraw = this->getOnlyPathDraw();
+    const SingleDraw& onlyDraw = this->getOnlyPathDraw();
     fOwningRTPendingOps = &fCCPR->fRTPendingOpsMap[opList->uniqueID()];
+    ++fOwningRTPendingOps->fNumTotalPaths;
+    fOwningRTPendingOps->fNumSkPoints += onlyDraw.fPath.countPoints();
+    fOwningRTPendingOps->fNumSkVerbs += onlyDraw.fPath.countVerbs();
     fOwningRTPendingOps->fOpList.addToTail(this);
-    fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
 }
 
 void GrCoverageCountingPathRenderer::preFlush(GrOnFlushResourceProvider* onFlushRP,
                                               const uint32_t* opListIDs, int numOpListIDs,
                                               SkTArray<sk_sp<GrRenderTargetContext>>* results) {
-    using PathInstance = GrCCPRPathProcessor::Instance;
-
-    SkASSERT(!fPerFlushIndexBuffer);
-    SkASSERT(!fPerFlushVertexBuffer);
-    SkASSERT(!fPerFlushInstanceBuffer);
-    SkASSERT(fPerFlushAtlases.empty());
     SkASSERT(!fFlushing);
     SkDEBUGCODE(fFlushing = true;)
 
@@ -154,8 +154,29 @@
         return; // Nothing to draw.
     }
 
+    this->setupPerFlushResources(onFlushRP, opListIDs, numOpListIDs, results);
+
+    // Erase these last, once we are done accessing data from the SingleDraw allocators.
+    for (int i = 0; i < numOpListIDs; ++i) {
+        fRTPendingOpsMap.erase(opListIDs[i]);
+    }
+}
+
+void GrCoverageCountingPathRenderer::setupPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
+                                                  const uint32_t* opListIDs,
+                                                  int numOpListIDs,
+                                                  SkTArray<sk_sp<GrRenderTargetContext>>* results) {
+    using PathInstance = GrCCPRPathProcessor::Instance;
+
+    SkASSERT(!fPerFlushIndexBuffer);
+    SkASSERT(!fPerFlushVertexBuffer);
+    SkASSERT(!fPerFlushInstanceBuffer);
+    SkASSERT(fPerFlushAtlases.empty());
+
+    fPerFlushResourcesAreValid = false;
+
     SkTInternalLList<DrawPathsOp> flushingOps;
-    GrCCPRCoverageOpsBuilder::MaxBufferItems maxBufferItems;
+    int maxTotalPaths = 0, numSkPoints = 0, numSkVerbs = 0;
 
     for (int i = 0; i < numOpListIDs; ++i) {
         auto it = fRTPendingOpsMap.find(opListIDs[i]);
@@ -163,13 +184,15 @@
             RTPendingOps& rtPendingOps = it->second;
             SkASSERT(!rtPendingOps.fOpList.isEmpty());
             flushingOps.concat(std::move(rtPendingOps.fOpList));
-            maxBufferItems += rtPendingOps.fMaxBufferItems;
+            maxTotalPaths += rtPendingOps.fNumTotalPaths;
+            numSkPoints += rtPendingOps.fNumSkPoints;
+            numSkVerbs += rtPendingOps.fNumSkVerbs;
         }
     }
 
-    SkASSERT(flushingOps.isEmpty() == !maxBufferItems.fMaxPaths);
+    SkASSERT(flushingOps.isEmpty() == !maxTotalPaths);
     if (flushingOps.isEmpty()) {
-        return; // Still nothing to draw.
+        return; // Nothing to draw.
     }
 
     fPerFlushIndexBuffer = GrCCPRPathProcessor::FindOrMakeIndexBuffer(onFlushRP);
@@ -184,14 +207,8 @@
         return;
     }
 
-    GrCCPRCoverageOpsBuilder atlasOpsBuilder;
-    if (!atlasOpsBuilder.init(onFlushRP, maxBufferItems)) {
-        SkDebugf("WARNING: failed to allocate buffers for coverage ops. No paths will be drawn.\n");
-        return;
-    }
-
     fPerFlushInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
-                                                   maxBufferItems.fMaxPaths * sizeof(PathInstance));
+                                                   maxTotalPaths * sizeof(PathInstance));
     if (!fPerFlushInstanceBuffer) {
         SkDebugf("WARNING: failed to allocate path instance buffer. No paths will be drawn.\n");
         return;
@@ -201,29 +218,29 @@
     SkASSERT(pathInstanceData);
     int pathInstanceIdx = 0;
 
+    GrCCPRCoverageOpsBuilder atlasOpsBuilder(maxTotalPaths, numSkPoints, numSkVerbs);
     GrCCPRAtlas* atlas = nullptr;
-    SkDEBUGCODE(int skippedPaths = 0;)
+    SkDEBUGCODE(int skippedTotalPaths = 0;)
 
     SkTInternalLList<DrawPathsOp>::Iter iter;
     iter.init(flushingOps, SkTInternalLList<DrawPathsOp>::Iter::kHead_IterStart);
-    while (DrawPathsOp* op = iter.get()) {
-        SkASSERT(op->fDebugInstanceCount > 0);
-        SkASSERT(-1 == op->fBaseInstance);
-        op->fBaseInstance = pathInstanceIdx;
+    while (DrawPathsOp* drawPathOp = iter.get()) {
+        SkASSERT(drawPathOp->fDebugInstanceCount > 0);
+        SkASSERT(-1 == drawPathOp->fBaseInstance);
+        drawPathOp->fBaseInstance = pathInstanceIdx;
 
-        for (const DrawPathsOp::SingleDraw* draw = &op->fHeadDraw; draw; draw = draw->fNext) {
+        for (const auto* draw = &drawPathOp->fHeadDraw; draw; draw = draw->fNext) {
             // parsePath gives us two tight bounding boxes: one in device space, as well as a second
             // one rotated an additional 45 degrees. The path vertex shader uses these two bounding
             // boxes to generate an octagon that circumscribes the path.
             SkRect devBounds, devBounds45;
-            atlasOpsBuilder.parsePath(draw->fScissorMode, draw->fMatrix, draw->fPath, &devBounds,
-                                      &devBounds45);
+            atlasOpsBuilder.parsePath(draw->fMatrix, draw->fPath, &devBounds, &devBounds45);
 
             SkRect clippedDevBounds = devBounds;
             if (ScissorMode::kScissored == draw->fScissorMode &&
                 !clippedDevBounds.intersect(devBounds, SkRect::Make(draw->fClipBounds))) {
-                SkDEBUGCODE(--op->fDebugInstanceCount);
-                SkDEBUGCODE(++skippedPaths;)
+                SkDEBUGCODE(++drawPathOp->fDebugSkippedInstances);
+                atlasOpsBuilder.discardParsedPath();
                 continue;
             }
 
@@ -234,12 +251,9 @@
             SkIPoint16 atlasLocation;
             if (atlas && !atlas->addRect(w, h, &atlasLocation)) {
                 // The atlas is out of room and can't grow any bigger.
-                auto atlasOp = atlasOpsBuilder.createIntermediateOp(atlas->drawBounds());
-                if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
-                    results->push_back(std::move(rtc));
-                }
-                if (pathInstanceIdx > op->fBaseInstance) {
-                    op->addAtlasBatch(atlas, pathInstanceIdx);
+                atlasOpsBuilder.emitOp(atlas->drawBounds());
+                if (pathInstanceIdx > drawPathOp->fBaseInstance) {
+                    drawPathOp->addAtlasBatch(atlas, pathInstanceIdx);
                 }
                 atlas = nullptr;
             }
@@ -262,34 +276,54 @@
                 draw->fColor
             };
 
-            atlasOpsBuilder.saveParsedPath(clippedDevIBounds, offsetX, offsetY);
+            atlasOpsBuilder.saveParsedPath(draw->fScissorMode, clippedDevIBounds, offsetX, offsetY);
         }
 
-        SkASSERT(pathInstanceIdx == op->fBaseInstance + op->fDebugInstanceCount);
-        op->addAtlasBatch(atlas, pathInstanceIdx);
+        SkASSERT(pathInstanceIdx == drawPathOp->fBaseInstance + drawPathOp->fDebugInstanceCount -
+                                    drawPathOp->fDebugSkippedInstances);
+        if (pathInstanceIdx > drawPathOp->fBaseInstance) {
+            drawPathOp->addAtlasBatch(atlas, pathInstanceIdx);
+        }
 
         iter.next();
+        SkDEBUGCODE(skippedTotalPaths += drawPathOp->fDebugSkippedInstances;)
+    }
+    SkASSERT(pathInstanceIdx == maxTotalPaths - skippedTotalPaths);
+
+    if (atlas) {
+        atlasOpsBuilder.emitOp(atlas->drawBounds());
     }
 
-    SkASSERT(pathInstanceIdx == maxBufferItems.fMaxPaths - skippedPaths);
     fPerFlushInstanceBuffer->unmap();
 
-    std::unique_ptr<GrDrawOp> atlasOp = atlasOpsBuilder.finalize(atlas->drawBounds());
-    if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
-        results->push_back(std::move(rtc));
+    // Draw the coverage ops into their respective atlases.
+    SkSTArray<4, std::unique_ptr<GrCCPRCoverageOp>> atlasOps(fPerFlushAtlases.count());
+    if (!atlasOpsBuilder.finalize(onFlushRP, &atlasOps)) {
+        SkDebugf("WARNING: failed to allocate ccpr atlas buffers. No paths will be drawn.\n");
+        return;
     }
+    SkASSERT(atlasOps.count() == fPerFlushAtlases.count());
 
-    // Erase these last, once we are done accessing data from the SingleDraw allocators.
-    for (int i = 0; i < numOpListIDs; ++i) {
-        fRTPendingOpsMap.erase(opListIDs[i]);
+    GrTAllocator<GrCCPRAtlas>::Iter atlasIter(&fPerFlushAtlases);
+    for (std::unique_ptr<GrCCPRCoverageOp>& atlasOp : atlasOps) {
+        SkAssertResult(atlasIter.next());
+        GrCCPRAtlas* atlas = atlasIter.get();
+        SkASSERT(atlasOp->bounds() == SkRect::MakeIWH(atlas->drawBounds().width(),
+                                                      atlas->drawBounds().height()));
+        if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
+            results->push_back(std::move(rtc));
+        }
     }
+    SkASSERT(!atlasIter.next());
+
+    fPerFlushResourcesAreValid = true;
 }
 
 void DrawPathsOp::onExecute(GrOpFlushState* flushState) {
     SkASSERT(fCCPR->fFlushing);
     SkASSERT(flushState->rtCommandBuffer());
 
-    if (!fCCPR->fPerFlushInstanceBuffer) {
+    if (!fCCPR->fPerFlushResourcesAreValid) {
         return; // Setup failed.
     }
 
@@ -323,7 +357,7 @@
         flushState->rtCommandBuffer()->draw(pipeline, coverProc, &mesh, nullptr, 1, this->bounds());
     }
 
-    SkASSERT(baseInstance == fBaseInstance + fDebugInstanceCount);
+    SkASSERT(baseInstance == fBaseInstance + fDebugInstanceCount - fDebugSkippedInstances);
 }
 
 void GrCoverageCountingPathRenderer::postFlush() {