Implement caching of stroked paths in the tessellated path renderer.
authorsenorblanco <senorblanco@chromium.org>
Wed, 5 Aug 2015 20:18:03 +0000 (13:18 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 5 Aug 2015 20:18:03 +0000 (13:18 -0700)
This requires adding the stroke info to the cache key, and doing the
stroking and dashing before rendering as triangles.

BUG=skia:3755

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

include/gpu/GrTestUtils.h
src/gpu/GrTessellatingPathRenderer.cpp
src/gpu/GrTestUtils.cpp

index 6fed7e1..91f36ea 100644 (file)
@@ -16,6 +16,7 @@
 #include "SkRandom.h"
 #include "SkStrokeRec.h"
 
+class GrStrokeInfo;
 class SkMatrix;
 class SkPath;
 class SkRRect;
@@ -35,6 +36,7 @@ const SkRRect& TestRRectSimple(SkRandom*);
 const SkPath& TestPath(SkRandom*);
 const SkPath& TestPathConvex(SkRandom*);
 SkStrokeRec TestStrokeRec(SkRandom*);
+GrStrokeInfo TestStrokeInfo(SkRandom*);
 
 }
 
index 282efe2..5bbc475 100644 (file)
@@ -1384,9 +1384,10 @@ private:
 }  // namespace
 
 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
-    // This path renderer can draw all fill styles, but does not do antialiasing. It can do convex
-    // and concave paths, but we'll leave the convex ones to simpler algorithms.
-    return args.fStroke->isFillStyle() && !args.fAntiAlias && !args.fPath->isConvex();
+    // This path renderer can draw all fill styles, all stroke styles except hairlines, but does
+    // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to
+    // simpler algorithms.
+    return !args.fStroke->isHairlineStyle() && !args.fAntiAlias && !args.fPath->isConvex();
 }
 
 class TessellatingPathBatch : public GrBatch {
@@ -1394,9 +1395,10 @@ public:
 
     static GrBatch* Create(const GrColor& color,
                            const SkPath& path,
+                           const GrStrokeInfo& stroke,
                            const SkMatrix& viewMatrix,
                            SkRect clipBounds) {
-        return SkNEW_ARGS(TessellatingPathBatch, (color, path, viewMatrix, clipBounds));
+        return SkNEW_ARGS(TessellatingPathBatch, (color, path, stroke, viewMatrix, clipBounds));
     }
 
     const char* name() const override { return "TessellatingPathBatch"; }
@@ -1421,7 +1423,23 @@ public:
     int tessellate(GrUniqueKey* key,
                    GrResourceProvider* resourceProvider,
                    SkAutoTUnref<GrVertexBuffer>& vertexBuffer) {
-        SkRect pathBounds = fPath.getBounds();
+        SkPath path;
+        GrStrokeInfo stroke(fStroke);
+        if (stroke.isDashed()) {
+            if (!stroke.applyDashToPath(&path, &stroke, fPath)) {
+                return 0;
+            }
+        } else {
+            path = fPath;
+        }
+        if (!stroke.isFillStyle()) {
+            stroke.setResScale(SkScalarAbs(fViewMatrix.getMaxScale()));
+            if (!stroke.applyToPath(&path, path)) {
+                return 0;
+            }
+            stroke.setFillStyle();
+        }
+        SkRect pathBounds = path.getBounds();
         Comparator c;
         if (pathBounds.width() > pathBounds.height()) {
             c.sweep_lt = sweep_lt_horiz;
@@ -1433,7 +1451,7 @@ public:
         SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
         SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, pathBounds);
         int contourCnt;
-        int maxPts = GrPathUtils::worstCasePointCount(fPath, &contourCnt, tol);
+        int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, tol);
         if (maxPts <= 0) {
             return 0;
         }
@@ -1441,7 +1459,7 @@ public:
             SkDebugf("Path not rendered, too many verts (%d)\n", maxPts);
             return 0;
         }
-        SkPath::FillType fillType = fPath.getFillType();
+        SkPath::FillType fillType = path.getFillType();
         if (SkPath::IsInverseFillType(fillType)) {
             contourCnt++;
         }
@@ -1455,7 +1473,7 @@ public:
         // connectivity of one Edge per Vertex (will grow for intersections).
         SkChunkAlloc alloc(maxPts * (3 * sizeof(Vertex) + sizeof(Edge)));
         bool isLinear;
-        path_to_contours(fPath, tol, fClipBounds, contours.get(), alloc, &isLinear);
+        path_to_contours(path, tol, fClipBounds, contours.get(), alloc, &isLinear);
         Poly* polys;
         polys = contours_to_polys(contours.get(), contourCnt, c, alloc);
         int count = 0;
@@ -1503,13 +1521,15 @@ public:
         GrUniqueKey key;
         int clipBoundsSize32 =
             fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0;
-        GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32);
+        int strokeDataSize32 = fStroke.computeUniqueKeyFragmentData32Cnt();
+        GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32 + strokeDataSize32);
         builder[0] = fPath.getGenerationID();
         builder[1] = fPath.getFillType();
         // For inverse fills, the tessellation is dependent on clip bounds.
         if (fPath.isInverseFillType()) {
             memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds));
         }
+        fStroke.asUniqueKeyFragment(&builder[2 + clipBoundsSize32]);
         builder.finish();
         GrResourceProvider* rp = batchTarget->resourceProvider();
         SkAutoTUnref<GrVertexBuffer> vertexBuffer(rp->findAndRefTByUniqueKey<GrVertexBuffer>(key));
@@ -1561,10 +1581,12 @@ public:
 private:
     TessellatingPathBatch(const GrColor& color,
                           const SkPath& path,
+                          const GrStrokeInfo& stroke,
                           const SkMatrix& viewMatrix,
                           const SkRect& clipBounds)
       : fColor(color)
       , fPath(path)
+      , fStroke(stroke)
       , fViewMatrix(viewMatrix)
       , fClipBounds(clipBounds) {
         this->initClassID<TessellatingPathBatch>();
@@ -1575,6 +1597,7 @@ private:
 
     GrColor        fColor;
     SkPath         fPath;
+    GrStrokeInfo   fStroke;
     SkMatrix       fViewMatrix;
     SkRect         fClipBounds; // in source space
     GrPipelineInfo fPipelineInfo;
@@ -1596,7 +1619,8 @@ bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
     }
     vmi.mapRect(&clipBounds);
     SkAutoTUnref<GrBatch> batch(TessellatingPathBatch::Create(args.fColor, *args.fPath,
-                                                              *args.fViewMatrix, clipBounds));
+                                                              *args.fStroke, *args.fViewMatrix,
+                                                              clipBounds));
     args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
 
     return true;
@@ -1617,7 +1641,8 @@ BATCH_TEST_DEFINE(TesselatingPathBatch) {
         SkFAIL("Cannot invert matrix\n");
     }
     vmi.mapRect(&clipBounds);
-    return TessellatingPathBatch::Create(color, path, viewMatrix, clipBounds);
+    GrStrokeInfo strokeInfo = GrTest::TestStrokeInfo(random);
+    return TessellatingPathBatch::Create(color, path, strokeInfo, viewMatrix, clipBounds);
 }
 
 #endif
index b690d7e..9e1dbce 100644 (file)
@@ -5,8 +5,10 @@
  * found in the LICENSE file.
  */
 
+#include "GrStrokeInfo.h"
 #include "GrTestUtils.h"
 #include "SkMatrix.h"
+#include "SkPathEffect.h"
 #include "SkPath.h"
 #include "SkRRect.h"
 
@@ -216,21 +218,44 @@ const SkPath& TestPathConvex(SkRandom* random) {
     return gPath[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gPath)))];
 }
 
-SkStrokeRec TestStrokeRec(SkRandom* random) {
-    SkStrokeRec::InitStyle style =
-            SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
-    SkStrokeRec rec(style);
+static void randomize_stroke_rec(SkStrokeRec* rec, SkRandom* random) {
     bool strokeAndFill = random->nextBool();
     SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f;
-    rec.setStrokeStyle(strokeWidth, strokeAndFill);
+    rec->setStrokeStyle(strokeWidth, strokeAndFill);
 
     SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount));
     SkPaint::Join join = SkPaint::Join(random->nextULessThan(SkPaint::kJoinCount));
     SkScalar miterLimit = random->nextRangeScalar(1.f, 5.f);
-    rec.setStrokeParams(cap, join, miterLimit);
+    rec->setStrokeParams(cap, join, miterLimit);
+}
+
+SkStrokeRec TestStrokeRec(SkRandom* random) {
+    SkStrokeRec::InitStyle style =
+            SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
+    SkStrokeRec rec(style);
+    randomize_stroke_rec(&rec, random);
     return rec;
 }
 
+GrStrokeInfo TestStrokeInfo(SkRandom* random) {
+    SkStrokeRec::InitStyle style =
+            SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
+    GrStrokeInfo strokeInfo(style);
+    randomize_stroke_rec(&strokeInfo, random);
+    SkPathEffect::DashInfo dashInfo;
+    dashInfo.fCount = random->nextRangeU(1, 100);
+    dashInfo.fIntervals = SkNEW_ARRAY(SkScalar, dashInfo.fCount);
+    SkScalar sum = 0;
+    for (int i = 0; i < dashInfo.fCount; i++) {
+        dashInfo.fIntervals[i] = random->nextRangeScalar(SkDoubleToScalar(0.01),
+                                                         SkDoubleToScalar(10.0));
+        sum += dashInfo.fIntervals[i];
+    }
+    dashInfo.fPhase = random->nextRangeScalar(0, sum);
+    strokeInfo.setDashInfo(dashInfo);
+    return strokeInfo;
+}
+
 };
 
 #endif