blob: c4a401f16f8aa028991fd66eab90226c3bcf08f8 [file] [log] [blame]
Michael Ludwig1beb8ae2019-10-28 13:22:11 -04001/*
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"
12#include "include/gpu/GrContext.h"
13#include "include/utils/SkRandom.h"
14
15#include "src/gpu/GrClip.h"
16#include "src/gpu/GrRenderTargetContext.h"
17#include "src/gpu/SkGr.h"
18
19// Benchmarks that exercise the bulk image and solid color quad APIs, under a variety of patterns:
20enum class ImageMode {
21 kShared, // 1. One shared image referenced by every rectangle
22 kUnique, // 2. Unique image for every rectangle
23 kNone // 3. No image, solid color shading per rectangle
24};
25// X
26enum class DrawMode {
27 kBatch, // Bulk API submission, one call to draw every rectangle
28 kRef, // One standard SkCanvas draw call per rectangle
29 kQuad // One experimental draw call per rectangle, only for solid color draws
30};
31// X
32enum class RectangleLayout {
33 kRandom, // Random overlapping rectangles
34 kGrid // Small, non-overlapping rectangles in a grid covering the output surface
35};
36
37// Benchmark runner that can be configured by template arguments.
38template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode>
39class BulkRectBench : public Benchmark {
40public:
41 static_assert(kImageMode == ImageMode::kNone || kDrawMode != DrawMode::kQuad,
42 "kQuad only supported for solid color draws");
43
44 static constexpr int kWidth = 1024;
45 static constexpr int kHeight = 1024;
46
47 // There will either be 0 images, 1 image, or 1 image per rect
48 static constexpr int kImageCount = kImageMode == ImageMode::kShared ?
49 1 : (kImageMode == ImageMode::kNone ? 0 : kRectCount);
50
51 bool isSuitableFor(Backend backend) override {
52 if (kDrawMode == DrawMode::kBatch && kImageMode == ImageMode::kNone) {
53 // Currently the bulk color quad API is only available on GrRenderTargetContext
54 return backend == kGPU_Backend;
55 } else {
56 return this->INHERITED::isSuitableFor(backend);
57 }
58 }
59
60protected:
61 SkRect fRects[kRectCount];
62 sk_sp<SkImage> fImages[kImageCount];
63 SkColor4f fColors[kRectCount];
64 SkString fName;
65
66 void computeName() {
67 fName = "bulkrect";
68 fName.appendf("_%d", kRectCount);
69 if (kLayout == RectangleLayout::kRandom) {
70 fName.append("_random");
71 } else {
72 fName.append("_grid");
73 }
74 if (kImageMode == ImageMode::kShared) {
75 fName.append("_sharedimage");
76 } else if (kImageMode == ImageMode::kUnique) {
77 fName.append("_uniqueimages");
78 } else {
79 fName.append("_solidcolor");
80 }
81 if (kDrawMode == DrawMode::kBatch) {
82 fName.append("_batch");
83 } else if (kDrawMode == DrawMode::kRef) {
84 fName.append("_ref");
85 } else {
86 fName.append("_quad");
87 }
88 }
89
90 void drawImagesBatch(SkCanvas* canvas) const {
91 SkASSERT(kImageMode != ImageMode::kNone);
92 SkASSERT(kDrawMode == DrawMode::kBatch);
93
94 SkCanvas::ImageSetEntry batch[kRectCount];
95 for (int i = 0; i < kRectCount; ++i) {
96 int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
97 batch[i].fImage = fImages[imageIndex];
98 batch[i].fSrcRect = SkRect::MakeIWH(fImages[imageIndex]->width(),
99 fImages[imageIndex]->height());
100 batch[i].fDstRect = fRects[i];
101 batch[i].fAAFlags = SkCanvas::kAll_QuadAAFlags;
102 }
103
104 SkPaint paint;
105 paint.setAntiAlias(true);
106 paint.setFilterQuality(kLow_SkFilterQuality);
107
108 canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr, &paint,
109 SkCanvas::kFast_SrcRectConstraint);
110 }
111
112 void drawImagesRef(SkCanvas* canvas) const {
113 SkASSERT(kImageMode != ImageMode::kNone);
114 SkASSERT(kDrawMode == DrawMode::kRef);
115
116 SkPaint paint;
117 paint.setAntiAlias(true);
118 paint.setFilterQuality(kLow_SkFilterQuality);
119
120 for (int i = 0; i < kRectCount; ++i) {
121 int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
122 SkIRect srcRect = SkIRect::MakeWH(fImages[imageIndex]->width(),
123 fImages[imageIndex]->height());
124 canvas->drawImageRect(fImages[imageIndex].get(), srcRect, fRects[i], &paint,
125 SkCanvas::kFast_SrcRectConstraint);
126 }
127 }
128
129 void drawSolidColorsBatch(SkCanvas* canvas) const {
130 SkASSERT(kImageMode == ImageMode::kNone);
131 SkASSERT(kDrawMode == DrawMode::kBatch);
132
133 GrContext* context = canvas->getGrContext();
134 SkASSERT(context);
135
136 GrRenderTargetContext::QuadSetEntry batch[kRectCount];
137 for (int i = 0; i < kRectCount; ++i) {
138 batch[i].fRect = fRects[i];
139 batch[i].fColor = fColors[i].premul();
140 batch[i].fLocalMatrix = SkMatrix::I();
141 batch[i].fAAFlags = GrQuadAAFlags::kAll;
142 }
143
144 SkPaint paint;
145 paint.setColor(SK_ColorWHITE);
146 paint.setAntiAlias(true);
147
148 GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
149 SkMatrix view = canvas->getTotalMatrix();
150 GrPaint grPaint;
151 SkPaintToGrPaint(context, rtc->colorInfo(), paint, view, &grPaint);
152 rtc->drawQuadSet(GrNoClip(), std::move(grPaint), GrAA::kYes, view, batch, kRectCount);
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
218 GrContext* context = canvas->getGrContext();
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
225 fImages[i] = context ? image->makeTextureImage(context) : std::move(image);
226 }
227 }
228
229 void onDraw(int loops, SkCanvas* canvas) override {
230 for (int i = 0; i < loops; i++) {
231 if (kImageMode == ImageMode::kNone) {
232 if (kDrawMode == DrawMode::kBatch) {
233 this->drawSolidColorsBatch(canvas);
234 } else {
235 this->drawSolidColorsRef(canvas);
236 }
237 } else {
238 if (kDrawMode == DrawMode::kBatch) {
239 this->drawImagesBatch(canvas);
240 } else {
241 this->drawImagesRef(canvas);
242 }
243 }
244 }
245 }
246
247 SkIPoint onGetSize() override {
248 return { kWidth, kHeight };
249 }
250
251 typedef Benchmark INHERITED;
252};
253
254// constructor call is wrapped in () so the macro doesn't break parsing the commas in the template
255#define ADD_BENCH(n, layout, imageMode, drawMode) \
256 DEF_BENCH( return (new BulkRectBench<n, layout, imageMode, drawMode>()); )
257
258#define ADD_BENCH_FAMILY(n, layout) \
259 ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kBatch) \
260 ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kRef) \
261 ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kBatch) \
262 ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kRef) \
263 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kBatch) \
264 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kRef) \
265 ADD_BENCH(n, layout, ImageMode::kNone, DrawMode::kQuad)
266
267ADD_BENCH_FAMILY(1000, RectangleLayout::kRandom)
268ADD_BENCH_FAMILY(1000, RectangleLayout::kGrid)
269
270#undef ADD_BENCH_FAMILY
271#undef ADD_BENCH