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