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