GameBench.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*
  2. * Copyright 2012 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/SkBitmap.h"
  9. #include "include/core/SkCanvas.h"
  10. #include "include/core/SkPaint.h"
  11. #include "include/core/SkShader.h"
  12. #include "include/core/SkString.h"
  13. #include "include/core/SkVertices.h"
  14. #include "include/utils/SkRandom.h"
  15. // This bench simulates the calls Skia sees from various HTML5 canvas
  16. // game bench marks
  17. class GameBench : public Benchmark {
  18. public:
  19. enum Type {
  20. kScale_Type,
  21. kTranslate_Type,
  22. kRotate_Type
  23. };
  24. enum Clear {
  25. kFull_Clear,
  26. kPartial_Clear
  27. };
  28. GameBench(Type type, Clear clear,
  29. bool aligned = false, bool useAtlas = false,
  30. bool useDrawVertices = false)
  31. : fType(type)
  32. , fClear(clear)
  33. , fAligned(aligned)
  34. , fUseAtlas(useAtlas)
  35. , fUseDrawVertices(useDrawVertices)
  36. , fName("game")
  37. , fNumSaved(0)
  38. , fInitialized(false) {
  39. switch (fType) {
  40. case kScale_Type:
  41. fName.append("_scale");
  42. break;
  43. case kTranslate_Type:
  44. fName.append("_trans");
  45. break;
  46. case kRotate_Type:
  47. fName.append("_rot");
  48. break;
  49. }
  50. if (aligned) {
  51. fName.append("_aligned");
  52. }
  53. if (kPartial_Clear == clear) {
  54. fName.append("_partial");
  55. } else {
  56. fName.append("_full");
  57. }
  58. if (useAtlas) {
  59. fName.append("_atlas");
  60. }
  61. if (useDrawVertices) {
  62. fName.append("_drawVerts");
  63. }
  64. // It's HTML 5 canvas, so always AA
  65. fName.append("_aa");
  66. }
  67. protected:
  68. const char* onGetName() override {
  69. return fName.c_str();
  70. }
  71. void onDelayedSetup() override {
  72. if (!fInitialized) {
  73. this->makeCheckerboard();
  74. this->makeAtlas();
  75. fInitialized = true;
  76. }
  77. }
  78. void onDraw(int loops, SkCanvas* canvas) override {
  79. SkRandom scaleRand;
  80. SkRandom transRand;
  81. SkRandom rotRand;
  82. int width, height;
  83. if (fUseAtlas) {
  84. width = kAtlasCellWidth;
  85. height = kAtlasCellHeight;
  86. } else {
  87. width = kCheckerboardWidth;
  88. height = kCheckerboardHeight;
  89. }
  90. SkPaint clearPaint;
  91. clearPaint.setColor(0xFF000000);
  92. clearPaint.setAntiAlias(true);
  93. SkISize size = canvas->getBaseLayerSize();
  94. SkScalar maxTransX, maxTransY;
  95. if (kScale_Type == fType) {
  96. maxTransX = size.fWidth - (1.5f * width);
  97. maxTransY = size.fHeight - (1.5f * height);
  98. } else if (kTranslate_Type == fType) {
  99. maxTransX = SkIntToScalar(size.fWidth - width);
  100. maxTransY = SkIntToScalar(size.fHeight - height);
  101. } else {
  102. SkASSERT(kRotate_Type == fType);
  103. // Yes, some rotations will be off the top and left sides
  104. maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
  105. maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
  106. }
  107. SkMatrix mat;
  108. SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
  109. SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
  110. SkPoint verts[4] = { // for drawVertices path
  111. { 0, 0 },
  112. { 0, SkIntToScalar(height) },
  113. { SkIntToScalar(width), SkIntToScalar(height) },
  114. { SkIntToScalar(width), 0 }
  115. };
  116. uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
  117. SkPaint p;
  118. p.setColor(0xFF000000);
  119. p.setFilterQuality(kLow_SkFilterQuality);
  120. SkPaint p2; // for drawVertices path
  121. p2.setColor(0xFF000000);
  122. p2.setFilterQuality(kLow_SkFilterQuality);
  123. p2.setShader(fAtlas.makeShader());
  124. for (int i = 0; i < loops; ++i, ++fNumSaved) {
  125. if (0 == i % kNumBeforeClear) {
  126. if (kPartial_Clear == fClear) {
  127. for (int j = 0; j < fNumSaved; ++j) {
  128. canvas->setMatrix(SkMatrix::I());
  129. mat.setTranslate(fSaved[j][0], fSaved[j][1]);
  130. if (kScale_Type == fType) {
  131. mat.preScale(fSaved[j][2], fSaved[j][2]);
  132. } else if (kRotate_Type == fType) {
  133. mat.preRotate(fSaved[j][2]);
  134. }
  135. canvas->concat(mat);
  136. canvas->drawRect(clearRect, clearPaint);
  137. }
  138. } else {
  139. canvas->clear(0xFF000000);
  140. }
  141. fNumSaved = 0;
  142. }
  143. SkASSERT(fNumSaved < kNumBeforeClear);
  144. canvas->setMatrix(SkMatrix::I());
  145. fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
  146. fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
  147. if (fAligned) {
  148. // make the translations integer aligned
  149. fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
  150. fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
  151. }
  152. mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
  153. if (kScale_Type == fType) {
  154. fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
  155. mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
  156. } else if (kRotate_Type == fType) {
  157. fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
  158. mat.preRotate(fSaved[fNumSaved][2]);
  159. }
  160. canvas->concat(mat);
  161. if (fUseAtlas) {
  162. const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
  163. SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
  164. if (fUseDrawVertices) {
  165. SkPoint uvs[4] = {
  166. { SkIntToScalar(src.fLeft), SkIntToScalar(src.fBottom) },
  167. { SkIntToScalar(src.fLeft), SkIntToScalar(src.fTop) },
  168. { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
  169. { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
  170. };
  171. canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
  172. 4, verts, uvs, nullptr, 6, indices),
  173. SkBlendMode::kModulate, p2);
  174. } else {
  175. canvas->drawBitmapRect(fAtlas, src, dst, &p,
  176. SkCanvas::kFast_SrcRectConstraint);
  177. }
  178. } else {
  179. canvas->drawBitmapRect(fCheckerboard, dst, &p);
  180. }
  181. }
  182. }
  183. private:
  184. static const int kCheckerboardWidth = 64;
  185. static const int kCheckerboardHeight = 128;
  186. static const int kAtlasCellWidth = 48;
  187. static const int kAtlasCellHeight = 36;
  188. static const int kNumAtlasedX = 5;
  189. static const int kNumAtlasedY = 5;
  190. static const int kAtlasSpacer = 2;
  191. static const int kTotAtlasWidth = kNumAtlasedX * kAtlasCellWidth +
  192. (kNumAtlasedX+1) * kAtlasSpacer;
  193. static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
  194. (kNumAtlasedY+1) * kAtlasSpacer;
  195. static const int kNumBeforeClear = 100;
  196. Type fType;
  197. Clear fClear;
  198. bool fAligned;
  199. bool fUseAtlas;
  200. bool fUseDrawVertices;
  201. SkString fName;
  202. int fNumSaved; // num draws stored in 'fSaved'
  203. bool fInitialized;
  204. // 0 & 1 are always x & y translate. 2 is either scale or rotate.
  205. SkScalar fSaved[kNumBeforeClear][3];
  206. SkBitmap fCheckerboard;
  207. SkBitmap fAtlas;
  208. SkIRect fAtlasRects[kNumAtlasedX][kNumAtlasedY];
  209. // Note: the resulting checker board has transparency
  210. void makeCheckerboard() {
  211. static int kCheckSize = 16;
  212. fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
  213. for (int y = 0; y < kCheckerboardHeight; ++y) {
  214. int even = (y / kCheckSize) % 2;
  215. SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
  216. for (int x = 0; x < kCheckerboardWidth; ++x) {
  217. if (even == (x / kCheckSize) % 2) {
  218. *scanline++ = 0xFFFF0000;
  219. } else {
  220. *scanline++ = 0x00000000;
  221. }
  222. }
  223. }
  224. }
  225. // Note: the resulting atlas has transparency
  226. void makeAtlas() {
  227. SkRandom rand;
  228. SkColor colors[kNumAtlasedX][kNumAtlasedY];
  229. for (int y = 0; y < kNumAtlasedY; ++y) {
  230. for (int x = 0; x < kNumAtlasedX; ++x) {
  231. colors[x][y] = rand.nextU() | 0xff000000;
  232. fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
  233. kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
  234. kAtlasCellWidth,
  235. kAtlasCellHeight);
  236. }
  237. }
  238. fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
  239. for (int y = 0; y < kTotAtlasHeight; ++y) {
  240. int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
  241. bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
  242. SkPMColor* scanline = fAtlas.getAddr32(0, y);
  243. for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
  244. int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
  245. bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
  246. if (inColorX && inColorY) {
  247. SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
  248. *scanline = colors[colorX][colorY];
  249. } else {
  250. *scanline = 0x00000000;
  251. }
  252. }
  253. }
  254. }
  255. typedef Benchmark INHERITED;
  256. };
  257. // Partial clear
  258. DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
  259. DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
  260. DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
  261. DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
  262. // Full clear
  263. DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
  264. DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
  265. DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
  266. DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
  267. // Atlased
  268. DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
  269. DEF_BENCH(return new GameBench(
  270. GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)