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