CompositingImagesBench.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * Copyright 2018 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/SkImage.h"
  10. #include "include/core/SkSurface.h"
  11. #include "include/utils/SkRandom.h"
  12. enum class ClampingMode {
  13. // Submit image set entries with the fast constraint
  14. kAlwaysFast,
  15. // Submit image set entries with the strict constraint
  16. kAlwaysStrict,
  17. // Submit non-right/bottom tiles as fast, the bottom-right corner as strict, and bottom or right
  18. // edge tiles as strict with geometry modification to match content area. These will be
  19. // submitted from left-to-right, top-to-bottom so will necessarily be split into many batches.
  20. kChromeTiling_RowMajor,
  21. // As above, but group all fast tiles first, then bottom and right edge tiles in a second batch.
  22. kChromeTiling_Optimal
  23. };
  24. enum class TransformMode {
  25. // Tiles will be axis aligned on integer pixels
  26. kNone,
  27. // Subpixel, tiles will be axis aligned but adjusted to subpixel coordinates
  28. kSubpixel,
  29. // Rotated, tiles will be rotated globally; they won't overlap but their device space bounds may
  30. kRotated,
  31. // Perspective, tiles will have global perspective
  32. kPerspective
  33. };
  34. /**
  35. * Simulates drawing layers images in a grid a la a tile based compositor.
  36. */
  37. class CompositingImages : public Benchmark {
  38. public:
  39. CompositingImages(SkISize imageSize, SkISize tileSize, SkISize tileGridSize,
  40. ClampingMode clampMode, TransformMode transformMode, int layerCnt)
  41. : fImageSize(imageSize)
  42. , fTileSize(tileSize)
  43. , fTileGridSize(tileGridSize)
  44. , fClampMode(clampMode)
  45. , fTransformMode(transformMode)
  46. , fLayerCnt(layerCnt) {
  47. fName.appendf("compositing_images_tile_size_%dx%d_grid_%dx%d_layers_%d",
  48. fTileSize.fWidth, fTileSize.fHeight, fTileGridSize.fWidth,
  49. fTileGridSize.fHeight, fLayerCnt);
  50. if (imageSize != tileSize) {
  51. fName.appendf("_image_%dx%d", imageSize.fWidth, imageSize.fHeight);
  52. }
  53. switch(clampMode) {
  54. case ClampingMode::kAlwaysFast:
  55. fName.append("_fast");
  56. break;
  57. case ClampingMode::kAlwaysStrict:
  58. fName.append("_strict");
  59. break;
  60. case ClampingMode::kChromeTiling_RowMajor:
  61. fName.append("_chrome");
  62. break;
  63. case ClampingMode::kChromeTiling_Optimal:
  64. fName.append("_chrome_optimal");
  65. break;
  66. }
  67. switch(transformMode) {
  68. case TransformMode::kNone:
  69. break;
  70. case TransformMode::kSubpixel:
  71. fName.append("_subpixel");
  72. break;
  73. case TransformMode::kRotated:
  74. fName.append("_rotated");
  75. break;
  76. case TransformMode::kPerspective:
  77. fName.append("_persp");
  78. break;
  79. }
  80. }
  81. bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
  82. protected:
  83. const char* onGetName() override { return fName.c_str(); }
  84. void onPerCanvasPreDraw(SkCanvas* canvas) override {
  85. // Use image size, which may be larger than the tile size (emulating how Chrome specifies
  86. // their tiles).
  87. auto ii = SkImageInfo::Make(fImageSize.fWidth, fImageSize.fHeight, kRGBA_8888_SkColorType,
  88. kPremul_SkAlphaType, nullptr);
  89. SkRandom random;
  90. int numImages = fLayerCnt * fTileGridSize.fWidth * fTileGridSize.fHeight;
  91. fImages.reset(new sk_sp<SkImage>[numImages]);
  92. for (int i = 0; i < numImages; ++i) {
  93. auto surf = canvas->makeSurface(ii);
  94. SkColor color = random.nextU();
  95. surf->getCanvas()->clear(color);
  96. SkPaint paint;
  97. paint.setColor(~color);
  98. paint.setBlendMode(SkBlendMode::kSrc);
  99. // While the image may be bigger than fTileSize, prepare its content as if fTileSize
  100. // is what will be visible.
  101. surf->getCanvas()->drawRect(
  102. SkRect::MakeLTRB(3, 3, fTileSize.fWidth - 3, fTileSize.fHeight - 3), paint);
  103. fImages[i] = surf->makeImageSnapshot();
  104. }
  105. }
  106. void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); }
  107. void onDraw(int loops, SkCanvas* canvas) override {
  108. SkPaint paint;
  109. paint.setFilterQuality(kLow_SkFilterQuality);
  110. paint.setAntiAlias(true);
  111. canvas->save();
  112. canvas->concat(this->getTransform());
  113. for (int i = 0; i < loops; ++i) {
  114. for (int l = 0; l < fLayerCnt; ++l) {
  115. SkAutoTArray<SkCanvas::ImageSetEntry> set(
  116. fTileGridSize.fWidth * fTileGridSize.fHeight);
  117. if (fClampMode == ClampingMode::kAlwaysFast ||
  118. fClampMode == ClampingMode::kAlwaysStrict) {
  119. // Simple 2D for loop, submit everything as a single batch
  120. int i = 0;
  121. for (int y = 0; y < fTileGridSize.fHeight; ++y) {
  122. for (int x = 0; x < fTileGridSize.fWidth; ++x) {
  123. set[i++] = this->getEntry(x, y, l);
  124. }
  125. }
  126. SkCanvas::SrcRectConstraint constraint =
  127. fClampMode == ClampingMode::kAlwaysFast
  128. ? SkCanvas::kFast_SrcRectConstraint
  129. : SkCanvas::kStrict_SrcRectConstraint;
  130. canvas->experimental_DrawEdgeAAImageSet(set.get(), i, nullptr, nullptr, &paint,
  131. constraint);
  132. } else if (fClampMode == ClampingMode::kChromeTiling_RowMajor) {
  133. // Same tile order, but break batching between fast and strict sections, and
  134. // adjust bottom and right tiles to encode content area distinct from src rect.
  135. int i = 0;
  136. for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
  137. int rowStart = i;
  138. for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
  139. set[i++] = this->getEntry(x, y, l);
  140. }
  141. // Flush "fast" horizontal row
  142. canvas->experimental_DrawEdgeAAImageSet(set.get() + rowStart,
  143. fTileGridSize.fWidth - 1, nullptr, nullptr, &paint,
  144. SkCanvas::kFast_SrcRectConstraint);
  145. // Then flush a single adjusted entry for the right edge
  146. SkPoint dstQuad[4];
  147. set[i++] = this->getAdjustedEntry(fTileGridSize.fWidth - 1, y, l, dstQuad);
  148. canvas->experimental_DrawEdgeAAImageSet(
  149. set.get() + fTileGridSize.fWidth - 1, 1, dstQuad, nullptr, &paint,
  150. SkCanvas::kStrict_SrcRectConstraint);
  151. }
  152. // For last row, accumulate it as a single strict batch
  153. int rowStart = i;
  154. SkAutoTArray<SkPoint> dstQuads(4 * (fTileGridSize.fWidth - 1));
  155. for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
  156. set[i++] = this->getAdjustedEntry(x, fTileGridSize.fHeight - 1, l,
  157. dstQuads.get() + x * 4);
  158. }
  159. // The corner can use conventional strict mode without geometric adjustment
  160. set[i++] = this->getEntry(
  161. fTileGridSize.fWidth - 1, fTileGridSize.fHeight - 1, l);
  162. canvas->experimental_DrawEdgeAAImageSet(set.get() + rowStart,
  163. fTileGridSize.fWidth, dstQuads.get(), nullptr, &paint,
  164. SkCanvas::kStrict_SrcRectConstraint);
  165. } else {
  166. SkASSERT(fClampMode == ClampingMode::kChromeTiling_Optimal);
  167. int i = 0;
  168. // Interior fast tiles
  169. for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
  170. for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
  171. set[i++] = this->getEntry(x, y, l);
  172. }
  173. }
  174. canvas->experimental_DrawEdgeAAImageSet(set.get(), i, nullptr, nullptr, &paint,
  175. SkCanvas::kFast_SrcRectConstraint);
  176. // Right edge
  177. int strictStart = i;
  178. SkAutoTArray<SkPoint> dstQuads(
  179. 4 * (fTileGridSize.fWidth + fTileGridSize.fHeight - 2));
  180. for (int y = 0; y < fTileGridSize.fHeight - 1; ++y) {
  181. set[i++] = this->getAdjustedEntry(fTileGridSize.fWidth - 1, y, l,
  182. dstQuads.get() + y * 4);
  183. }
  184. canvas->experimental_DrawEdgeAAImageSet(set.get() + strictStart,
  185. i - strictStart, dstQuads.get(), nullptr, &paint,
  186. SkCanvas::kStrict_SrcRectConstraint);
  187. int quadStart = 4 * (fTileGridSize.fHeight - 1);
  188. strictStart = i;
  189. for (int x = 0; x < fTileGridSize.fWidth - 1; ++x) {
  190. set[i++] = this->getAdjustedEntry(x, fTileGridSize.fHeight - 1, l,
  191. dstQuads.get() + quadStart + x * 4);
  192. }
  193. set[i++] = this->getEntry(
  194. fTileGridSize.fWidth - 1, fTileGridSize.fHeight - 1, l);
  195. canvas->experimental_DrawEdgeAAImageSet(set.get() + strictStart,
  196. i - strictStart, dstQuads.get() + quadStart, nullptr, &paint,
  197. SkCanvas::kStrict_SrcRectConstraint);
  198. }
  199. }
  200. // Prevent any batching between composited "frames".
  201. canvas->flush();
  202. }
  203. canvas->restore();
  204. }
  205. private:
  206. SkMatrix getTransform() const {
  207. SkMatrix m;
  208. switch(fTransformMode) {
  209. case TransformMode::kNone:
  210. m.setIdentity();
  211. break;
  212. case TransformMode::kSubpixel:
  213. m.setTranslate(0.5f, 0.5f);
  214. break;
  215. case TransformMode::kRotated:
  216. m.setRotate(15.f);
  217. break;
  218. case TransformMode::kPerspective: {
  219. m.setIdentity();
  220. m.setPerspY(0.001f);
  221. m.setSkewX(SkIntToScalar(8) / 25);
  222. break;
  223. }
  224. }
  225. return m;
  226. }
  227. SkIPoint onGetSize() override {
  228. SkRect size = SkRect::MakeWH(1.25f * fTileSize.fWidth * fTileGridSize.fWidth,
  229. 1.25f * fTileSize.fHeight * fTileGridSize.fHeight);
  230. this->getTransform().mapRect(&size);
  231. return SkIPoint::Make(SkScalarCeilToInt(size.width()), SkScalarCeilToInt(size.height()));
  232. }
  233. unsigned getEdgeFlags(int x, int y) const {
  234. unsigned flags = SkCanvas::kNone_QuadAAFlags;
  235. if (x == 0) {
  236. flags |= SkCanvas::kLeft_QuadAAFlag;
  237. } else if (x == fTileGridSize.fWidth - 1) {
  238. flags |= SkCanvas::kRight_QuadAAFlag;
  239. }
  240. if (y == 0) {
  241. flags |= SkCanvas::kTop_QuadAAFlag;
  242. } else if (y == fTileGridSize.fHeight - 1) {
  243. flags |= SkCanvas::kBottom_QuadAAFlag;
  244. }
  245. return flags;
  246. }
  247. SkCanvas::ImageSetEntry getEntry(int x, int y, int layer) const {
  248. int imageIdx =
  249. fTileGridSize.fWidth * fTileGridSize.fHeight * layer + fTileGridSize.fWidth * y + x;
  250. SkRect srcRect = SkRect::Make(fTileSize);
  251. // Make a non-identity transform between src and dst so bilerp isn't disabled.
  252. float dstWidth = srcRect.width() * 1.25f;
  253. float dstHeight = srcRect.height() * 1.25f;
  254. SkRect dstRect = SkRect::MakeXYWH(dstWidth * x, dstHeight * y, dstWidth, dstHeight);
  255. return SkCanvas::ImageSetEntry(fImages[imageIdx], srcRect, dstRect, 1.f,
  256. this->getEdgeFlags(x, y));
  257. }
  258. SkCanvas::ImageSetEntry getAdjustedEntry(int x, int y, int layer, SkPoint dstQuad[4]) const {
  259. SkASSERT(x == fTileGridSize.fWidth - 1 || y == fTileGridSize.fHeight - 1);
  260. SkCanvas::ImageSetEntry entry = this->getEntry(x, y, layer);
  261. SkRect contentRect = SkRect::Make(fImageSize);
  262. if (x == fTileGridSize.fWidth - 1) {
  263. // Right edge, so restrict horizontal content to tile width
  264. contentRect.fRight = fTileSize.fWidth;
  265. }
  266. if (y == fTileGridSize.fHeight - 1) {
  267. // Bottom edge, so restrict vertical content to tile height
  268. contentRect.fBottom = fTileSize.fHeight;
  269. }
  270. SkMatrix srcToDst = SkMatrix::MakeRectToRect(entry.fSrcRect, entry.fDstRect,
  271. SkMatrix::kFill_ScaleToFit);
  272. // Story entry's dstRect into dstQuad, and use contentRect and contentDst as its src and dst
  273. entry.fDstRect.toQuad(dstQuad);
  274. entry.fSrcRect = contentRect;
  275. entry.fDstRect = srcToDst.mapRect(contentRect);
  276. entry.fHasClip = true;
  277. return entry;
  278. }
  279. std::unique_ptr<sk_sp<SkImage>[]> fImages;
  280. SkString fName;
  281. SkISize fImageSize;
  282. SkISize fTileSize;
  283. SkISize fTileGridSize;
  284. ClampingMode fClampMode;
  285. TransformMode fTransformMode;
  286. int fLayerCnt;
  287. typedef Benchmark INHERITED;
  288. };
  289. // Subpixel = false; all of the draw commands align with integer pixels so AA will be automatically
  290. // turned off within the operation
  291. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
  292. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
  293. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
  294. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
  295. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
  296. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
  297. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
  298. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
  299. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
  300. // Subpixel = true; force the draw commands to not align with pixels exactly so AA remains on
  301. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
  302. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
  303. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
  304. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
  305. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
  306. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
  307. DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
  308. DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
  309. DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
  310. // Test different tiling scenarios inspired by Chrome's compositor
  311. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
  312. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kNone, 1));
  313. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kNone, 1));
  314. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kNone, 1));
  315. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
  316. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kSubpixel, 1));
  317. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kSubpixel, 1));
  318. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kSubpixel, 1));
  319. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kRotated, 1));
  320. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kRotated, 1));
  321. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kRotated, 1));
  322. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kRotated, 1));
  323. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kPerspective, 1));
  324. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kPerspective, 1));
  325. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kPerspective, 1));
  326. DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kPerspective, 1));