ShapesBench.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*
  2. * Copyright 2016 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "bench/Benchmark.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkPaint.h"
  10. #include "include/core/SkRRect.h"
  11. #include "include/core/SkString.h"
  12. #include "include/utils/SkRandom.h"
  13. #include "tools/flags/CommandLineFlags.h"
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <functional>
  17. #define ENABLE_COMMAND_LINE_SHAPES_BENCH 0
  18. #if ENABLE_COMMAND_LINE_SHAPES_BENCH
  19. static DEFINE_string(shapesType, "mixed",
  20. "Type of shape to use in ShapesBench. Must be one of: "
  21. "rect, oval, rrect, mixed.");
  22. static DEFINE_string(innerShapesType, "none",
  23. "Type of inner shape to use in ShapesBench. Must be one of: "
  24. "none, rect, oval, rrect, mixed.");
  25. static DEFINE_int(numShapes, 10000, "Number of shapes to draw in ShapesBench.");
  26. static DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench.");
  27. static DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?");
  28. #endif
  29. /*
  30. * This class is used for several benchmarks that draw different primitive Skia shapes at various
  31. * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large
  32. * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order
  33. * to take advantage of instanced rendering approaches.
  34. */
  35. class ShapesBench : public Benchmark {
  36. public:
  37. enum ShapesType {
  38. kNone_ShapesType,
  39. kRect_ShapesType,
  40. kOval_ShapesType,
  41. kRRect_ShapesType,
  42. kMixed_ShapesType
  43. };
  44. ShapesBench(ShapesType shapesType, ShapesType innerShapesType,
  45. int numShapes, const SkISize& shapesSize, bool perspective)
  46. : fShapesType(shapesType)
  47. , fInnerShapesType(innerShapesType)
  48. , fNumShapes(numShapes)
  49. , fShapesSize(shapesSize)
  50. , fPerspective(perspective) {
  51. clampShapeSize();
  52. }
  53. #if ENABLE_COMMAND_LINE_SHAPES_BENCH
  54. ShapesBench() {
  55. if (!strcmp(FLAGS_shapesType[0], "rect")) {
  56. fShapesType = kRect_ShapesType;
  57. } else if (!strcmp(FLAGS_shapesType[0], "oval")) {
  58. fShapesType = kOval_ShapesType;
  59. } else if (!strcmp(FLAGS_shapesType[0], "rrect")) {
  60. fShapesType = kRRect_ShapesType;
  61. } else if (!strcmp(FLAGS_shapesType[0], "mixed")) {
  62. fShapesType = kMixed_ShapesType;
  63. } else {
  64. SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.",
  65. FLAGS_shapesType[0]);
  66. exit(-1);
  67. }
  68. if (!strcmp(FLAGS_innerShapesType[0], "none")) {
  69. fInnerShapesType = kNone_ShapesType;
  70. } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) {
  71. fInnerShapesType = kRect_ShapesType;
  72. } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) {
  73. fInnerShapesType = kOval_ShapesType;
  74. } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) {
  75. fInnerShapesType = kRRect_ShapesType;
  76. } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) {
  77. fInnerShapesType = kMixed_ShapesType;
  78. } else {
  79. SkDebugf("Invalid innerShapesType \"%s\". Must be one of: "
  80. "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]);
  81. exit(-1);
  82. }
  83. if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) {
  84. SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n",
  85. FLAGS_shapesSize[0]);
  86. exit(-1);
  87. }
  88. fNumShapes = FLAGS_numShapes;
  89. fPerspective = FLAGS_shapesPersp;
  90. clampShapeSize();
  91. }
  92. #endif
  93. private:
  94. void clampShapeSize() {
  95. float maxDiagonal = static_cast<float>(SkTMin(kBenchWidth, kBenchHeight));
  96. float diagonal = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
  97. static_cast<float>(fShapesSize.height() * fShapesSize.height()));
  98. if (diagonal > maxDiagonal) {
  99. fShapesSize.fWidth = static_cast<int>(fShapesSize.width() * maxDiagonal / diagonal);
  100. fShapesSize.fHeight = static_cast<int>(fShapesSize.height() * maxDiagonal / diagonal);
  101. }
  102. }
  103. const char* onGetName() override {
  104. const char* shapeTypeNames[] = {
  105. "none", "rect", "oval", "rrect", "mixed"
  106. };
  107. fName.printf("shapes_%s", shapeTypeNames[fShapesType]);
  108. if (kNone_ShapesType != fInnerShapesType) {
  109. fName.appendf("_inner_%s", shapeTypeNames[fInnerShapesType]);
  110. }
  111. fName.appendf("_%i_%ix%i", fNumShapes, fShapesSize.width(), fShapesSize.height());
  112. if (fPerspective) {
  113. fName.append("_persp");
  114. }
  115. return fName.c_str();
  116. }
  117. SkIPoint onGetSize() override { return SkIPoint::Make(kBenchWidth, kBenchHeight); }
  118. void onDelayedSetup() override {
  119. SkScalar w = SkIntToScalar(fShapesSize.width());
  120. SkScalar h = SkIntToScalar(fShapesSize.height());
  121. fRect.setRect(SkRect::MakeXYWH(-w / 2, -h / 2, w, h));
  122. fOval.setOval(fRect.rect());
  123. fRRect.setNinePatch(fRect.rect(), w / 8, h / 13, w / 11, h / 7);
  124. if (kNone_ShapesType != fInnerShapesType) {
  125. fRect.inset(w / 7, h / 11, &fInnerRect);
  126. fInnerRect.offset(w / 28, h / 44);
  127. fInnerOval.setOval(fInnerRect.rect());
  128. fInnerRRect.setRectXY(fInnerRect.rect(), w / 13, w / 7);
  129. }
  130. SkRandom rand;
  131. fShapes.push_back_n(fNumShapes);
  132. for (int i = 0; i < fNumShapes; i++) {
  133. float pad = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
  134. static_cast<float>(fShapesSize.height() * fShapesSize.height()));
  135. fShapes[i].fMatrix.setTranslate(0.5f * pad + rand.nextF() * (kBenchWidth - pad),
  136. 0.5f * pad + rand.nextF() * (kBenchHeight - pad));
  137. fShapes[i].fMatrix.preRotate(rand.nextF() * 360.0f);
  138. if (fPerspective) {
  139. fShapes[i].fMatrix.setPerspX(0.00015f);
  140. fShapes[i].fMatrix.setPerspY(-0.00015f);
  141. }
  142. fShapes[i].fColor = rand.nextU() | 0xff808080;
  143. }
  144. for (int i = 0; i < fNumShapes; i++) {
  145. // Do this in a separate loop so mixed shapes get the same random numbers during
  146. // placement as non-mixed do.
  147. int shapeType = fShapesType;
  148. if (kMixed_ShapesType == shapeType) {
  149. shapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
  150. }
  151. int innerShapeType = fInnerShapesType;
  152. if (kMixed_ShapesType == innerShapeType) {
  153. innerShapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
  154. }
  155. if (kNone_ShapesType == innerShapeType) {
  156. switch (shapeType) {
  157. using namespace std;
  158. using namespace std::placeholders;
  159. case kRect_ShapesType:
  160. fShapes[i].fDraw = bind(&SkCanvas::drawRect, _1, cref(fRect.rect()), _2);
  161. break;
  162. case kOval_ShapesType:
  163. fShapes[i].fDraw = bind(&SkCanvas::drawOval, _1, cref(fOval.rect()), _2);
  164. break;
  165. case kRRect_ShapesType:
  166. fShapes[i].fDraw = bind(&SkCanvas::drawRRect, _1, cref(fRRect), _2);
  167. break;
  168. }
  169. } else {
  170. const SkRRect* outer = nullptr;
  171. switch (shapeType) {
  172. case kRect_ShapesType: outer = &fRect; break;
  173. case kOval_ShapesType: outer = &fOval; break;
  174. case kRRect_ShapesType: outer = &fRRect; break;
  175. }
  176. const SkRRect* inner = nullptr;
  177. switch (innerShapeType) {
  178. case kRect_ShapesType: inner = &fInnerRect; break;
  179. case kOval_ShapesType: inner = &fInnerOval; break;
  180. case kRRect_ShapesType: inner = &fInnerRRect; break;
  181. }
  182. fShapes[i].fDraw = std::bind(&SkCanvas::drawDRRect, std::placeholders::_1,
  183. std::cref(*outer), std::cref(*inner),
  184. std::placeholders::_2);
  185. }
  186. }
  187. }
  188. void onDraw(int loops, SkCanvas* canvas) override {
  189. SkPaint paint;
  190. this->setupPaint(&paint);
  191. for (int j = 0; j < loops; j++) {
  192. for (int i = 0; i < fNumShapes; i++) {
  193. canvas->save();
  194. canvas->setMatrix(fShapes[i].fMatrix);
  195. paint.setColor(fShapes[i].fColor);
  196. fShapes[i].fDraw(canvas, paint);
  197. canvas->restore();
  198. }
  199. }
  200. }
  201. enum {
  202. kBenchWidth = 1000,
  203. kBenchHeight = 1000
  204. };
  205. struct ShapeInfo {
  206. SkMatrix fMatrix;
  207. SkColor fColor;
  208. std::function<void(SkCanvas*, const SkPaint&)> fDraw;
  209. };
  210. ShapesType fShapesType;
  211. ShapesType fInnerShapesType;
  212. int fNumShapes;
  213. SkISize fShapesSize;
  214. bool fPerspective;
  215. SkString fName;
  216. SkRRect fRect;
  217. SkRRect fOval;
  218. SkRRect fRRect;
  219. SkRRect fInnerRect;
  220. SkRRect fInnerOval;
  221. SkRRect fInnerRRect;
  222. SkTArray<ShapeInfo> fShapes;
  223. typedef Benchmark INHERITED;
  224. };
  225. #if ENABLE_COMMAND_LINE_SHAPES_BENCH
  226. DEF_BENCH(return new ShapesBench;)
  227. #else
  228. // Small primitives (CPU bound, in theory):
  229. DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
  230. 10000, SkISize::Make(32, 32), false);)
  231. DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
  232. 10000, SkISize::Make(32, 32), false);)
  233. DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
  234. 10000, SkISize::Make(32, 33), false);)
  235. DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
  236. 10000, SkISize::Make(32, 32), false);)
  237. DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
  238. 10000, SkISize::Make(32, 33), false);)
  239. // Large primitives (GPU bound, in theory):
  240. DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
  241. 100, SkISize::Make(500, 500), false);)
  242. DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
  243. 100, SkISize::Make(500, 500), false);)
  244. DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
  245. 100, SkISize::Make(500, 501), false);)
  246. DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
  247. 100, SkISize::Make(500, 500), false);)
  248. DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
  249. 100, SkISize::Make(500, 501), false);)
  250. // Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation
  251. // making them quite slow. Thus, reduce the counts substantially:
  252. DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
  253. 500, SkISize::Make(32, 32), false);)
  254. DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
  255. 500, SkISize::Make(32, 32), false);)
  256. DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
  257. 50, SkISize::Make(500, 500), false);)
  258. DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
  259. 50, SkISize::Make(500, 500), false);)
  260. #endif