• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/GrDirectContext.h"
14 #include "src/base/SkRandom.h"
15 #include "src/core/SkCanvasPriv.h"
16 #include "src/gpu/ganesh/GrOpsTypes.h"
17 #include "src/gpu/ganesh/SkGr.h"
18 #include "src/gpu/ganesh/SurfaceDrawContext.h"
19 
20 // Benchmarks that exercise the bulk image and solid color quad APIs, under a variety of patterns:
21 enum 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
27 enum 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
33 enum 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.
39 template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode>
40 class BulkRectBench : public Benchmark {
41 public:
42     static_assert(kImageMode == ImageMode::kNone || kDrawMode != DrawMode::kQuad,
43                   "kQuad only supported for solid color draws");
44 
45     inline static constexpr int kWidth      = 1024;
46     inline static constexpr int kHeight     = 1024;
47 
48     // There will either be 0 images, 1 image, or 1 image per rect
49     inline static constexpr int kImageCount = kImageMode == ImageMode::kShared ?
50             1 : (kImageMode == ImageMode::kNone ? 0 : kRectCount);
51 
isSuitableFor(Backend backend)52     bool isSuitableFor(Backend backend) override {
53         if (kDrawMode == DrawMode::kBatch && kImageMode == ImageMode::kNone) {
54             // Currently the bulk color quad API is only available on skgpu::v1::SurfaceDrawContext
55             return backend == kGPU_Backend;
56         } else {
57             return this->INHERITED::isSuitableFor(backend);
58         }
59     }
60 
61 protected:
62     SkRect         fRects[kRectCount];
63     sk_sp<SkImage> fImages[kImageCount];
64     SkColor4f      fColors[kRectCount];
65     SkString       fName;
66 
computeName()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 
drawImagesBatch(SkCanvas * canvas) const91     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);
107 
108         canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr,
109                                                 SkSamplingOptions(SkFilterMode::kLinear), &paint,
110                                                 SkCanvas::kFast_SrcRectConstraint);
111     }
112 
drawImagesRef(SkCanvas * canvas) const113     void drawImagesRef(SkCanvas* canvas) const {
114         SkASSERT(kImageMode != ImageMode::kNone);
115         SkASSERT(kDrawMode == DrawMode::kRef);
116 
117         SkPaint paint;
118         paint.setAntiAlias(true);
119 
120         for (int i = 0; i < kRectCount; ++i) {
121             int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
122             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,
126                                   SkCanvas::kFast_SrcRectConstraint);
127         }
128     }
129 
drawSolidColorsBatch(SkCanvas * canvas) const130     void drawSolidColorsBatch(SkCanvas* canvas) const {
131         SkASSERT(kImageMode == ImageMode::kNone);
132         SkASSERT(kDrawMode == DrawMode::kBatch);
133 
134         auto context = canvas->recordingContext();
135         SkASSERT(context);
136 
137         GrQuadSetEntry batch[kRectCount];
138         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 
149         auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
150         SkMatrix view = canvas->getLocalToDeviceAs3x3();
151         SkSurfaceProps props;
152         GrPaint grPaint;
153         SkPaintToGrPaint(context, sdc->colorInfo(), paint, view, props, &grPaint);
154         sdc->drawQuadSet(nullptr, std::move(grPaint), view, batch, kRectCount);
155     }
156 
drawSolidColorsRef(SkCanvas * canvas) const157     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 
onGetName()174     const char* onGetName() override {
175         if (fName.isEmpty()) {
176             this->computeName();
177         }
178         return fName.c_str();
179     }
180 
onDelayedSetup()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 
onPerCanvasPreDraw(SkCanvas * canvas)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
220         auto direct = GrAsDirectContext(canvas->recordingContext());
221         for (int i = 0; i < kImageCount; ++i) {
222             SkBitmap bm;
223             bm.allocN32Pixels(256, 256);
224             bm.eraseColor(fColors[i].toSkColor());
225             auto image = bm.asImage();
226 
227             fImages[i] = direct ? image->makeTextureImage(direct) : std::move(image);
228         }
229     }
230 
onPerCanvasPostDraw(SkCanvas * canvas)231     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 
onDraw(int loops,SkCanvas * canvas)240     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 
onGetSize()258     SkIPoint onGetSize() override {
259         return { kWidth, kHeight };
260     }
261 
262     using INHERITED = Benchmark;
263 };
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 
278 ADD_BENCH_FAMILY(1000,  RectangleLayout::kRandom)
279 ADD_BENCH_FAMILY(1000,  RectangleLayout::kGrid)
280 
281 #undef ADD_BENCH_FAMILY
282 #undef ADD_BENCH
283