Complex clipPath accounting

Add a clipPath heuristic to SkPathCounter, and extend
SkPictureGpuAnalyzer to support external clipPath() op accounting.

BUG=skia:5347
R=reed@google.com,mtklein@google.com,senorblanco@chromium.org,bsalomon@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2000423005

Review-Url: https://codereview.chromium.org/2000423005
diff --git a/include/core/SkPictureAnalyzer.h b/include/core/SkPictureAnalyzer.h
index fa8cdb1..b7a76cc 100644
--- a/include/core/SkPictureAnalyzer.h
+++ b/include/core/SkPictureAnalyzer.h
@@ -9,11 +9,13 @@
 #define SkPictureAnalyzer_DEFINED
 
 #include "SkRefCnt.h"
+#include "SkRegion.h"
 #include "SkTypes.h"
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
 
+class SkPath;
 class SkPicture;
 
 /** \class SkPictureGpuAnalyzer
@@ -29,7 +31,15 @@
     /**
      *  Process the given picture and accumulate its stats.
      */
-    void analyze(const SkPicture*);
+    void analyzePicture(const SkPicture*);
+
+    // Legacy/transitional alias.
+    void analyze(const SkPicture* picture) { this->analyzePicture(picture); }
+
+    /**
+     *  Process an explicit clipPath op.
+     */
+    void analyzeClipPath(const SkPath&, SkRegion::Op, bool doAntiAlias);
 
     /**
      *  Reset all accumulated stats.
diff --git a/src/core/SkPictureAnalyzer.cpp b/src/core/SkPictureAnalyzer.cpp
index 0ba4202..49c4fce 100644
--- a/src/core/SkPictureAnalyzer.cpp
+++ b/src/core/SkPictureAnalyzer.cpp
@@ -5,8 +5,11 @@
  * found in the LICENSE file.
  */
 
+#include "SkPath.h"
 #include "SkPicture.h"
 #include "SkPictureAnalyzer.h"
+#include "SkPictureCommon.h"
+#include "SkRecords.h"
 
 #if SK_SUPPORT_GPU
 
@@ -27,7 +30,7 @@
     this->analyze(picture.get());
 }
 
-void SkPictureGpuAnalyzer::analyze(const SkPicture* picture) {
+void SkPictureGpuAnalyzer::analyzePicture(const SkPicture* picture) {
     if (!picture || veto_predicate(fNumSlowPaths)) {
         return;
     }
@@ -35,6 +38,22 @@
     fNumSlowPaths += picture->numSlowPaths();
 }
 
+void SkPictureGpuAnalyzer::analyzeClipPath(const SkPath& path, SkRegion::Op op, bool doAntiAlias) {
+    if (veto_predicate(fNumSlowPaths)) {
+        return;
+    }
+
+    const SkRecords::ClipPath clipOp = {
+        SkIRect::MakeEmpty(), // Willie don't care.
+        path,
+        SkRecords::RegionOpAndAA(op, doAntiAlias)
+    };
+
+    SkPathCounter counter;
+    counter(clipOp);
+    fNumSlowPaths += counter.fNumSlowPathsAndDashEffects;
+}
+
 void SkPictureGpuAnalyzer::reset() {
     fNumSlowPaths = 0;
 }
diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h
index 16146be..1c38b04 100644
--- a/src/core/SkPictureCommon.h
+++ b/src/core/SkPictureCommon.h
@@ -123,6 +123,13 @@
         }
     }
 
+    void operator()(const SkRecords::ClipPath& op) {
+        // TODO: does the SkRegion op matter?
+        if (op.opAA.aa && !op.path.isConvex()) {
+            fNumSlowPathsAndDashEffects++;
+        }
+    }
+
     void operator()(const SkRecords::SaveLayer& op) {
         this->checkPaint(AsPtr(op.paint));
     }
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index c71e663..0603eb3 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -135,6 +135,25 @@
 
 #if SK_SUPPORT_GPU
 
+static SkPath make_convex_path() {
+    SkPath path;
+    path.lineTo(100, 0);
+    path.lineTo(50, 100);
+    path.close();
+
+    return path;
+}
+
+static SkPath make_concave_path() {
+    SkPath path;
+    path.lineTo(50, 50);
+    path.lineTo(100, 0);
+    path.lineTo(50, 100);
+    path.close();
+
+    return path;
+}
+
 static void test_gpu_veto(skiatest::Reporter* reporter) {
     SkPictureRecorder recorder;
 
@@ -261,6 +280,34 @@
     // ... but only when applied to drawPoint() calls
     REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
 
+    canvas = recorder.beginRecording(100, 100);
+    {
+        const SkPath convexClip = make_convex_path();
+        const SkPath concaveClip = make_concave_path();
+
+        for (int i = 0; i < 50; ++i) {
+            canvas->clipPath(convexClip);
+            canvas->clipPath(concaveClip);
+            canvas->clipPath(convexClip, SkRegion::kIntersect_Op, true);
+            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // Convex clips and non-AA concave clips are fine on the GPU.
+    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
+    canvas = recorder.beginRecording(100, 100);
+    {
+        const SkPath concaveClip = make_concave_path();
+        for (int i = 0; i < 50; ++i) {
+            canvas->clipPath(concaveClip, SkRegion::kIntersect_Op, true);
+            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // ... but AA concave clips are not.
+    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
     // Nest the previous picture inside a new one.
     canvas = recorder.beginRecording(100, 100);
     {
@@ -1380,6 +1427,22 @@
 
     analyzer.analyze(nestedVetoPicture.get());
     REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
+
+    analyzer.reset();
+
+    const SkPath convexClip = make_convex_path();
+    const SkPath concaveClip = make_concave_path();
+    for (int i = 0; i < 50; ++i) {
+        analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, false);
+        analyzer.analyzeClipPath(convexClip, SkRegion::kIntersect_Op, true);
+        analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, false);
+    }
+    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
+
+    for (int i = 0; i < 50; ++i) {
+        analyzer.analyzeClipPath(concaveClip, SkRegion::kIntersect_Op, true);
+    }
+    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
 }
 
 #endif // SK_SUPPORT_GPU