Index: bench/ShapesBench.cpp |
diff --git a/bench/ShapesBench.cpp b/bench/ShapesBench.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1a3702fb5415cebdf1479142638466c561143bb4 |
--- /dev/null |
+++ b/bench/ShapesBench.cpp |
@@ -0,0 +1,289 @@ |
+ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "Benchmark.h" |
+#include "SkCanvas.h" |
+#include "SkCommandLineFlags.h" |
+#include "SkPaint.h" |
+#include "SkRandom.h" |
+#include "SkRRect.h" |
+#include "SkString.h" |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <functional> |
+ |
+#define ENABLE_COMMAND_LINE_SHAPES_BENCH 0 |
+ |
+#if ENABLE_COMMAND_LINE_SHAPES_BENCH |
+DEFINE_string(shapesType, "mixed", "Type of shape to use in ShapesBench. Must be one of: " |
+ "rect, oval, rrect, mixed."); |
+DEFINE_string(innerShapesType, "none", "Type of inner shape to use in ShapesBench. Must be one of: " |
+ "none, rect, oval, rrect, mixed."); |
+DEFINE_int32(numShapes, 10000, "Number of shapes to draw in ShapesBench."); |
+DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench."); |
+DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?"); |
+#endif |
+ |
+/* |
+ * This class is used for several benchmarks that draw different primitive Skia shapes at various |
+ * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large |
+ * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order |
+ * to take advantage of instanced rendering approaches. |
+ */ |
+class ShapesBench : public Benchmark { |
+public: |
+ enum ShapesType { |
+ kNone_ShapesType, |
+ kRect_ShapesType, |
+ kOval_ShapesType, |
+ kRRect_ShapesType, |
+ kMixed_ShapesType |
+ }; |
+ |
+ ShapesBench(ShapesType shapesType, ShapesType innerShapesType, |
+ int numShapes, const SkISize& shapesSize, bool perspective) |
+ : fShapesType(shapesType) |
+ , fInnerShapesType(innerShapesType) |
+ , fNumShapes(numShapes) |
+ , fShapesSize(shapesSize) |
+ , fPerspective(perspective) { |
+ clampShapeSize(); |
+ } |
+ |
+#if ENABLE_COMMAND_LINE_SHAPES_BENCH |
+ ShapesBench() { |
+ if (!strcmp(FLAGS_shapesType[0], "rect")) { |
+ fShapesType = kRect_ShapesType; |
+ } else if (!strcmp(FLAGS_shapesType[0], "oval")) { |
+ fShapesType = kOval_ShapesType; |
+ } else if (!strcmp(FLAGS_shapesType[0], "rrect")) { |
+ fShapesType = kRRect_ShapesType; |
+ } else if (!strcmp(FLAGS_shapesType[0], "mixed")) { |
+ fShapesType = kMixed_ShapesType; |
+ } else { |
+ SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.", |
+ FLAGS_shapesType[0]); |
+ exit(-1); |
+ } |
+ if (!strcmp(FLAGS_innerShapesType[0], "none")) { |
+ fInnerShapesType = kNone_ShapesType; |
+ } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) { |
+ fInnerShapesType = kRect_ShapesType; |
+ } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) { |
+ fInnerShapesType = kOval_ShapesType; |
+ } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) { |
+ fInnerShapesType = kRRect_ShapesType; |
+ } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) { |
+ fInnerShapesType = kMixed_ShapesType; |
+ } else { |
+ SkDebugf("Invalid innerShapesType \"%s\". Must be one of: " |
+ "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]); |
+ exit(-1); |
+ } |
+ if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) { |
+ SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n", |
+ FLAGS_shapesSize[0]); |
+ exit(-1); |
+ } |
+ |
+ fNumShapes = FLAGS_numShapes; |
+ fPerspective = FLAGS_shapesPersp; |
+ |
+ clampShapeSize(); |
+ } |
+#endif |
+ |
+ bool isVisual() override { return true; } |
+ |
+private: |
+ void clampShapeSize() { |
+ float maxDiagonal = static_cast<float>(SkTMin(kBenchWidth, kBenchHeight)); |
+ float diagonal = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) + |
+ static_cast<float>(fShapesSize.height() * fShapesSize.height())); |
+ if (diagonal > maxDiagonal) { |
+ fShapesSize.fWidth = static_cast<int>(fShapesSize.width() * maxDiagonal / diagonal); |
+ fShapesSize.fHeight = static_cast<int>(fShapesSize.height() * maxDiagonal / diagonal); |
+ } |
+ } |
+ |
+ const char* onGetName() override { |
+ const char* shapeTypeNames[] = { |
+ "none", "rect", "oval", "rrect", "mixed" |
+ }; |
+ |
+ fName.printf("shapes_%s", shapeTypeNames[fShapesType]); |
+ |
+ if (kNone_ShapesType != fInnerShapesType) { |
+ fName.appendf("_inner_%s", shapeTypeNames[fInnerShapesType]); |
+ } |
+ |
+ fName.appendf("_%i_%ix%i", fNumShapes, fShapesSize.width(), fShapesSize.height()); |
+ |
+ if (fPerspective) { |
+ fName.append("_persp"); |
+ } |
+ |
+ return fName.c_str(); |
+ } |
+ SkIPoint onGetSize() override { return SkIPoint::Make(kBenchWidth, kBenchHeight); } |
+ |
+ void onDelayedSetup() override { |
+ SkScalar w = SkIntToScalar(fShapesSize.width()); |
+ SkScalar h = SkIntToScalar(fShapesSize.height()); |
+ |
+ fRect.setRect(SkRect::MakeXYWH(-w / 2, -h / 2, w, h)); |
+ fOval.setOval(fRect.rect()); |
+ fRRect.setNinePatch(fRect.rect(), w / 8, h / 13, w / 11, h / 7); |
+ |
+ if (kNone_ShapesType != fInnerShapesType) { |
+ fRect.inset(w / 7, h / 11, &fInnerRect); |
+ fInnerRect.offset(w / 28, h / 44); |
+ fInnerOval.setOval(fInnerRect.rect()); |
+ fInnerRRect.setRectXY(fInnerRect.rect(), w / 13, w / 7); |
+ } |
+ |
+ SkRandom rand; |
+ fShapes.push_back_n(fNumShapes); |
+ for (int i = 0; i < fNumShapes; i++) { |
+ float pad = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) + |
+ static_cast<float>(fShapesSize.height() * fShapesSize.height())); |
+ fShapes[i].fMatrix.setTranslate(0.5f * pad + rand.nextF() * (kBenchWidth - pad), |
+ 0.5f * pad + rand.nextF() * (kBenchHeight - pad)); |
+ fShapes[i].fMatrix.preRotate(rand.nextF() * 360.0f); |
+ if (fPerspective) { |
+ fShapes[i].fMatrix.setPerspX(0.00015f); |
+ fShapes[i].fMatrix.setPerspY(-0.00015f); |
+ } |
+ fShapes[i].fColor = rand.nextU() | 0xff808080; |
+ } |
+ for (int i = 0; i < fNumShapes; i++) { |
+ // Do this in a separate loop so mixed shapes get the same random numbers during |
+ // placement as non-mixed do. |
+ int shapeType = fShapesType; |
+ if (kMixed_ShapesType == shapeType) { |
+ shapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType); |
+ } |
+ int innerShapeType = fInnerShapesType; |
+ if (kMixed_ShapesType == innerShapeType) { |
+ innerShapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType); |
+ } |
+ if (kNone_ShapesType == innerShapeType) { |
+ switch (shapeType) { |
+ using namespace std; |
+ using namespace std::placeholders; |
+ case kRect_ShapesType: |
+ fShapes[i].fDraw = bind(&SkCanvas::drawRect, _1, cref(fRect.rect()), _2); |
+ break; |
+ case kOval_ShapesType: |
+ fShapes[i].fDraw = bind(&SkCanvas::drawOval, _1, cref(fOval.rect()), _2); |
+ break; |
+ case kRRect_ShapesType: |
+ fShapes[i].fDraw = bind(&SkCanvas::drawRRect, _1, cref(fRRect), _2); |
+ break; |
+ } |
+ } else { |
+ const SkRRect* outer; |
+ switch (shapeType) { |
+ case kRect_ShapesType: outer = &fRect; break; |
+ case kOval_ShapesType: outer = &fOval; break; |
+ case kRRect_ShapesType: outer = &fRRect; break; |
+ } |
+ const SkRRect* inner; |
+ switch (innerShapeType) { |
+ case kRect_ShapesType: inner = &fInnerRect; break; |
+ case kOval_ShapesType: inner = &fInnerOval; break; |
+ case kRRect_ShapesType: inner = &fInnerRRect; break; |
+ } |
+ fShapes[i].fDraw = std::bind(&SkCanvas::drawDRRect, std::placeholders::_1, |
+ std::cref(*outer), std::cref(*inner), |
+ std::placeholders::_2); |
+ } |
+ } |
+ } |
+ |
+ void onDraw(int loops, SkCanvas* canvas) override { |
+ SkPaint paint; |
+ this->setupPaint(&paint); |
+ for (int j = 0; j < loops; j++) { |
+ for (int i = 0; i < fNumShapes; i++) { |
+ canvas->save(); |
+ canvas->setMatrix(fShapes[i].fMatrix); |
+ paint.setColor(fShapes[i].fColor); |
+ fShapes[i].fDraw(canvas, paint); |
+ canvas->restore(); |
+ } |
+ } |
+ } |
+ |
+ enum { |
+ kBenchWidth = 1000, |
+ kBenchHeight = 1000 |
+ }; |
+ |
+ struct ShapeInfo { |
+ SkMatrix fMatrix; |
+ SkColor fColor; |
+ std::function<void(SkCanvas*, const SkPaint&)> fDraw; |
+ }; |
+ |
+ ShapesType fShapesType; |
+ ShapesType fInnerShapesType; |
+ int fNumShapes; |
+ SkISize fShapesSize; |
+ bool fPerspective; |
+ SkString fName; |
+ SkRRect fRect; |
+ SkRRect fOval; |
+ SkRRect fRRect; |
+ SkRRect fInnerRect; |
+ SkRRect fInnerOval; |
+ SkRRect fInnerRRect; |
+ SkTArray<ShapeInfo> fShapes; |
+ |
+ |
+ typedef Benchmark INHERITED; |
+}; |
+ |
+// Small primitives (CPU bound, in theory): |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType, |
+ 10000, SkISize::Make(32, 32), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType, |
+ 10000, SkISize::Make(32, 32), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType, |
+ 10000, SkISize::Make(32, 33), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType, |
+ 10000, SkISize::Make(32, 32), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType, |
+ 10000, SkISize::Make(32, 33), false);) |
+ |
+// Large primitives (GPU bound, in theory): |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType, |
+ 1000, SkISize::Make(500, 500), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType, |
+ 1000, SkISize::Make(500, 500), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType, |
+ 1000, SkISize::Make(500, 501), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType, |
+ 1000, SkISize::Make(500, 500), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType, |
+ 1000, SkISize::Make(500, 501), false);) |
+ |
+// Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation |
+// making them quite slow. Thus, reduce the counts substantially: |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType, |
+ 2000, SkISize::Make(32, 32), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType, |
+ 2000, SkISize::Make(32, 32), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType, |
+ 200, SkISize::Make(500, 500), false);) |
+DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType, |
+ 200, SkISize::Make(500, 500), false);) |
+ |
+#if ENABLE_COMMAND_LINE_SHAPES_BENCH |
+DEF_BENCH(return new ShapesBench;) |
+#endif |