1 /*
2  * Copyright 2014 Google Inc.
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/effects/SkImageFilters.h"
12 #include "include/gpu/GrDirectContext.h"
13 #include "include/gpu/GrRecordingContext.h"
14 #include "tools/Resources.h"
15 
16 // Exercise a blur filter connected to 5 inputs of the same merge filter.
17 // This bench shows an improvement in performance once cacheing of re-used
18 // nodes is implemented, since the DAG is no longer flattened to a tree.
19 class ImageFilterDAGBench : public Benchmark {
20 public:
ImageFilterDAGBench()21     ImageFilterDAGBench() {}
22 
23 protected:
onGetName()24     const char* onGetName() override {
25         return "image_filter_dag";
26     }
27 
onDraw(int loops,SkCanvas * canvas)28     void onDraw(int loops, SkCanvas* canvas) override {
29         const SkRect rect = SkRect::Make(SkIRect::MakeWH(400, 400));
30 
31         // Set up the filters once, we're not interested in measuring allocation time here
32         sk_sp<SkImageFilter> blur(SkImageFilters::Blur(20.0f, 20.0f, nullptr));
33         sk_sp<SkImageFilter> inputs[kNumInputs];
34         for (int i = 0; i < kNumInputs; ++i) {
35             inputs[i] = blur;
36         }
37         SkPaint paint;
38         paint.setImageFilter(SkImageFilters::Merge(inputs, kNumInputs));
39 
40         // Only measure the filter computations done in drawRect()
41         // TODO (michaelludwig) - This benchmark, and the ones defined below, allocate their filters
42         // outside of the loop. This means that repeatedly drawing with the same filter will hit
43         // the global image filter cache inside the loop. Raster backend uses this cache so will see
44         // artificially improved performance. Ganesh will not because it uses a cache per filter
45         // call, so only within-DAG cache hits are measured (as desired). skbug:9297 wants to move
46         // raster backend to the same pattern, which will make the benchmark executions fair again.
47         for (int j = 0; j < loops; j++) {
48             canvas->drawRect(rect, paint);
49         }
50     }
51 
52 private:
53     static const int kNumInputs = 5;
54 
55     using INHERITED = Benchmark;
56 };
57 
58 class ImageMakeWithFilterDAGBench : public Benchmark {
59 public:
ImageMakeWithFilterDAGBench()60     ImageMakeWithFilterDAGBench() {}
61 
62 protected:
onGetName()63     const char* onGetName() override {
64         return "image_make_with_filter_dag";
65     }
66 
onDelayedSetup()67     void onDelayedSetup() override {
68         fImage = GetResourceAsImage("images/mandrill_512.png");
69     }
70 
onDraw(int loops,SkCanvas * canvas)71     void onDraw(int loops, SkCanvas* canvas) override {
72         SkIRect subset = SkIRect::MakeSize(fImage->dimensions());
73         SkIPoint offset = SkIPoint::Make(0, 0);
74         SkIRect discardSubset;
75 
76         auto dContext = GrAsDirectContext(canvas->recordingContext());
77         // makeWithFilter will only use the GPU backend if the image is already a texture
78         sk_sp<SkImage> image = fImage->makeTextureImage(dContext);
79         if (!image) {
80             image = fImage;
81         }
82 
83         // Set up the filters once so the allocation cost isn't included per-loop
84         sk_sp<SkImageFilter> blur(SkImageFilters::Blur(20.0f, 20.0f, nullptr));
85         sk_sp<SkImageFilter> inputs[kNumInputs];
86         for (int i = 0; i < kNumInputs; ++i) {
87             inputs[i] = blur;
88         }
89         sk_sp<SkImageFilter> mergeFilter = SkImageFilters::Merge(inputs, kNumInputs);
90 
91         // But measure makeWithFilter() per loop since that's the focus of this benchmark
92         for (int j = 0; j < loops; j++) {
93             image = image->makeWithFilter(dContext, mergeFilter.get(), subset, subset,
94                                           &discardSubset, &offset);
95             SkASSERT(image && image->dimensions() == fImage->dimensions());
96         }
97     }
98 
99 private:
100     static const int kNumInputs = 5;
101     sk_sp<SkImage> fImage;
102 
103     using INHERITED = Benchmark;
104 };
105 
106 // Exercise a blur filter connected to both inputs of an SkDisplacementMapEffect.
107 
108 class ImageFilterDisplacedBlur : public Benchmark {
109 public:
ImageFilterDisplacedBlur()110     ImageFilterDisplacedBlur() {}
111 
112 protected:
onGetName()113     const char* onGetName() override {
114         return "image_filter_displaced_blur";
115     }
116 
onDraw(int loops,SkCanvas * canvas)117     void onDraw(int loops, SkCanvas* canvas) override {
118         // Setup filter once
119         sk_sp<SkImageFilter> blur(SkImageFilters::Blur(4.0f, 4.0f, nullptr));
120         SkScalar scale = 2;
121 
122         SkPaint paint;
123         paint.setImageFilter(SkImageFilters::DisplacementMap(SkColorChannel::kR, SkColorChannel::kR,
124                                                              scale, blur, blur));
125 
126         SkRect rect = SkRect::Make(SkIRect::MakeWH(400, 400));
127 
128         // As before, measure just the filter computation time inside the loops
129         for (int j = 0; j < loops; j++) {
130             canvas->drawRect(rect, paint);
131         }
132     }
133 
134 private:
135     using INHERITED = Benchmark;
136 };
137 
138 // Exercise an Xfermode kSrcIn filter compositing two inputs which have a small intersection.
139 class ImageFilterXfermodeIn : public Benchmark {
140 public:
ImageFilterXfermodeIn()141     ImageFilterXfermodeIn() {}
142 
143 protected:
onGetName()144     const char* onGetName() override { return "image_filter_xfermode_in"; }
145 
onDraw(int loops,SkCanvas * canvas)146     void onDraw(int loops, SkCanvas* canvas) override {
147         // Allocate filters once to avoid measuring instantiation time
148         auto blur = SkImageFilters::Blur(20.0f, 20.0f, nullptr);
149         auto offset1 = SkImageFilters::Offset(100.0f, 100.0f, blur);
150         auto offset2 = SkImageFilters::Offset(-100.0f, -100.0f, blur);
151         auto xfermode =
152                 SkImageFilters::Blend(SkBlendMode::kSrcIn, offset1, offset2, nullptr);
153 
154         SkPaint paint;
155         paint.setImageFilter(xfermode);
156 
157         // Measure only the filter time
158         for (int j = 0; j < loops; j++) {
159             canvas->drawRect(SkRect::MakeWH(200.0f, 200.0f), paint);
160         }
161     }
162 
163 private:
164     using INHERITED = Benchmark;
165 };
166 
167 DEF_BENCH(return new ImageFilterDAGBench;)
168 DEF_BENCH(return new ImageMakeWithFilterDAGBench;)
169 DEF_BENCH(return new ImageFilterDisplacedBlur;)
170 DEF_BENCH(return new ImageFilterXfermodeIn;)
171