Adds a bunch of benchmarks around creating, transforming, testing path equality,
and concatting paths. Also allows benchs to do setup / tear down steps outside
of the cons/destructor via new SkBenchmark virtuals.

Review URL: http://codereview.appspot.com/6454137/



git-svn-id: http://skia.googlecode.com/svn/trunk@5054 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index d3437c4..f3330e4 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -13,6 +13,8 @@
 #include "SkRandom.h"
 #include "SkShader.h"
 #include "SkString.h"
+#include "SkTArray.h"
+
 
 enum Flags {
     kStroke_Flag = 1 << 0,
@@ -218,6 +220,385 @@
     typedef PathBench INHERITED;
 };
 
+class RandomPathBench : public SkBenchmark {
+public:
+    RandomPathBench(void* param) : INHERITED(param) {
+    }
+
+protected:
+    void createData(int minVerbs,
+                    int maxVerbs,
+                    int pathCnt,
+                    bool allowMoves = true,
+                    SkRect* bounds = NULL) {
+        SkRect tempBounds;
+        if (NULL == bounds) {
+            tempBounds.setXYWH(0, 0, SK_Scalar1, SK_Scalar1);
+            bounds = &tempBounds;
+        }
+        fVerbCnts.setReserve(pathCnt);
+        for (int i = 0; i < pathCnt; ++i) {
+            int vCount = fRandom.nextRangeU(minVerbs, maxVerbs + 1);
+            *fVerbCnts.append() = vCount;
+            for (int v = 0; v < vCount; ++v) {
+                int verb = fRandom.nextULessThan(SkPath::kDone_Verb);
+                if (SkPath::kMove_Verb == verb && !allowMoves) {
+                    --v;
+                    continue;
+                }
+                *fVerbs.append() = static_cast<SkPath::Verb>(verb);
+                static const int gPointCnt[] = {
+                    1, // kMove
+                    1, // kLine
+                    2, // kQuad
+                    3, // kCubic
+                    0, // kClose
+                };
+                int pCnt = gPointCnt[verb];
+                for (int p = 0; p < pCnt; ++p) {
+                    fPoints.append()->set(fRandom.nextRangeScalar(bounds->fLeft, bounds->fRight),
+                                          fRandom.nextRangeScalar(bounds->fTop, bounds->fBottom));
+                }
+            }
+        }
+        this->restartMakingPaths();
+    }
+
+    void restartMakingPaths() {
+        fCurrPath = 0;
+        fCurrVerb = 0;
+        fCurrPoint = 0;
+    }
+
+    void makePath(SkPath* path) {
+        int vCount = fVerbCnts[fCurrPath++];
+        for (int v = 0; v < vCount; ++v) {
+            switch (fVerbs[fCurrVerb++]) {
+                case SkPath::kMove_Verb:
+                    path->moveTo(fPoints[fCurrPoint]);
+                    ++fCurrPoint;
+                    break;
+                case SkPath::kLine_Verb:
+                    path->lineTo(fPoints[fCurrPoint]);
+                    ++fCurrPoint;
+                    break;
+                case SkPath::kQuad_Verb:
+                    path->quadTo(fPoints[fCurrPoint], fPoints[fCurrPoint + 1]);
+                    fCurrPoint += 2;
+                    break;
+                case SkPath::kCubic_Verb:
+                    path->cubicTo(fPoints[fCurrPoint],
+                                  fPoints[fCurrPoint + 1],
+                                  fPoints[fCurrPoint + 2]);
+                    fCurrPoint += 3;
+                    break;
+                case SkPath::kClose_Verb:
+                    path->close();
+                    break;
+                default:
+                    SkDEBUGFAIL("Unexpected path verb");
+                    break;
+            }
+        }
+    }
+
+    void finishedMakingPaths() {
+        fVerbCnts.reset();
+        fVerbs.reset();
+        fPoints.reset();
+    }
+
+private:
+    SkTDArray<int>          fVerbCnts;
+    SkTDArray<SkPath::Verb> fVerbs;
+    SkTDArray<SkPoint>      fPoints;
+    int                     fCurrPath;
+    int                     fCurrVerb;
+    int                     fCurrPoint;
+    SkRandom                fRandom;
+    typedef SkBenchmark INHERITED;
+};
+
+class PathCreateBench : public RandomPathBench {
+public:
+    PathCreateBench(void* param) : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(5000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_create";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        this->createData(10, 100, N);
+        SkASSERT(0 == fPaths.count());
+        fPaths.resize_back(N);
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths[i]);
+        }
+        this->restartMakingPaths();
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        this->finishedMakingPaths();
+        fPaths.reset();
+    }
+
+private:
+    SkTArray<SkPath> fPaths;
+
+    typedef RandomPathBench INHERITED;
+};
+
+class PathCopyBench : public RandomPathBench {
+public:
+    PathCopyBench(void* param) : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(50000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_copy";
+    }
+    virtual void onPreDraw() SK_OVERRIDE {
+        this->createData(10, 100, N);
+        SkASSERT(0 == fPaths.count());
+        fPaths.resize_back(N);
+        fCopies.resize_back(N);
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths[i]);
+        }
+        this->finishedMakingPaths();
+    }
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            fCopies[i] = fPaths[i];
+        }
+    }
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset();
+        fCopies.reset();
+    }
+
+private:
+    SkTArray<SkPath> fPaths;
+    SkTArray<SkPath> fCopies;
+
+    typedef RandomPathBench INHERITED;
+};
+
+class PathTransformBench : public RandomPathBench {
+public:
+    PathTransformBench(bool inPlace, void* param)
+        : INHERITED(param)
+        , fInPlace(inPlace) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(30000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fInPlace ? "path_transform_in_place" : "path_transform_copy";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        fMatrix.setScale(5 * SK_Scalar1, 6 * SK_Scalar1);
+        this->createData(10, 100, N);
+        SkASSERT(0 == fPaths.count());
+        SkASSERT(0 == fTransformed.count());
+        fPaths.resize_back(N);
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths[i]);
+        }
+        if (!fInPlace) {
+            fTransformed.resize_back(N);
+        }
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        if (fInPlace) {
+            for (int i = 0; i < N; ++i) {
+                fPaths[i].transform(fMatrix);
+            }
+        } else {
+            for (int i = 0; i < N; ++i) {
+                fPaths[i].transform(fMatrix, &fTransformed[i]);
+            }
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset();
+        fTransformed.reset();
+    }
+
+private:
+    SkTArray<SkPath> fPaths;
+    SkTArray<SkPath> fTransformed;
+    SkMatrix fMatrix;
+    bool fInPlace;
+    typedef RandomPathBench INHERITED;
+};
+
+class PathEqualityBench : public RandomPathBench {
+public:
+    PathEqualityBench(void* param)
+        : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(40000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_equality_50%";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        fParity = 0;
+        this->createData(10, 100, N);
+        SkASSERT(0 == fPaths.count());
+        SkASSERT(0 == fCopies.count());
+        fPaths.resize_back(N);
+        fCopies.resize_back(N);
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths[i]);
+            fCopies[i] = fPaths[i];
+        }
+        this->finishedMakingPaths();
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            fParity ^= (fPaths[i] == fCopies[i & ~0x1]);
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset();
+    }
+
+private:
+    bool fParity; // attempt to keep compiler from optimizing out the ==
+    SkTArray<SkPath> fPaths;
+    SkTArray<SkPath> fCopies;
+    typedef RandomPathBench INHERITED;
+};
+
+class SkBench_AddPathTest : public RandomPathBench {
+public:
+    enum AddType {
+        kAdd_AddType,
+        kAddTrans_AddType,
+        kAddMatrix_AddType,
+        kPathTo_AddType,
+        kReverseAdd_AddType,
+        kReversePathTo_AddType,
+    };
+
+    SkBench_AddPathTest(AddType type, void* param)
+        : INHERITED(param)
+        , fType(type) {
+        fMatrix.setRotate(60 * SK_Scalar1);
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(15000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        switch (fType) {
+            case kAdd_AddType:
+                return "path_add_path";
+            case kAddTrans_AddType:
+                return "path_add_path_trans";
+            case kAddMatrix_AddType:
+                return "path_add_path_matrix";
+            case kPathTo_AddType:
+                return "path_path_to";
+            case kReverseAdd_AddType:
+                return "path_reverse_add_path";
+            case kReversePathTo_AddType:
+                return "path_reverse_path_to";
+            default:
+                SkDEBUGFAIL("Bad add type");
+                return "";
+        }
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        // pathTo and reversePathTo assume a single contour path.
+        bool allowMoves = kPathTo_AddType != fType &&
+                          kReversePathTo_AddType != fType;
+        this->createData(10, 100, 2 * N, allowMoves);
+        SkASSERT(0 == fPaths0.count());
+        SkASSERT(0 == fPaths1.count());
+        fPaths0.resize_back(N);
+        fPaths1.resize_back(N);
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths0[i]);
+            this->makePath(&fPaths1[i]);
+        }
+        this->finishedMakingPaths();
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        switch (fType) {
+            case kAdd_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.addPath(fPaths1[i]);
+                }
+                break;
+            case kAddTrans_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.addPath(fPaths1[i], 2 * SK_Scalar1, 5 * SK_Scalar1);
+                }
+                break;
+            case kAddMatrix_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.addPath(fPaths1[i], fMatrix);
+                }
+                break;
+            case kPathTo_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.pathTo(fPaths1[i]);
+                }
+                break;
+            case kReverseAdd_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.reverseAddPath(fPaths1[i]);
+                }
+                break;
+            case kReversePathTo_AddType:
+                for (int i = 0; i < N; ++i) {
+                    SkPath result = fPaths0[i];
+                    result.reversePathTo(fPaths1[i]);
+                }
+                break;
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths0.reset();
+        fPaths1.reset();
+    }
+
+private:
+    AddType fType; // or reverseAddPath
+    SkTArray<SkPath> fPaths0;
+    SkTArray<SkPath> fPaths1;
+    SkMatrix         fMatrix;
+    typedef RandomPathBench INHERITED;
+};
 
 static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); }
 static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); }
@@ -286,3 +667,31 @@
 static BenchRegistry gRegLL00(FactLL00);
 static BenchRegistry gRegLL01(FactLL01);
 
+static SkBenchmark* FactCreate(void* p) { return new PathCreateBench(p); }
+static BenchRegistry gRegCreate(FactCreate);
+
+static SkBenchmark* FactCopy(void* p) { return new PathCopyBench(p); }
+static BenchRegistry gRegCopy(FactCopy);
+
+static SkBenchmark* FactPathTransformInPlace(void* p) { return new PathTransformBench(true, p); }
+static BenchRegistry gRegPathTransformInPlace(FactPathTransformInPlace);
+
+static SkBenchmark* FactPathTransformCopy(void* p) { return new PathTransformBench(false, p); }
+static BenchRegistry gRegPathTransformCopy(FactPathTransformCopy);
+
+static SkBenchmark* FactEquality(void* p) { return new PathEqualityBench(p); }
+static BenchRegistry gRegEquality(FactEquality);
+
+static SkBenchmark* FactAdd(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAdd_AddType, p); }
+static SkBenchmark* FactAddTrans(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAddTrans_AddType, p); }
+static SkBenchmark* FactAddMatrix(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAddMatrix_AddType, p); }
+static SkBenchmark* FactPathTo(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kPathTo_AddType, p); }
+static SkBenchmark* FactReverseAdd(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kReverseAdd_AddType, p); }
+static SkBenchmark* FactReverseTo(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kReversePathTo_AddType, p); }
+
+static BenchRegistry gRegAdd(FactAdd);
+static BenchRegistry gRegAddTrans(FactAddTrans);
+static BenchRegistry gRegAddMatrix(FactAddMatrix);
+static BenchRegistry gRegPathTo(FactPathTo);
+static BenchRegistry gRegReverseAdd(FactReverseAdd);
+static BenchRegistry gRegReverseTo(FactReverseTo);
diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp
index 19bbf59..c8552d5 100644
--- a/bench/SkBenchmark.cpp
+++ b/bench/SkBenchmark.cpp
@@ -29,10 +29,18 @@
     return this->onGetSize();
 }
 
+void SkBenchmark::preDraw() {
+    this->onPreDraw();
+}
+
 void SkBenchmark::draw(SkCanvas* canvas) {
     this->onDraw(canvas);
 }
 
+void SkBenchmark::postDraw() {
+    this->onPostDraw();
+}
+
 void SkBenchmark::setupPaint(SkPaint* paint) {
     paint->setAlpha(fForceAlpha);
     paint->setAntiAlias(fForceAA);
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
index 1d6fb41..28314ea 100644
--- a/bench/SkBenchmark.h
+++ b/bench/SkBenchmark.h
@@ -39,8 +39,19 @@
 
     const char* getName();
     SkIPoint getSize();
+
+    // Call before draw, allows the benchmark to do setup work outside of the
+    // timer. When a benchmark is repeatedly drawn, this should be called once
+    // before the initial draw.
+    void preDraw();
+
     void draw(SkCanvas*);
-    
+
+    // Call after draw, allows the benchmark to do cleanup work outside of the
+    // timer. When a benchmark is repeatedly drawn, this is only called once
+    // after the last draw.
+    void postDraw();
+
     void setForceAlpha(int alpha) {
         fForceAlpha = alpha;
     }
@@ -78,7 +89,9 @@
     void setupPaint(SkPaint* paint);
 
     virtual const char* onGetName() = 0;
+    virtual void onPreDraw() {}
     virtual void onDraw(SkCanvas*) = 0;
+    virtual void onPostDraw() {}
 
     virtual SkIPoint onGetSize();
 
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 7cfa849..0ea48e9 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -133,6 +133,18 @@
     void* fParam;
 };
 
+class AutoPrePostDraw {
+public:
+    AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) {
+        fBench->preDraw();
+    }
+    ~AutoPrePostDraw() {
+        fBench->postDraw();
+    }
+private:
+    SkBenchmark* fBench;
+};
+
 static void make_filename(const char name[], SkString* path) {
     path->set(name);
     for (int i = 0; name[i]; i++) {
@@ -750,6 +762,8 @@
             logger.logProgress(str);
         }
         
+        AutoPrePostDraw appd(bench);
+
         for (int x = 0; x < configs.count(); ++x) {
             int configIndex = configs[x];
 
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index a6d78ec..2bd2d76 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -866,6 +866,7 @@
 
     friend class SkAutoPathBoundsUpdate;
     friend class SkAutoDisableOvalCheck;
+    friend class SkBench_AddPathTest; // perf test pathTo/reversePathTo
 };
 
 #endif