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