Default path renderer batch
authorjoshualitt <joshualitt@chromium.org>
Mon, 23 Feb 2015 16:44:31 +0000 (08:44 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 23 Feb 2015 16:44:31 +0000 (08:44 -0800)
BUG=skia:

Review URL: https://codereview.chromium.org/884013010

src/gpu/GrAARectRenderer.cpp
src/gpu/GrDefaultPathRenderer.cpp

index fa755dc..2cc8f95 100644 (file)
@@ -588,8 +588,8 @@ public:
             return;
         }
 
-        SkAutoTUnref<const GrGeometryProcessor>gp(create_fill_rect_gp(canTweakAlphaForCoverage,
-                                                                      localMatrix));
+        SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
+                                                                       localMatrix));
 
         batchTarget->initDraw(gp, pipeline);
 
index 7b98174..792b76c 100644 (file)
@@ -7,6 +7,9 @@
 
 #include "GrDefaultPathRenderer.h"
 
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBufferAllocPool.h"
 #include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrPathUtils.h"
@@ -191,9 +194,9 @@ static inline void append_countour_edge_indices(bool hairLine,
 
 static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
                             SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
-                            bool isHairline, uint16_t subpathIdxStart, uint16_t** idx) {
+                            bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
     // first pt of quad is the pt we ended on in previous step
-    uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1;
+    uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
     uint16_t numPts =  (uint16_t)
         GrPathUtils::generateQuadraticPoints(
             pts[0], pts[1], pts[2],
@@ -207,139 +210,316 @@ static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint p
     }
 }
 
-bool GrDefaultPathRenderer::createGeom(GrDrawTarget* target,
-                                       GrPipelineBuilder* pipelineBuilder,
-                                       GrPrimitiveType* primType,
-                                       int* vertexCnt,
-                                       int* indexCnt,
-                                       GrDrawTarget::AutoReleaseGeometry* arg,
-                                       const SkPath& path,
-                                       const SkStrokeRec& stroke,
-                                       SkScalar srcSpaceTol) {
-    {
-    SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
-    int contourCnt;
-    int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt,
-                                                  srcSpaceTol);
-
-    if (maxPts <= 0) {
-        return false;
+class DefaultPathBatch : public GrBatch {
+public:
+    struct Geometry {
+        GrColor fColor;
+        SkPath fPath;
+        SkScalar fTolerance;
+        SkDEBUGCODE(SkRect fDevBounds;)
+    };
+
+    static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
+                           bool isHairline) {
+        return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline));
+    }
+
+    const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; }
+
+    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);
     }
-    if (maxPts > ((int)SK_MaxU16 + 1)) {
-        SkDebugf("Path not rendered, too many verts (%d)\n", maxPts);
-        return false;
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+        out->setUnknownSingleComponent();
     }
 
-    bool indexed = contourCnt > 1;
+    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;
+        }
 
-    const bool isHairline = stroke.isHairlineStyle();
+        // setup batch properties
+        fBatch.fColorIgnored = init.fColorIgnored;
+        fBatch.fColor = fGeoData[0].fColor;
+        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+        fBatch.fCoverageIgnored = init.fCoverageIgnored;
+    }
 
-    int maxIdxs = 0;
-    if (isHairline) {
-        if (indexed) {
-            maxIdxs = 2 * maxPts;
-            *primType = kLines_GrPrimitiveType;
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+        SkAutoTUnref<const GrGeometryProcessor> gp(
+                GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
+                                                this->color(),
+                                                this->viewMatrix(),
+                                                SkMatrix::I(),
+                                                false,
+                                                this->coverage()));
+
+        size_t vertexStride = gp->getVertexStride();
+        SkASSERT(vertexStride == sizeof(SkPoint));
+
+        batchTarget->initDraw(gp, pipeline);
+
+        // TODO this is hacky, but the only way we have to initialize the GP is to use the
+        // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
+        // everywhere we can remove this nastiness
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+        int instanceCount = fGeoData.count();
+
+        // compute number of vertices
+        int maxVertices = 0;
+
+        // We will use index buffers if we have multiple paths or one path with multiple contours
+        bool isIndexed = instanceCount > 1;
+        for (int i = 0; i < instanceCount; i++) {
+            Geometry& args = fGeoData[i];
+
+            int contourCount;
+            maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
+                                                            args.fTolerance);
+
+            isIndexed = isIndexed || contourCount > 1;
+        }
+
+        // determine primitiveType
+        int maxIndices = 0;
+        GrPrimitiveType primitiveType;
+        if (this->isHairline()) {
+            if (isIndexed) {
+                maxIndices = 2 * maxVertices;
+                primitiveType = kLines_GrPrimitiveType;
+            } else {
+                primitiveType = kLineStrip_GrPrimitiveType;
+            }
         } else {
-            *primType = kLineStrip_GrPrimitiveType;
+            if (isIndexed) {
+                maxIndices = 3 * maxVertices;
+                primitiveType = kTriangles_GrPrimitiveType;
+            } else {
+                primitiveType = kTriangleFan_GrPrimitiveType;
+            }
         }
-    } else {
-        if (indexed) {
-            maxIdxs = 3 * maxPts;
-            *primType = kTriangles_GrPrimitiveType;
+
+        // allocate vertex / index buffers
+        const GrVertexBuffer* vertexBuffer;
+        int firstVertex;
+
+        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
+                                                              maxVertices,
+                                                              &vertexBuffer,
+                                                              &firstVertex);
+
+        const GrIndexBuffer* indexBuffer;
+        int firstIndex;
+
+        void* indices = NULL;
+        if (isIndexed) {
+            indices = batchTarget->indexPool()->makeSpace(maxIndices,
+                                                          &indexBuffer,
+                                                          &firstIndex);
+        }
+
+        // fill buffers
+        int vertexOffset = 0;
+        int indexOffset = 0;
+        for (int i = 0; i < instanceCount; i++) {
+            Geometry& args = fGeoData[i];
+
+            int vertexCnt = 0;
+            int indexCnt = 0;
+            if (!this->createGeom(vertices,
+                                  vertexOffset,
+                                  indices,
+                                  indexOffset,
+                                  &vertexCnt,
+                                  &indexCnt,
+                                  args.fPath,
+                                  args.fTolerance,
+                                  isIndexed)) {
+                return;
+            }
+
+            vertexOffset += vertexCnt;
+            indexOffset += indexCnt;
+            SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
+        }
+
+        GrDrawTarget::DrawInfo drawInfo;
+        drawInfo.setPrimitiveType(primitiveType);
+        drawInfo.setVertexBuffer(vertexBuffer);
+        drawInfo.setStartVertex(firstVertex);
+        drawInfo.setVertexCount(vertexOffset);
+        if (isIndexed) {
+            drawInfo.setIndexBuffer(indexBuffer);
+            drawInfo.setStartIndex(firstIndex);
+            drawInfo.setIndexCount(indexOffset);
         } else {
-            *primType = kTriangleFan_GrPrimitiveType;
+            drawInfo.setStartIndex(0);
+            drawInfo.setIndexCount(0);
         }
+        batchTarget->draw(drawInfo);
     }
 
-    if (!arg->set(target, maxPts, GrDefaultGeoProcFactory::DefaultVertexStride(), maxIdxs)) {
-        return false;
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
+                     bool isHairline) {
+        this->initClassID<DefaultPathBatch>();
+        fBatch.fCoverage = coverage;
+        fBatch.fIsHairline = isHairline;
+        fBatch.fViewMatrix = viewMatrix;
+        fGeoData.push_back(geometry);
     }
-    SkASSERT(GrDefaultGeoProcFactory::DefaultVertexStride() == sizeof(SkPoint));
 
-    uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices());
-    uint16_t* idx = idxBase;
-    uint16_t subpathIdxStart = 0;
+    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+        DefaultPathBatch* that = t->cast<DefaultPathBatch>();
 
-    SkPoint* base = reinterpret_cast<SkPoint*>(arg->vertices());
-    SkASSERT(base);
-    SkPoint* vert = base;
+        if (this->color() != that->color()) {
+            return false;
+        }
 
-    SkPoint pts[4];
+        if (this->coverage() != that->coverage()) {
+            return false;
+        }
 
-    bool first = true;
-    int subpath = 0;
+        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+            return false;
+        }
 
-    SkPath::Iter iter(path, false);
+        if (this->isHairline() != that->isHairline()) {
+            return false;
+        }
 
-    for (;;) {
-        SkPath::Verb verb = iter.next(pts);
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                if (!first) {
-                    uint16_t currIdx = (uint16_t) (vert - base);
-                    subpathIdxStart = currIdx;
-                    ++subpath;
-                }
-                *vert = pts[0];
-                vert++;
-                break;
-            case SkPath::kLine_Verb:
-                if (indexed) {
-                    uint16_t prevIdx = (uint16_t)(vert - base) - 1;
-                    append_countour_edge_indices(isHairline, subpathIdxStart,
-                                                 prevIdx, &idx);
-                }
-                *(vert++) = pts[1];
-                break;
-            case SkPath::kConic_Verb: {
-                SkScalar weight = iter.conicWeight();
-                SkAutoConicToQuads converter;
-                // Converting in src-space, hance the finer tolerance (0.25)
-                // TODO: find a way to do this in dev-space so the tolerance means something
-                const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
-                for (int i = 0; i < converter.countQuads(); ++i) {
-                    add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, indexed,
-                             isHairline, subpathIdxStart, &idx);
-                }
-                break;
-            }
-            case SkPath::kQuad_Verb:
-                add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, indexed,
-                         isHairline, subpathIdxStart, &idx);
-                break;
-            case SkPath::kCubic_Verb: {
-                // first pt of cubic is the pt we ended on in previous step
-                uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
-                uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
-                                pts[0], pts[1], pts[2], pts[3],
-                                srcSpaceTolSqd, &vert,
-                                GrPathUtils::cubicPointCount(pts, srcSpaceTol));
-                if (indexed) {
-                    for (uint16_t i = 0; i < numPts; ++i) {
-                        append_countour_edge_indices(isHairline, subpathIdxStart,
-                                                     firstCPtIdx + i, &idx);
+        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        return true;
+    }
+
+    bool createGeom(void* vertices,
+                    size_t vertexOffset,
+                    void* indices,
+                    size_t indexOffset,
+                    int* vertexCnt,
+                    int* indexCnt,
+                    const SkPath& path,
+                    SkScalar srcSpaceTol,
+                    bool isIndexed)  {
+        {
+            SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
+
+            uint16_t indexOffsetU16 = (uint16_t)indexOffset;
+            uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
+
+            uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
+            uint16_t* idx = idxBase;
+            uint16_t subpathIdxStart = vertexOffsetU16;
+
+            SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
+            SkPoint* vert = base;
+
+            SkPoint pts[4];
+
+            bool first = true;
+            int subpath = 0;
+
+            SkPath::Iter iter(path, false);
+
+            bool done = false;
+            while (!done) {
+                SkPath::Verb verb = iter.next(pts);
+                switch (verb) {
+                    case SkPath::kMove_Verb:
+                        if (!first) {
+                            uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
+                            subpathIdxStart = currIdx;
+                            ++subpath;
+                        }
+                        *vert = pts[0];
+                        vert++;
+                        break;
+                    case SkPath::kLine_Verb:
+                        if (isIndexed) {
+                            uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
+                            append_countour_edge_indices(this->isHairline(), subpathIdxStart,
+                                                         prevIdx, &idx);
+                        }
+                        *(vert++) = pts[1];
+                        break;
+                    case SkPath::kConic_Verb: {
+                        SkScalar weight = iter.conicWeight();
+                        SkAutoConicToQuads converter;
+                        // Converting in src-space, hance the finer tolerance (0.25)
+                        // TODO: find a way to do this in dev-space so the tolerance means something
+                        const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f);
+                        for (int i = 0; i < converter.countQuads(); ++i) {
+                            add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
+                                     isIndexed, this->isHairline(), subpathIdxStart,
+                                     (int)vertexOffset, &idx);
+                        }
+                        break;
+                    }
+                    case SkPath::kQuad_Verb:
+                        add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
+                                 this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
+                        break;
+                    case SkPath::kCubic_Verb: {
+                        // first pt of cubic is the pt we ended on in previous step
+                        uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
+                        uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
+                                        pts[0], pts[1], pts[2], pts[3],
+                                        srcSpaceTolSqd, &vert,
+                                        GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+                        if (isIndexed) {
+                            for (uint16_t i = 0; i < numPts; ++i) {
+                                append_countour_edge_indices(this->isHairline(), subpathIdxStart,
+                                                             firstCPtIdx + i, &idx);
+                            }
+                        }
+                        break;
                     }
+                    case SkPath::kClose_Verb:
+                        break;
+                    case SkPath::kDone_Verb:
+                        done = true;
                 }
-                break;
+                first = false;
             }
-            case SkPath::kClose_Verb:
-                break;
-            case SkPath::kDone_Verb:
-             // uint16_t currIdx = (uint16_t) (vert - base);
-                goto FINISHED;
-        }
-        first = false;
-    }
-FINISHED:
-    SkASSERT((vert - base) <= maxPts);
-    SkASSERT((idx - idxBase) <= maxIdxs);
 
-    *vertexCnt = static_cast<int>(vert - base);
-    *indexCnt = static_cast<int>(idx - idxBase);
+            *vertexCnt = static_cast<int>(vert - base);
+            *indexCnt = static_cast<int>(idx - idxBase);
 
+        }
+        return true;
     }
-    return true;
-}
+
+    GrColor color() const { return fBatch.fColor; }
+    uint8_t coverage() const { return fBatch.fCoverage; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
+    bool isHairline() const { return fBatch.fIsHairline; }
+
+    struct BatchTracker {
+        GrColor fColor;
+        uint8_t fCoverage;
+        SkMatrix fViewMatrix;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+        bool fIsHairline;
+    };
+
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+};
 
 bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
                                              GrPipelineBuilder* pipelineBuilder,
@@ -360,24 +540,8 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
         }
     }
 
-    SkScalar tol = SK_Scalar1;
-    tol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
-
-    int vertexCnt;
-    int indexCnt;
-    GrPrimitiveType primType;
-    GrDrawTarget::AutoReleaseGeometry arg;
-    if (!this->createGeom(target,
-                          pipelineBuilder,
-                          &primType,
-                          &vertexCnt,
-                          &indexCnt,
-                          &arg,
-                          path,
-                          *stroke,
-                          tol)) {
-        return false;
-    }
+    const bool isHairline = stroke->isHairlineStyle();
+
     // Save the current xp on the draw state so we can reset it if needed
     SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
     // face culling doesn't make sense here
@@ -385,11 +549,11 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
 
     int                         passCount = 0;
     const GrStencilSettings*    passes[3];
-    GrPipelineBuilder::DrawFace       drawFace[3];
+    GrPipelineBuilder::DrawFace drawFace[3];
     bool                        reverse = false;
     bool                        lastPassIsBounds;
 
-    if (stroke->isHairlineStyle()) {
+    if (isHairline) {
         passCount = 1;
         if (stencilOnly) {
             passes[0] = &gDirectToStencil;
@@ -475,6 +639,9 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
         }
     }
 
+    SkScalar tol = SK_Scalar1;
+    SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
+
     SkRect devBounds;
     GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
 
@@ -513,26 +680,17 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
             if (passCount > 1) {
                 pipelineBuilder->setDisableColorXPFactory();
             }
-            GrPipelineBuilder::AutoRestoreEffects are(pipelineBuilder);
-            SkAutoTUnref<const GrGeometryProcessor> gp(
-                    GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
-                                                    color,
-                                                    viewMatrix,
-                                                    SkMatrix::I(),
-                                                    false,
-                                                    newCoverage));
-            if (indexCnt) {
-                target->drawIndexed(pipelineBuilder,
-                                    gp,
-                                    primType,
-                                    0,
-                                    0,
-                                    vertexCnt,
-                                    indexCnt,
-                                    &devBounds);
-            } else {
-                target->drawNonIndexed(pipelineBuilder, gp, primType, 0, vertexCnt, &devBounds);
-            }
+
+            DefaultPathBatch::Geometry geometry;
+            geometry.fColor = color;
+            geometry.fPath = path;
+            geometry.fTolerance = srcSpaceTol;
+            SkDEBUGCODE(geometry.fDevBounds = devBounds;)
+
+            SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
+                                                                 isHairline));
+
+            target->drawBatch(pipelineBuilder, batch, &devBounds);
         }
     }
     return true;