blob: 2509d61466f581e6b12cece55fbc509ad70bf472 [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"
Robert Phillipsf0288102020-07-06 13:45:34 -040012#include "include/gpu/GrDirectContext.h"
Michael Ludwig1beb8ae2019-10-28 13:22:11 -040013#include "include/utils/SkRandom.h"
14
Michael Ludwig1beb8ae2019-10-28 13:22:11 -040015#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:
19enum 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
25enum 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
31enum 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.
37template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode>
38class BulkRectBench : public Benchmark {
39public:
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
59protected:
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 Phillipsf0288102020-07-06 13:45:34 -0400132 auto context = canvas->recordingContext();
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400133 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 Osman449b1152020-04-15 16:43:00 -0400149 SkSimpleMatrixProvider matrixProvider(view);
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400150 GrPaint grPaint;
Brian Osman449b1152020-04-15 16:43:00 -0400151 SkPaintToGrPaint(context, rtc->colorInfo(), paint, matrixProvider, &grPaint);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400152 rtc->drawQuadSet(nullptr, std::move(grPaint), GrAA::kYes, view, batch, kRectCount);
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400153 }
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 Holler4caa9352020-07-16 10:58:58 -0400218 auto direct = GrAsDirectContext(canvas->recordingContext());
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400219 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 Holler4caa9352020-07-16 10:58:58 -0400225 fImages[i] = direct ? image->makeTextureImage(direct) : std::move(image);
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400226 }
227 }
228
Greg Daniel5ed3c112020-06-18 15:59:17 -0400229 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 Ludwig1beb8ae2019-10-28 13:22:11 -0400238 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 Stiles7571f9e2020-09-02 22:42:33 -0400260 using INHERITED = Benchmark;
Michael Ludwig1beb8ae2019-10-28 13:22:11 -0400261};
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
276ADD_BENCH_FAMILY(1000, RectangleLayout::kRandom)
277ADD_BENCH_FAMILY(1000, RectangleLayout::kGrid)
278
279#undef ADD_BENCH_FAMILY
280#undef ADD_BENCH