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