1 /* 2 * Copyright 2019 Google LLC 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 "bench/Benchmark.h" 9 #include "include/core/SkBitmap.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkImage.h" 12 #include "include/core/SkPaint.h" 13 #include "include/gpu/ganesh/GrDirectContext.h" 14 #include "include/gpu/ganesh/SkImageGanesh.h" 15 #include "src/base/SkRandom.h" 16 #include "src/core/SkCanvasPriv.h" 17 #include "src/gpu/ganesh/GrCanvas.h" 18 #include "src/gpu/ganesh/GrOpsTypes.h" 19 #include "src/gpu/ganesh/SkGr.h" 20 #include "src/gpu/ganesh/SurfaceDrawContext.h" 21 22 // Benchmarks that exercise the bulk image and solid color quad APIs, under a variety of patterns: 23 enum class ImageMode { 24 kShared, // 1. One shared image referenced by every rectangle 25 kUnique, // 2. Unique image for every rectangle 26 kNone // 3. No image, solid color shading per rectangle 27 }; 28 // X 29 enum class DrawMode { 30 kBatch, // Bulk API submission, one call to draw every rectangle 31 kRef, // One standard SkCanvas draw call per rectangle 32 kQuad // One experimental draw call per rectangle, only for solid color draws 33 }; 34 // X 35 enum class RectangleLayout { 36 kRandom, // Random overlapping rectangles 37 kGrid // Small, non-overlapping rectangles in a grid covering the output surface 38 }; 39 40 // Benchmark runner that can be configured by template arguments. 41 template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode> 42 class BulkRectBench : public Benchmark { 43 public: 44 static_assert(kImageMode == ImageMode::kNone || kDrawMode != DrawMode::kQuad, 45 "kQuad only supported for solid color draws"); 46 47 inline static constexpr int kWidth = 1024; 48 inline static constexpr int kHeight = 1024; 49 50 // There will either be 0 images, 1 image, or 1 image per rect 51 inline static constexpr int kImageCount = kImageMode == ImageMode::kShared ? 52 1 : (kImageMode == ImageMode::kNone ? 0 : kRectCount); 53 isSuitableFor(Backend backend)54 bool isSuitableFor(Backend backend) override { 55 if (kDrawMode == DrawMode::kBatch && kImageMode == ImageMode::kNone) { 56 // Currently the bulk color quad API is only available on 57 // skgpu::ganesh::SurfaceDrawContext 58 return backend == Backend::kGanesh; 59 } else { 60 return this->INHERITED::isSuitableFor(backend); 61 } 62 } 63 64 protected: 65 SkRect fRects[kRectCount]; 66 sk_sp<SkImage> fImages[kImageCount > 0 ? kImageCount : 1]; 67 SkColor4f fColors[kRectCount]; 68 SkString fName; 69 computeName()70 void computeName() { 71 fName = "bulkrect"; 72 fName.appendf("_%d", kRectCount); 73 if (kLayout == RectangleLayout::kRandom) { 74 fName.append("_random"); 75 } else { 76 fName.append("_grid"); 77 } 78 if (kImageMode == ImageMode::kShared) { 79 fName.append("_sharedimage"); 80 } else if (kImageMode == ImageMode::kUnique) { 81 fName.append("_uniqueimages"); 82 } else { 83 fName.append("_solidcolor"); 84 } 85 if (kDrawMode == DrawMode::kBatch) { 86 fName.append("_batch"); 87 } else if (kDrawMode == DrawMode::kRef) { 88 fName.append("_ref"); 89 } else { 90 fName.append("_quad"); 91 } 92 } 93 drawImagesBatch(SkCanvas * canvas) const94 void drawImagesBatch(SkCanvas* canvas) const { 95 SkASSERT(kImageMode != ImageMode::kNone); 96 SkASSERT(kDrawMode == DrawMode::kBatch); 97 98 SkCanvas::ImageSetEntry batch[kRectCount]; 99 for (int i = 0; i < kRectCount; ++i) { 100 int imageIndex = kImageMode == ImageMode::kShared ? 0 : i; 101 batch[i].fImage = fImages[imageIndex]; 102 batch[i].fSrcRect = SkRect::MakeIWH(fImages[imageIndex]->width(), 103 fImages[imageIndex]->height()); 104 batch[i].fDstRect = fRects[i]; 105 batch[i].fAAFlags = SkCanvas::kAll_QuadAAFlags; 106 } 107 108 SkPaint paint; 109 paint.setAntiAlias(true); 110 111 canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr, 112 SkSamplingOptions(SkFilterMode::kLinear), &paint, 113 SkCanvas::kFast_SrcRectConstraint); 114 } 115 drawImagesRef(SkCanvas * canvas) const116 void drawImagesRef(SkCanvas* canvas) const { 117 SkASSERT(kImageMode != ImageMode::kNone); 118 SkASSERT(kDrawMode == DrawMode::kRef); 119 120 SkPaint paint; 121 paint.setAntiAlias(true); 122 123 for (int i = 0; i < kRectCount; ++i) { 124 int imageIndex = kImageMode == ImageMode::kShared ? 0 : i; 125 SkRect srcRect = SkRect::MakeIWH(fImages[imageIndex]->width(), 126 fImages[imageIndex]->height()); 127 canvas->drawImageRect(fImages[imageIndex].get(), srcRect, fRects[i], 128 SkSamplingOptions(SkFilterMode::kLinear), &paint, 129 SkCanvas::kFast_SrcRectConstraint); 130 } 131 } 132 drawSolidColorsBatch(SkCanvas * canvas) const133 void drawSolidColorsBatch(SkCanvas* canvas) const { 134 SkASSERT(kImageMode == ImageMode::kNone); 135 SkASSERT(kDrawMode == DrawMode::kBatch); 136 137 auto context = canvas->recordingContext(); 138 SkASSERT(context); 139 140 GrQuadSetEntry batch[kRectCount]; 141 for (int i = 0; i < kRectCount; ++i) { 142 batch[i].fRect = fRects[i]; 143 batch[i].fColor = fColors[i].premul(); 144 batch[i].fLocalMatrix = SkMatrix::I(); 145 batch[i].fAAFlags = GrQuadAAFlags::kAll; 146 } 147 148 SkPaint paint; 149 paint.setColor(SK_ColorWHITE); 150 paint.setAntiAlias(true); 151 152 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas); 153 SkMatrix view = canvas->getLocalToDeviceAs3x3(); 154 SkSurfaceProps props; 155 GrPaint grPaint; 156 SkPaintToGrPaint(context, sdc->colorInfo(), paint, view, props, &grPaint); 157 sdc->drawQuadSet(nullptr, std::move(grPaint), view, batch, kRectCount); 158 } 159 drawSolidColorsRef(SkCanvas * canvas) const160 void drawSolidColorsRef(SkCanvas* canvas) const { 161 SkASSERT(kImageMode == ImageMode::kNone); 162 SkASSERT(kDrawMode == DrawMode::kRef || kDrawMode == DrawMode::kQuad); 163 164 SkPaint paint; 165 paint.setAntiAlias(true); 166 for (int i = 0; i < kRectCount; ++i) { 167 if (kDrawMode == DrawMode::kRef) { 168 paint.setColor4f(fColors[i]); 169 canvas->drawRect(fRects[i], paint); 170 } else { 171 canvas->experimental_DrawEdgeAAQuad(fRects[i], nullptr, SkCanvas::kAll_QuadAAFlags, 172 fColors[i], SkBlendMode::kSrcOver); 173 } 174 } 175 } 176 onGetName()177 const char* onGetName() override { 178 if (fName.isEmpty()) { 179 this->computeName(); 180 } 181 return fName.c_str(); 182 } 183 onDelayedSetup()184 void onDelayedSetup() override { 185 static constexpr SkScalar kMinRectSize = 0.2f; 186 static constexpr SkScalar kMaxRectSize = 300.f; 187 188 SkRandom rand; 189 for (int i = 0; i < kRectCount; i++) { 190 if (kLayout == RectangleLayout::kRandom) { 191 SkScalar w = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize; 192 SkScalar h = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize; 193 194 SkScalar x = rand.nextF() * (kWidth - w); 195 SkScalar y = rand.nextF() * (kHeight - h); 196 197 fRects[i].setXYWH(x, y, w, h); 198 } else { 199 int gridSize = SkScalarCeilToInt(SkScalarSqrt(kRectCount)); 200 SkASSERT(gridSize * gridSize >= kRectCount); 201 202 SkScalar w = (kWidth - 1.f) / gridSize; 203 SkScalar h = (kHeight - 1.f) / gridSize; 204 205 SkScalar x = (i % gridSize) * w + 0.5f; // Offset to ensure AA doesn't get disabled 206 SkScalar y = (i / gridSize) * h + 0.5f; 207 208 fRects[i].setXYWH(x, y, w, h); 209 } 210 211 // Make sure we don't extend outside the render target, don't want to include clipping 212 // in the benchmark. 213 SkASSERT(SkRect::MakeWH(kWidth, kHeight).contains(fRects[i])); 214 215 fColors[i] = {rand.nextF(), rand.nextF(), rand.nextF(), 1.f}; 216 } 217 } 218 onPerCanvasPreDraw(SkCanvas * canvas)219 void onPerCanvasPreDraw(SkCanvas* canvas) override { 220 // Push the skimages to the GPU when using the GPU backend so that the texture creation is 221 // not part of the bench measurements. Always remake the images since they are so simple, 222 // and since they are context-specific, this works when the bench runs multiple GPU backends 223 auto direct = GrAsDirectContext(canvas->recordingContext()); 224 for (int i = 0; i < kImageCount; ++i) { 225 SkBitmap bm; 226 bm.allocN32Pixels(256, 256); 227 bm.eraseColor(fColors[i].toSkColor()); 228 auto image = bm.asImage(); 229 230 if (direct) { 231 fImages[i] = SkImages::TextureFromImage(direct, image); 232 } else { 233 fImages[i] = std::move(image); 234 } 235 } 236 } 237 onPerCanvasPostDraw(SkCanvas * canvas)238 void onPerCanvasPostDraw(SkCanvas* canvas) override { 239 for (int i = 0; i < kImageCount; ++i) { 240 // For Vulkan we need to make sure the bench isn't holding onto any refs to the 241 // GrContext when we go to delete the vulkan context (which happens before the bench is 242 // deleted). So reset all the images here so they aren't holding GrContext refs. 243 fImages[i].reset(); 244 } 245 } 246 onDraw(int loops,SkCanvas * canvas)247 void onDraw(int loops, SkCanvas* canvas) override { 248 for (int i = 0; i < loops; i++) { 249 if (kImageMode == ImageMode::kNone) { 250 if (kDrawMode == DrawMode::kBatch) { 251 this->drawSolidColorsBatch(canvas); 252 } else { 253 this->drawSolidColorsRef(canvas); 254 } 255 } else { 256 if (kDrawMode == DrawMode::kBatch) { 257 this->drawImagesBatch(canvas); 258 } else { 259 this->drawImagesRef(canvas); 260 } 261 } 262 } 263 } 264 onGetSize()265 SkISize onGetSize() override { 266 return { kWidth, kHeight }; 267 } 268 269 using INHERITED = Benchmark; 270 }; 271 272 // constructor call is wrapped in () so the macro doesn't break parsing the commas in the template 273 #define ADD_BENCH(n, layout, imageMode, drawMode) \ 274 DEF_BENCH( return (new BulkRectBench<n, layout, imageMode, drawMode>()); ) 275 276 #define ADD_BENCH_FAMILY(n, layout) \ 277 ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kBatch) \ 278 ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kRef) \ 279 ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kBatch) \ 280 ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kRef) \ 281 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kBatch) \ 282 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kRef) \ 283 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kQuad) 284 285 ADD_BENCH_FAMILY(1000, RectangleLayout::kRandom) 286 ADD_BENCH_FAMILY(1000, RectangleLayout::kGrid) 287 288 #undef ADD_BENCH_FAMILY 289 #undef ADD_BENCH 290