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