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