From 4a6e40d9d2e0eb81e00d8b2bd20532aa28f61afb Mon Sep 17 00:00:00 2001 From: cdalton Date: Wed, 10 Feb 2016 14:54:21 -0800 Subject: Add bench and gm for shapes BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1677253002 patch from issue 1686673002 at patchset 20001 (http://crrev.com/1686673002#ps20001) Review URL: https://codereview.chromium.org/1677253002 --- bench/ShapesBench.cpp | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 bench/ShapesBench.cpp (limited to 'bench/ShapesBench.cpp') 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 +#include +#include + +#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(SkTMin(kBenchWidth, kBenchHeight)); + float diagonal = sqrtf(static_cast(fShapesSize.width() * fShapesSize.width()) + + static_cast(fShapesSize.height() * fShapesSize.height())); + if (diagonal > maxDiagonal) { + fShapesSize.fWidth = static_cast(fShapesSize.width() * maxDiagonal / diagonal); + fShapesSize.fHeight = static_cast(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(fShapesSize.width() * fShapesSize.width()) + + static_cast(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 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 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 -- cgit v1.2.3