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" 12 #include "include/core/SkCanvas.h" 13 #include "include/core/SkColorSpace.h" 14 #include "include/core/SkImage.h" 15 #include "include/core/SkSurface.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 Backend::kGanesh == 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 skgpu::Flush(canvas->getSurface()); 224 } 225 canvas->restore(); 226 } 227 228 private: getTransform() const229 SkMatrix getTransform() const { 230 SkMatrix m; 231 switch(fTransformMode) { 232 case TransformMode::kNone: 233 m.setIdentity(); 234 break; 235 case TransformMode::kSubpixel: 236 m.setTranslate(0.5f, 0.5f); 237 break; 238 case TransformMode::kRotated: 239 m.setRotate(15.f); 240 break; 241 case TransformMode::kPerspective: { 242 m.setIdentity(); 243 m.setPerspY(0.001f); 244 m.setSkewX(SkIntToScalar(8) / 25); 245 break; 246 } 247 } 248 return m; 249 } 250 onGetSize()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 getEdgeFlags(int x,int y) const258 unsigned getEdgeFlags(int x, int y) const { 259 unsigned flags = SkCanvas::kNone_QuadAAFlags; 260 if (x == 0) { 261 flags |= SkCanvas::kLeft_QuadAAFlag; 262 } else if (x == fTileGridSize.fWidth - 1) { 263 flags |= SkCanvas::kRight_QuadAAFlag; 264 } 265 266 if (y == 0) { 267 flags |= SkCanvas::kTop_QuadAAFlag; 268 } else if (y == fTileGridSize.fHeight - 1) { 269 flags |= SkCanvas::kBottom_QuadAAFlag; 270 } 271 return flags; 272 } 273 getEntry(int x,int y,int layer) const274 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 getAdjustedEntry(int x,int y,int layer,SkPoint dstQuad[4]) const286 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 325 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1)); 326 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1)); 327 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1)); 328 329 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4)); 330 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4)); 331 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 4)); 332 333 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16)); 334 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kNone, 16)); 335 DEF_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 338 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1)); 339 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1)); 340 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1)); 341 342 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4)); 343 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4)); 344 DEF_BENCH(return new CompositingImages({1024, 512}, {1024, 512}, {2, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 4)); 345 346 DEF_BENCH(return new CompositingImages({256, 256}, {256, 256}, {8, 8}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16)); 347 DEF_BENCH(return new CompositingImages({512, 512}, {512, 512}, {4, 4}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 16)); 348 DEF_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 351 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kNone, 1)); 352 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kNone, 1)); 353 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kNone, 1)); 354 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kNone, 1)); 355 356 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kSubpixel, 1)); 357 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kSubpixel, 1)); 358 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kSubpixel, 1)); 359 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kSubpixel, 1)); 360 361 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kRotated, 1)); 362 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kRotated, 1)); 363 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kRotated, 1)); 364 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kRotated, 1)); 365 366 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysFast, TransformMode::kPerspective, 1)); 367 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kAlwaysStrict, TransformMode::kPerspective, 1)); 368 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_RowMajor, TransformMode::kPerspective, 1)); 369 DEF_BENCH(return new CompositingImages({512, 512}, {380, 380}, {5, 5}, ClampingMode::kChromeTiling_Optimal, TransformMode::kPerspective, 1)); 370