aboutsummaryrefslogtreecommitdiffhomepage
path: root/bench/ShapesBench.cpp
diff options
context:
space:
mode:
authorGravatar cdalton <cdalton@nvidia.com>2016-02-10 14:54:21 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-02-10 14:54:21 -0800
commit4a6e40d9d2e0eb81e00d8b2bd20532aa28f61afb (patch)
tree0c3b292d130b40135b587b57a960c1b77913e733 /bench/ShapesBench.cpp
parent12d47ce49e65e27f39fa20b78445e637c95b19b8 (diff)
Add bench and gm for shapes
Diffstat (limited to 'bench/ShapesBench.cpp')
-rw-r--r--bench/ShapesBench.cpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/bench/ShapesBench.cpp b/bench/ShapesBench.cpp
new file mode 100644
index 0000000000..1a3702fb54
--- /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