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