1 /* 2 * Copyright 2016 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 <tuple> 9 10 #include "Benchmark.h" 11 #include "Resources.h" 12 #include "SkCpu.h" 13 #include "SkImage.h" 14 #include "SkImage_Base.h" 15 #include "SkNx.h" 16 #include "SkOpts.h" 17 #include "SkPM4fPriv.h" 18 #include "SkString.h" 19 20 #define INNER_LOOPS 10 21 22 static inline void brute_srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) { 23 auto d = Sk4f_fromS32(*dst), 24 s = Sk4f_fromS32( src); 25 *dst = Sk4f_toS32(s + d * (1.0f - s[3])); 26 } 27 28 static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) { 29 if (src >= 0xFF000000) { 30 *dst = src; 31 return; 32 } 33 brute_srcover_srgb_srgb_1(dst, src); 34 } 35 36 static void brute_force_srcover_srgb_srgb( 37 uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) { 38 while (ndst > 0) { 39 int n = SkTMin(ndst, nsrc); 40 41 for (int i = 0; i < n; i++) { 42 brute_srcover_srgb_srgb_1(dst++, src[i]); 43 } 44 ndst -= n; 45 } 46 } 47 48 static void trivial_srcover_srgb_srgb( 49 uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) { 50 while (ndst > 0) { 51 int n = SkTMin(ndst, nsrc); 52 53 for (int i = 0; i < n; i++) { 54 srcover_srgb_srgb_1(dst++, src[i]); 55 } 56 ndst -= n; 57 } 58 } 59 60 static void best_non_simd_srcover_srgb_srgb( 61 uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) { 62 uint64_t* ddst = reinterpret_cast<uint64_t*>(dst); 63 64 auto srcover_srgb_srgb_2 = [](uint32_t* dst, const uint32_t* src) { 65 srcover_srgb_srgb_1(dst++, *src++); 66 srcover_srgb_srgb_1(dst, *src); 67 }; 68 69 while (ndst >0) { 70 int count = SkTMin(ndst, nsrc); 71 ndst -= count; 72 const uint64_t* dsrc = reinterpret_cast<const uint64_t*>(src); 73 const uint64_t* end = dsrc + (count >> 1); 74 do { 75 if ((~*dsrc & 0xFF000000FF000000) == 0) { 76 do { 77 *ddst++ = *dsrc++; 78 } while (dsrc < end && (~*dsrc & 0xFF000000FF000000) == 0); 79 } else if ((*dsrc & 0xFF000000FF000000) == 0) { 80 do { 81 dsrc++; 82 ddst++; 83 } while (dsrc < end && (*dsrc & 0xFF000000FF000000) == 0); 84 } else { 85 srcover_srgb_srgb_2(reinterpret_cast<uint32_t*>(ddst++), 86 reinterpret_cast<const uint32_t*>(dsrc++)); 87 } 88 } while (dsrc < end); 89 90 if ((count & 1) != 0) { 91 uint32_t s1; 92 memcpy(&s1, dsrc, 4); 93 srcover_srgb_srgb_1(reinterpret_cast<uint32_t*>(ddst), s1); 94 } 95 } 96 } 97 98 class SrcOverVSkOptsBruteForce { 99 public: 100 static SkString Name() { return SkString{"VSkOptsBruteForce"}; } 101 static void BlendN(uint32_t* dst, const uint32_t* src, int count) { 102 brute_force_srcover_srgb_srgb(dst, src, count, count); 103 } 104 }; 105 106 class SrcOverVSkOptsTrivial { 107 public: 108 static SkString Name() { return SkString{"VSkOptsTrivial"}; } 109 static void BlendN(uint32_t* dst, const uint32_t* src, int count) { 110 trivial_srcover_srgb_srgb(dst, src, count, count); 111 } 112 }; 113 114 class SrcOverVSkOptsNonSimdCore { 115 public: 116 static SkString Name() { return SkString{"VSkOptsNonSimdCore"}; } 117 static void BlendN(uint32_t* dst, const uint32_t* src, int count) { 118 best_non_simd_srcover_srgb_srgb(dst, src, count, count); 119 } 120 }; 121 122 class SrcOverVSkOptsDefault { 123 public: 124 static SkString Name() { return SkString{"VSkOptsDefault"}; } 125 static void BlendN(uint32_t* dst, const uint32_t* src, int count) { 126 SkOpts::srcover_srgb_srgb(dst, src, count, count); 127 } 128 }; 129 130 /////////////////////////////////////////////////////////////////////////////////////////////////// 131 132 template <typename Blender> 133 class LinearSrcOverBench : public Benchmark { 134 public: 135 LinearSrcOverBench(const char* fileName) : fFileName(fileName) { 136 fName = "LinearSrcOver_"; 137 fName.append(fileName); 138 fName.append(Blender::Name()); 139 } 140 141 protected: 142 bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; } 143 const char* onGetName() override { return fName.c_str(); } 144 145 void onPreDraw(SkCanvas*) override { 146 if (!fPixmap.addr()) { 147 sk_sp<SkImage> image = GetResourceAsImage(fFileName.c_str()); 148 SkBitmap bm; 149 SkColorSpace* legacyColorSpace = nullptr; 150 if (!as_IB(image)->getROPixels(&bm, legacyColorSpace)) { 151 SkFAIL("Could not read resource"); 152 } 153 bm.peekPixels(&fPixmap); 154 fCount = fPixmap.rowBytesAsPixels(); 155 fDst.reset(fCount); 156 sk_bzero(fDst.get(), fPixmap.rowBytes()); 157 } 158 } 159 160 void onDraw(int loops, SkCanvas*) override { 161 SkASSERT(fPixmap.colorType() == kN32_SkColorType); 162 163 const int width = fPixmap.rowBytesAsPixels(); 164 165 for (int i = 0; i < loops * INNER_LOOPS; ++i) { 166 const uint32_t* src = fPixmap.addr32(); 167 for (int y = 0; y < fPixmap.height(); y++) { 168 Blender::BlendN(fDst.get(), src, width); 169 src += width; 170 } 171 } 172 } 173 174 void onPostDraw(SkCanvas*) override { 175 // Make sure the compiler does not optimize away the operation. 176 volatile uint32_t v = 0; 177 for (int i = 0; i < fCount; i++) { 178 v ^= fDst[i]; 179 } 180 } 181 182 private: 183 int fCount; 184 SkAutoTArray<uint32_t> fDst; 185 SkString fFileName; 186 SkString fName; 187 SkPixmap fPixmap; 188 189 typedef Benchmark INHERITED; 190 }; 191 192 #define BENCHES(fileName) \ 193 DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsBruteForce>(fileName); ) \ 194 DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsTrivial>(fileName); ) \ 195 DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsNonSimdCore>(fileName); ) \ 196 DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsDefault>(fileName); ) 197 198 BENCHES("yellow_rose.png") 199 BENCHES("baby_tux.png") 200 BENCHES("plane.png") 201 BENCHES("mandrill_512.png") 202 BENCHES("iconstrip.png") 203