Flutter Engine
The Flutter Engine
CompositingImagesBench.cpp
Go to the documentation of this file.
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 "bench/GpuTools.h"
17#include "src/base/SkRandom.h"
18
19using namespace skia_private;
20
21enum class ClampingMode {
22 // Submit image set entries with the fast constraint
24 // Submit image set entries with the strict constraint
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.
30 // As above, but group all fast tiles first, then bottom and right edge tiles in a second batch.
32};
33
34enum 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
39 // Rotated, tiles will be rotated globally; they won't overlap but their device space bounds may
41 // Perspective, tiles will have global perspective
43};
44
45/**
46 * Simulates drawing layers images in a grid a la a tile based compositor.
47 */
49public:
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) {
66 fName.append("_fast");
67 break;
69 fName.append("_strict");
70 break;
72 fName.append("_chrome");
73 break;
75 fName.append("_chrome_optimal");
76 break;
77 }
78 switch(transformMode) {
80 break;
82 fName.append("_subpixel");
83 break;
85 fName.append("_rotated");
86 break;
88 fName.append("_persp");
89 break;
90 }
91 }
92
94
95protected:
96 const char* onGetName() override { return fName.c_str(); }
97
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);
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
121 void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); }
122
123 void onDraw(int loops, SkCanvas* canvas) override {
125 paint.setAntiAlias(true);
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) {
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
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,
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);
169 set.get() + fTileGridSize.fWidth - 1, 1, dstQuad, nullptr, sampling,
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,
185 } else {
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,
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,
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,
220 }
221 }
222 // Prevent any batching between composited "frames".
223 skgpu::Flush(canvas->getSurface());
224 }
225 canvas->restore();
226 }
227
228private:
229 SkMatrix getTransform() const {
230 SkMatrix m;
231 switch(fTransformMode) {
233 m.setIdentity();
234 break;
236 m.setTranslate(0.5f, 0.5f);
237 break;
239 m.setRotate(15.f);
240 break;
242 m.setIdentity();
243 m.setPerspY(0.001f);
244 m.setSkewX(SkIntToScalar(8) / 25);
245 break;
246 }
247 }
248 return m;
249 }
250
251 SkISize onGetSize() override {
252 SkRect size = SkRect::MakeWH(1.25f * fTileSize.fWidth * fTileGridSize.fWidth,
253 1.25f * fTileSize.fHeight * fTileGridSize.fHeight);
254 this->getTransform().mapRect(&size);
255 return SkISize::Make(SkScalarCeilToInt(size.width()), SkScalarCeilToInt(size.height()));
256 }
257
258 unsigned getEdgeFlags(int x, int y) const {
260 if (x == 0) {
262 } else if (x == fTileGridSize.fWidth - 1) {
264 }
265
266 if (y == 0) {
268 } else if (y == fTileGridSize.fHeight - 1) {
270 }
271 return flags;
272 }
273
274 SkCanvas::ImageSetEntry getEntry(int x, int y, int layer) const {
275 int imageIdx =
276 fTileGridSize.fWidth * fTileGridSize.fHeight * layer + fTileGridSize.fWidth * y + x;
277 SkRect srcRect = SkRect::Make(fTileSize);
278 // Make a non-identity transform between src and dst so bilerp isn't disabled.
279 float dstWidth = srcRect.width() * 1.25f;
280 float dstHeight = srcRect.height() * 1.25f;
281 SkRect dstRect = SkRect::MakeXYWH(dstWidth * x, dstHeight * y, dstWidth, dstHeight);
282 return SkCanvas::ImageSetEntry(fImages[imageIdx], srcRect, dstRect, 1.f,
283 this->getEdgeFlags(x, y));
284 }
285
286 SkCanvas::ImageSetEntry getAdjustedEntry(int x, int y, int layer, SkPoint dstQuad[4]) const {
287 SkASSERT(x == fTileGridSize.fWidth - 1 || y == fTileGridSize.fHeight - 1);
288
289 SkCanvas::ImageSetEntry entry = this->getEntry(x, y, layer);
290 SkRect contentRect = SkRect::Make(fImageSize);
291 if (x == fTileGridSize.fWidth - 1) {
292 // Right edge, so restrict horizontal content to tile width
293 contentRect.fRight = fTileSize.fWidth;
294 }
295 if (y == fTileGridSize.fHeight - 1) {
296 // Bottom edge, so restrict vertical content to tile height
297 contentRect.fBottom = fTileSize.fHeight;
298 }
299
300 SkMatrix srcToDst = SkMatrix::RectToRect(entry.fSrcRect, entry.fDstRect);
301
302 // Story entry's dstRect into dstQuad, and use contentRect and contentDst as its src and dst
303 entry.fDstRect.toQuad(dstQuad);
304 entry.fSrcRect = contentRect;
305 entry.fDstRect = srcToDst.mapRect(contentRect);
306 entry.fHasClip = true;
307
308 return entry;
309 }
310
311 std::unique_ptr<sk_sp<SkImage>[]> fImages;
312 SkString fName;
313 SkISize fImageSize;
314 SkISize fTileSize;
315 SkISize fTileGridSize;
316 ClampingMode fClampMode;
317 TransformMode fTransformMode;
318 int fLayerCnt;
319
320 using INHERITED = Benchmark;
321};
322
323// Subpixel = false; all of the draw commands align with integer pixels so AA will be automatically
324// turned off within the operation
325DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
326DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
327DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
328
329DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
330DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
331DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4));
332
333DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
334DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
335DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16));
336
337// Subpixel = true; force the draw commands to not align with pixels exactly so AA remains on
338DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
339DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
340DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
341
342DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
343DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
344DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4));
345
346DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
347DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
348DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16));
349
350// Test different tiling scenarios inspired by Chrome's compositor
351DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1));
352DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kNone, 1));
355
356DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1));
360
361DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kRotated, 1));
365
const char * backend
@ kChromeTiling_RowMajor
DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1))
@ kSubpixel
Definition: FontTest.cpp:57
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
uint32_t SkColor
Definition: SkColor.h:37
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SkIntToScalar(x)
Definition: SkScalar.h:57
void onPerCanvasPostDraw(SkCanvas *) override
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *canvas) override
void onPerCanvasPreDraw(SkCanvas *canvas) override
CompositingImages(SkISize imageSize, SkISize tileSize, SkISize tileGridSize, ClampingMode clampMode, TransformMode transformMode, int layerCnt)
const char * onGetName() override
SkSurface * getSurface() const
Definition: SkCanvas.cpp:365
void restore()
Definition: SkCanvas.cpp:461
sk_sp< SkSurface > makeSurface(const SkImageInfo &info, const SkSurfaceProps *props=nullptr)
Definition: SkCanvas.cpp:1195
SrcRectConstraint
Definition: SkCanvas.h:1541
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition: SkCanvas.h:1542
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition: SkCanvas.h:1543
void experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt, const SkPoint dstClips[], const SkMatrix preViewMatrices[], const SkSamplingOptions &, const SkPaint *paint=nullptr, SrcRectConstraint constraint=kStrict_SrcRectConstraint)
Definition: SkCanvas.cpp:1853
int save()
Definition: SkCanvas.cpp:447
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
@ kTop_QuadAAFlag
Definition: SkCanvas.h:1660
@ kRight_QuadAAFlag
Definition: SkCanvas.h:1661
@ kLeft_QuadAAFlag
Definition: SkCanvas.h:1659
@ kBottom_QuadAAFlag
Definition: SkCanvas.h:1662
@ kNone_QuadAAFlags
Definition: SkCanvas.h:1664
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition: SkMatrix.h:157
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
uint32_t nextU()
Definition: SkRandom.h:42
void append(const char text[])
Definition: SkString.h:203
const char * c_str() const
Definition: SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:550
const Paint & paint
Definition: color_source.cc:38
DlColor color
FlutterSemanticsFlag flags
double y
double x
SkSamplingOptions sampling
Definition: SkRecords.h:337
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
void Flush(SkSurface *surface)
Definition: GpuTools.h:25
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
void toQuad(SkPoint quad[4]) const
Definition: SkRect.cpp:50
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646