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