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