Convex batch

BUG=skia:

Review URL: https://codereview.chromium.org/880643002
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index b301076..f4a6266 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -8,6 +8,9 @@
 
 #include "GrAAConvexPathRenderer.h"
 
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBufferAllocPool.h"
 #include "GrContext.h"
 #include "GrDrawTargetCaps.h"
 #include "GrGeometryProcessor.h"
@@ -222,46 +225,34 @@
 }
 
 static inline void add_line_to_segment(const SkPoint& pt,
-                                       SegmentArray* segments,
-                                       SkRect* devBounds) {
+                                       SegmentArray* segments) {
     segments->push_back();
     segments->back().fType = Segment::kLine;
     segments->back().fPts[0] = pt;
-    devBounds->growToInclude(pt.fX, pt.fY);
 }
 
-#ifdef SK_DEBUG
-static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) {
-    return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.fY <= rect.fBottom;
-}
-#endif
-
 static inline void add_quad_segment(const SkPoint pts[3],
-                                    SegmentArray* segments,
-                                    SkRect* devBounds) {
+                                    SegmentArray* segments) {
     if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
         if (pts[0] != pts[2]) {
-            add_line_to_segment(pts[2], segments, devBounds);
+            add_line_to_segment(pts[2], segments);
         }
     } else {
         segments->push_back();
         segments->back().fType = Segment::kQuad;
         segments->back().fPts[0] = pts[1];
         segments->back().fPts[1] = pts[2];
-        SkASSERT(contains_inclusive(*devBounds, pts[0]));
-        devBounds->growToInclude(pts + 1, 2);
     }
 }
 
 static inline void add_cubic_segments(const SkPoint pts[4],
                                       SkPath::Direction dir,
-                                      SegmentArray* segments,
-                                      SkRect* devBounds) {
+                                      SegmentArray* segments) {
     SkSTArray<15, SkPoint, true> quads;
     GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
     int count = quads.count();
     for (int q = 0; q < count; q += 3) {
-        add_quad_segment(&quads[q], segments, devBounds);
+        add_quad_segment(&quads[q], segments);
     }
 }
 
@@ -270,8 +261,7 @@
                          SegmentArray* segments,
                          SkPoint* fanPt,
                          int* vCount,
-                         int* iCount,
-                         SkRect* devBounds) {
+                         int* iCount) {
     SkPath::Iter iter(path, true);
     // This renderer over-emphasizes very thin path regions. We use the distance
     // to the path from the sample to compute coverage. Every pixel intersected
@@ -294,19 +284,18 @@
             case SkPath::kMove_Verb:
                 m.mapPoints(pts, 1);
                 update_degenerate_test(&degenerateData, pts[0]);
-                devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY);
                 break;
             case SkPath::kLine_Verb: {
                 m.mapPoints(&pts[1], 1);
                 update_degenerate_test(&degenerateData, pts[1]);
-                add_line_to_segment(pts[1], segments, devBounds);
+                add_line_to_segment(pts[1], segments);
                 break;
             }
             case SkPath::kQuad_Verb:
                 m.mapPoints(pts, 3);
                 update_degenerate_test(&degenerateData, pts[1]);
                 update_degenerate_test(&degenerateData, pts[2]);
-                add_quad_segment(pts, segments, devBounds);
+                add_quad_segment(pts, segments);
                 break;
             case SkPath::kConic_Verb: {
                 m.mapPoints(pts, 3);
@@ -316,7 +305,7 @@
                 for (int i = 0; i < converter.countQuads(); ++i) {
                     update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
                     update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
-                    add_quad_segment(quadPts + 2*i, segments, devBounds);
+                    add_quad_segment(quadPts + 2*i, segments);
                 }
                 break;
             }
@@ -325,7 +314,7 @@
                 update_degenerate_test(&degenerateData, pts[1]);
                 update_degenerate_test(&degenerateData, pts[2]);
                 update_degenerate_test(&degenerateData, pts[3]);
-                add_cubic_segments(pts, dir, segments, devBounds);
+                add_cubic_segments(pts, dir, segments);
                 break;
             };
             case SkPath::kDone_Verb:
@@ -703,95 +692,214 @@
             stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
 }
 
+class AAConvexPathBatch : public GrBatch {
+public:
+    struct Geometry {
+        GrColor fColor;
+        SkMatrix fViewMatrix;
+        SkPath fPath;
+        SkDEBUGCODE(SkRect fDevBounds;)
+    };
+
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(AAConvexPathBatch, (geometry));
+    }
+
+    const char* name() const SK_OVERRIDE { return "AAConvexBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+        // When this is called on a batch, there is only one geometry bundle
+        out->setKnownFourComponents(fGeoData[0].fColor);
+    }
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+        out->setUnknownSingleComponent();
+    }
+
+    void initBatchOpt(const GrBatchOpt& batchOpt) {
+        fBatchOpt = batchOpt;
+    }
+
+    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+        // Handle any color overrides
+        if (init.fColorIgnored) {
+            fGeoData[0].fColor = GrColor_ILLEGAL;
+        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
+            fGeoData[0].fColor = init.fOverrideColor;
+        }
+
+        // setup batch properties
+        fBatch.fColorIgnored = init.fColorIgnored;
+        fBatch.fColor = fGeoData[0].fColor;
+        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+        fBatch.fCoverageIgnored = init.fCoverageIgnored;
+    }
+
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+        int instanceCount = fGeoData.count();
+
+        SkMatrix invert;
+        if (!this->viewMatrix().invert(&invert)) {
+            SkDebugf("Could not invert viewmatrix\n");
+            return;
+        }
+
+        // Setup GrGeometryProcessor
+        SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(),
+                                                                               invert));
+
+        batchTarget->initDraw(quadProcessor, pipeline);
+
+        // TODO remove this when batch is everywhere
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+        // TODO generate all segments for all paths and use one vertex buffer
+        for (int i = 0; i < instanceCount; i++) {
+            Geometry& args = fGeoData[i];
+
+            // We use the fact that SkPath::transform path does subdivision based on
+            // perspective. Otherwise, we apply the view matrix when copying to the
+            // segment representation.
+            const SkMatrix* viewMatrix = &args.fViewMatrix;
+            if (viewMatrix->hasPerspective()) {
+                args.fPath.transform(*viewMatrix);
+                viewMatrix = &SkMatrix::I();
+            }
+
+            int vertexCount;
+            int indexCount;
+            enum {
+                kPreallocSegmentCnt = 512 / sizeof(Segment),
+                kPreallocDrawCnt = 4,
+            };
+            SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
+            SkPoint fanPt;
+
+            if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
+                              &indexCount)) {
+                continue;
+            }
+
+            const GrVertexBuffer* vertexBuffer;
+            int firstVertex;
+
+            size_t vertexStride = quadProcessor->getVertexStride();
+            void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+                                                                  vertexCount,
+                                                                  &vertexBuffer,
+                                                                  &firstVertex);
+
+            const GrIndexBuffer* indexBuffer;
+            int firstIndex;
+
+            void *indices = batchTarget->indexPool()->makeSpace(indexCount,
+                                                                &indexBuffer,
+                                                                &firstIndex);
+
+            QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices);
+            uint16_t* idxs = reinterpret_cast<uint16_t*>(indices);
+
+            SkSTArray<kPreallocDrawCnt, Draw, true> draws;
+            create_vertices(segments, fanPt, &draws, verts, idxs);
+
+#ifdef SK_DEBUG
+            // Check devBounds
+            SkRect actualBounds;
+            actualBounds.set(verts[0].fPos, verts[1].fPos);
+            for (int i = 2; i < vertexCount; ++i) {
+                actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
+            }
+            SkASSERT(args.fDevBounds.contains(actualBounds));
+#endif
+
+            GrDrawTarget::DrawInfo info;
+            info.setVertexBuffer(vertexBuffer);
+            info.setIndexBuffer(indexBuffer);
+            info.setPrimitiveType(kTriangles_GrPrimitiveType);
+            info.setStartIndex(firstIndex);
+
+            int vOffset = 0;
+            for (int i = 0; i < draws.count(); ++i) {
+                const Draw& draw = draws[i];
+                info.setStartVertex(vOffset + firstVertex);
+                info.setVertexCount(draw.fVertexCnt);
+                info.setIndexCount(draw.fIndexCnt);
+                batchTarget->draw(info);
+                vOffset += draw.fVertexCnt;
+            }
+        }
+    }
+
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    AAConvexPathBatch(const Geometry& geometry) {
+        this->initClassID<AAConvexPathBatch>();
+        fGeoData.push_back(geometry);
+    }
+
+    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+        AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
+
+        if (this->color() != that->color()) {
+            return false;
+        }
+
+        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
+        if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+            return false;
+        }
+
+        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        return true;
+    }
+
+    GrColor color() const { return fBatch.fColor; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+
+    struct BatchTracker {
+        GrColor fColor;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+    };
+
+    GrBatchOpt fBatchOpt;
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+};
+
 bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
                                         GrPipelineBuilder* pipelineBuilder,
                                         GrColor color,
                                         const SkMatrix& vm,
-                                        const SkPath& origPath,
+                                        const SkPath& path,
                                         const SkStrokeRec&,
                                         bool antiAlias) {
-
-    const SkPath* path = &origPath;
-    if (path->isEmpty()) {
+    if (path.isEmpty()) {
         return true;
     }
 
-    SkMatrix viewMatrix = vm;
-    SkMatrix invert;
-    if (!viewMatrix.invert(&invert)) {
-        return false;
-    }
+    // We outset our vertices one pixel and add one more pixel for precision.
+    // TODO create tighter bounds when we start reordering.
+    SkRect devRect = path.getBounds();
+    vm.mapRect(&devRect);
+    devRect.outset(2, 2);
 
-    // We use the fact that SkPath::transform path does subdivision based on
-    // perspective. Otherwise, we apply the view matrix when copying to the
-    // segment representation.
-    SkPath tmpPath;
-    if (viewMatrix.hasPerspective()) {
-        origPath.transform(viewMatrix, &tmpPath);
-        path = &tmpPath;
-        viewMatrix = SkMatrix::I();
-    }
+    AAConvexPathBatch::Geometry geometry;
+    geometry.fColor = color;
+    geometry.fViewMatrix = vm;
+    geometry.fPath = path;
+    SkDEBUGCODE(geometry.fDevBounds = devRect;)
 
-    QuadVertex *verts;
-    uint16_t* idxs;
-
-    int vCount;
-    int iCount;
-    enum {
-        kPreallocSegmentCnt = 512 / sizeof(Segment),
-        kPreallocDrawCnt = 4,
-    };
-    SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
-    SkPoint fanPt;
-
-    // We can't simply use the path bounds because we may degenerate cubics to quads which produces
-    // new control points outside the original convex hull.
-    SkRect devBounds;
-    if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &devBounds)) {
-        return false;
-    }
-
-    // Our computed verts should all be within one pixel of the segment control points.
-    devBounds.outset(SK_Scalar1, SK_Scalar1);
-
-    SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(color, invert));
-
-    GrDrawTarget::AutoReleaseGeometry arg(target, vCount, quadProcessor->getVertexStride(), iCount);
-    SkASSERT(quadProcessor->getVertexStride() == sizeof(QuadVertex));
-    if (!arg.succeeded()) {
-        return false;
-    }
-    verts = reinterpret_cast<QuadVertex*>(arg.vertices());
-    idxs = reinterpret_cast<uint16_t*>(arg.indices());
-
-    SkSTArray<kPreallocDrawCnt, Draw, true> draws;
-    create_vertices(segments, fanPt, &draws, verts, idxs);
-
-    // Check devBounds
-#ifdef SK_DEBUG
-    SkRect tolDevBounds = devBounds;
-    tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
-    SkRect actualBounds;
-    actualBounds.set(verts[0].fPos, verts[1].fPos);
-    for (int i = 2; i < vCount; ++i) {
-        actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
-    }
-    SkASSERT(tolDevBounds.contains(actualBounds));
-#endif
-
-    int vOffset = 0;
-    for (int i = 0; i < draws.count(); ++i) {
-        const Draw& draw = draws[i];
-        target->drawIndexed(pipelineBuilder,
-                            quadProcessor,
-                            kTriangles_GrPrimitiveType,
-                            vOffset,  // start vertex
-                            0,        // start index
-                            draw.fVertexCnt,
-                            draw.fIndexCnt,
-                            &devBounds);
-        vOffset += draw.fVertexCnt;
-    }
+    SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
+    target->drawBatch(pipelineBuilder, batch, &devRect);
 
     return true;
+
 }