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