• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "bench/Benchmark.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkSurface.h"
13 #include "include/utils/SkRandom.h"
14 
15 enum 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 
28 enum 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 
39 /**
40  * Simulates drawing layers images in a grid a la a tile based compositor.
41  */
42 class CompositingImages : public Benchmark {
43 public:
CompositingImages(SkISize imageSize,SkISize tileSize,SkISize tileGridSize,ClampingMode clampMode,TransformMode transformMode,int layerCnt)44     CompositingImages(SkISize imageSize, SkISize tileSize, SkISize tileGridSize,
45                       ClampingMode clampMode, TransformMode transformMode, int layerCnt)
46             : fImageSize(imageSize)
47             , fTileSize(tileSize)
48             , fTileGridSize(tileGridSize)
49             , fClampMode(clampMode)
50             , fTransformMode(transformMode)
51             , fLayerCnt(layerCnt) {
52         fName.appendf("compositing_images_tile_size_%dx%d_grid_%dx%d_layers_%d",
53                       fTileSize.fWidth, fTileSize.fHeight, fTileGridSize.fWidth,
54                       fTileGridSize.fHeight, fLayerCnt);
55         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;
84         }
85     }
86 
isSuitableFor(Backend backend)87     bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; }
88 
89 protected:
onGetName()90     const char* onGetName() override { return fName.c_str(); }
91 
onPerCanvasPreDraw(SkCanvas * canvas)92     void onPerCanvasPreDraw(SkCanvas* canvas) override {
93         // 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,
96                                     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);
107             // While the image may be bigger than fTileSize, prepare its content as if fTileSize
108             // is what will be visible.
109             surf->getCanvas()->drawRect(
110                     SkRect::MakeLTRB(3, 3, fTileSize.fWidth - 3, fTileSize.fHeight - 3), paint);
111             fImages[i] = surf->makeImageSnapshot();
112         }
113     }
114 
onPerCanvasPostDraw(SkCanvas *)115     void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); }
116 
onDraw(int loops,SkCanvas * canvas)117     void onDraw(int loops, SkCanvas* canvas) override {
118         SkPaint paint;
119         paint.setFilterQuality(kLow_SkFilterQuality);
120         paint.setAntiAlias(true);
121 
122         canvas->save();
123         canvas->concat(this->getTransform());
124 
125         for (int i = 0; i < loops; ++i) {
126             for (int l = 0; l < fLayerCnt; ++l) {
127                 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                         }
138                     }
139 
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);
213                 }
214             }
215             // Prevent any batching between composited "frames".
216             canvas->flush();
217         }
218         canvas->restore();
219     }
220 
221 private:
getTransform() const222     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;
242     }
243 
onGetSize()244     SkIPoint onGetSize() override {
245         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 
getEdgeFlags(int x,int y) const251     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 
getEntry(int x,int y,int layer) const267     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 
getAdjustedEntry(int x,int y,int layer,SkPoint dstQuad[4]) const279     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;
303     }
304 
305     std::unique_ptr<sk_sp<SkImage>[]> fImages;
306     SkString fName;
307     SkISize fImageSize;
308     SkISize fTileSize;
309     SkISize fTileGridSize;
310     ClampingMode fClampMode;
311     TransformMode fTransformMode;
312     int fLayerCnt;
313 
314     typedef Benchmark INHERITED;
315 };
316 
317 // Subpixel = false; all of the draw commands align with integer pixels so AA will be automatically
318 // turned off within the operation
319 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
320 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
321 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
322 
323 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
324 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
325 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
326 
327 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
328 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
329 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
330 
331 // Subpixel = true; force the draw commands to not align with pixels exactly so AA remains on
332 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
333 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
334 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
335 
336 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
337 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
338 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
339 
340 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
341 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
342 DEF_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
345 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
346 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kNone, 1));
347 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kNone, 1));
348 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kNone, 1));
349 
350 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
351 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kSubpixel, 1));
352 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kSubpixel, 1));
353 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kSubpixel, 1));
354 
355 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kRotated, 1));
356 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kRotated, 1));
357 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kRotated, 1));
358 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kRotated, 1));
359 
360 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kPerspective, 1));
361 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kPerspective, 1));
362 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kPerspective, 1));
363 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kPerspective, 1));
364