blob: a472d2489f19cabfba6cda2481d3cc46ed7d47d6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.comf523e252009-01-26 23:15:37 +00008#include "SkBenchmark.h"
9#include "SkBitmap.h"
10#include "SkPaint.h"
11#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkRandom.h"
14#include "SkString.h"
15
reed@android.comf523e252009-01-26 23:15:37 +000016static const char* gConfigName[] = {
17 "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
18};
19
reed@android.comf523e252009-01-26 23:15:37 +000020static int conv6ToByte(int x) {
21 return x * 0xFF / 5;
22}
23
24static int convByteTo6(int x) {
25 return x * 5 / 255;
26}
27
28static uint8_t compute666Index(SkPMColor c) {
29 int r = SkGetPackedR32(c);
30 int g = SkGetPackedG32(c);
31 int b = SkGetPackedB32(c);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000032
reed@android.comf523e252009-01-26 23:15:37 +000033 return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
34}
35
36static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) {
37 SkColorTable* ctable = new SkColorTable(216);
38 SkPMColor* colors = ctable->lockColors();
39 // rrr ggg bbb
40 for (int r = 0; r < 6; r++) {
41 int rr = conv6ToByte(r);
42 for (int g = 0; g < 6; g++) {
43 int gg = conv6ToByte(g);
44 for (int b = 0; b < 6; b++) {
45 int bb = conv6ToByte(b);
46 *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb);
47 }
48 }
49 }
50 ctable->unlockColors(true);
51 dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
52 dst->allocPixels(ctable);
53 ctable->unref();
rmistry@google.comfbfcd562012-08-23 18:09:54 +000054
reed@android.comf523e252009-01-26 23:15:37 +000055 SkAutoLockPixels alps(src);
56 SkAutoLockPixels alpd(*dst);
57
58 for (int y = 0; y < src.height(); y++) {
59 const SkPMColor* srcP = src.getAddr32(0, y);
60 uint8_t* dstP = dst->getAddr8(0, y);
61 for (int x = src.width() - 1; x >= 0; --x) {
62 *dstP++ = compute666Index(*srcP++);
63 }
64 }
65}
66
67/* Variants for bitmaps
rmistry@google.comfbfcd562012-08-23 18:09:54 +000068
reed@android.comf523e252009-01-26 23:15:37 +000069 - src depth (32 w+w/o alpha), 565, 4444, index, a8
70 - paint options: filtering, dither, alpha
71 - matrix options: translate, scale, rotate, persp
72 - tiling: none, repeat, mirror, clamp
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073
reed@android.comf523e252009-01-26 23:15:37 +000074 */
75
76class BitmapBench : public SkBenchmark {
77 SkBitmap fBitmap;
78 SkPaint fPaint;
reed@android.com11ec1862009-10-19 19:01:45 +000079 bool fIsOpaque;
junov@google.com4ee7ae52011-06-30 17:30:49 +000080 bool fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache
djsollen@google.comc2532dd2013-04-09 18:06:06 +000081 bool fIsVolatile;
82 SkBitmap::Config fConfig;
reed@android.comf523e252009-01-26 23:15:37 +000083 SkString fName;
reed@google.comef77ec22013-05-29 15:39:54 +000084 enum { BICUBIC_DUR_SCALE = 20 };
85 enum { N = SkBENCHLOOP(15 * BICUBIC_DUR_SCALE) };
djsollen@google.comc2532dd2013-04-09 18:06:06 +000086 enum { W = 128 };
87 enum { H = 128 };
reed@android.comf523e252009-01-26 23:15:37 +000088public:
reed@android.com11ec1862009-10-19 19:01:45 +000089 BitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +000090 bool forceUpdate = false, bool bitmapVolatile = false)
djsollen@google.comc2532dd2013-04-09 18:06:06 +000091 : INHERITED(param)
92 , fIsOpaque(isOpaque)
93 , fForceUpdate(forceUpdate)
djsollen@google.comc2532dd2013-04-09 18:06:06 +000094 , fIsVolatile(bitmapVolatile)
95 , fConfig(c) {
reed@android.comf523e252009-01-26 23:15:37 +000096 }
97
98protected:
99 virtual const char* onGetName() {
100 fName.set("bitmap");
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000101 fName.appendf("_%s%s", gConfigName[fConfig],
reed@android.com11ec1862009-10-19 19:01:45 +0000102 fIsOpaque ? "" : "_A");
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000103 if (fForceUpdate)
junov@google.com4ee7ae52011-06-30 17:30:49 +0000104 fName.append("_update");
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000105 if (fIsVolatile)
junov@google.com4ee7ae52011-06-30 17:30:49 +0000106 fName.append("_volatile");
107
reed@android.comf523e252009-01-26 23:15:37 +0000108 return fName.c_str();
109 }
110
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000111 virtual void onPreDraw() {
112 SkBitmap bm;
113
114 if (SkBitmap::kIndex8_Config == fConfig) {
115 bm.setConfig(SkBitmap::kARGB_8888_Config, W, H);
116 } else {
117 bm.setConfig(fConfig, W, H);
118 }
119
120 bm.allocPixels();
121 bm.eraseColor(fIsOpaque ? SK_ColorBLACK : 0);
122
123 onDrawIntoBitmap(bm);
124
125 if (SkBitmap::kIndex8_Config == fConfig) {
126 convertToIndex666(bm, &fBitmap);
127 } else {
128 fBitmap = bm;
129 }
130
131 if (fBitmap.getColorTable()) {
132 fBitmap.getColorTable()->setIsOpaque(fIsOpaque);
133 }
134 fBitmap.setIsOpaque(fIsOpaque);
135 fBitmap.setIsVolatile(fIsVolatile);
136 }
137
reed@android.comf523e252009-01-26 23:15:37 +0000138 virtual void onDraw(SkCanvas* canvas) {
139 SkIPoint dim = this->getSize();
140 SkRandom rand;
141
142 SkPaint paint(fPaint);
143 this->setupPaint(&paint);
144
145 const SkBitmap& bitmap = fBitmap;
146 const SkScalar x0 = SkIntToScalar(-bitmap.width() / 2);
147 const SkScalar y0 = SkIntToScalar(-bitmap.height() / 2);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000148
reed@google.comef77ec22013-05-29 15:39:54 +0000149 int count = N;
150#ifdef SK_RELEASE
151 // in DEBUG, N is always 1
reed@google.com9cfc83c2013-07-22 17:18:18 +0000152 if (SkPaint::kHigh_FilterLevel == paint.getFilterLevel()) {
reed@google.comef77ec22013-05-29 15:39:54 +0000153 count /= BICUBIC_DUR_SCALE;
154 }
155#endif
156 for (int i = 0; i < count; i++) {
reed@android.comf523e252009-01-26 23:15:37 +0000157 SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
158 SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
junov@google.com4ee7ae52011-06-30 17:30:49 +0000159
160 if (fForceUpdate)
161 bitmap.notifyPixelsChanged();
162
reed@android.comf523e252009-01-26 23:15:37 +0000163 canvas->drawBitmap(bitmap, x, y, &paint);
164 }
165 }
166
reed@google.comef77ec22013-05-29 15:39:54 +0000167 virtual float onGetDurationScale() SK_OVERRIDE {
168 SkPaint paint;
169 this->setupPaint(&paint);
170#ifdef SK_DEBUG
171 return 1;
172#else
reed@google.com9cfc83c2013-07-22 17:18:18 +0000173 return SkPaint::kHigh_FilterLevel == paint.getFilterLevel() ?
reed@google.coma2be2cc2013-05-29 16:19:48 +0000174 (float)BICUBIC_DUR_SCALE : 1;
reed@google.comef77ec22013-05-29 15:39:54 +0000175#endif
176 }
177
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000178 virtual void onDrawIntoBitmap(const SkBitmap& bm) {
179 const int w = bm.width();
180 const int h = bm.height();
181
182 SkCanvas canvas(bm);
183 SkPaint p;
184 p.setAntiAlias(true);
185 p.setColor(SK_ColorRED);
186 canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
187 SkIntToScalar(SkMin32(w, h))*3/8, p);
188
189 SkRect r;
190 r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
191 p.setStyle(SkPaint::kStroke_Style);
192 p.setStrokeWidth(SkIntToScalar(4));
193 p.setColor(SK_ColorBLUE);
194 canvas.drawRect(r, p);
195 }
196
reed@android.comf523e252009-01-26 23:15:37 +0000197private:
198 typedef SkBenchmark INHERITED;
199};
200
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000201/** Explicitly invoke some filter types to improve coverage of acceleration
202 procs. */
203
reed@google.comef77ec22013-05-29 15:39:54 +0000204enum Flags {
205 kScale_Flag = 1 << 0,
206 kRotate_Flag = 1 << 1,
207 kBilerp_Flag = 1 << 2,
208 kBicubic_Flag = 1 << 3,
209};
210
211static bool isBilerp(uint32_t flags) {
212 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag);
213}
214
215static bool isBicubic(uint32_t flags) {
216 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag | kBicubic_Flag);
217}
218
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000219class FilterBitmapBench : public BitmapBench {
reed@google.comef77ec22013-05-29 15:39:54 +0000220 uint32_t fFlags;
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000221 SkString fFullName;
222 enum { N = SkBENCHLOOP(300) };
223public:
224 FilterBitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +0000225 bool forceUpdate, bool isVolitile, uint32_t flags)
226 : INHERITED(param, isOpaque, c, forceUpdate, isVolitile)
227 , fFlags(flags) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000228 }
229
230protected:
231 virtual const char* onGetName() {
232 fFullName.set(INHERITED::onGetName());
reed@google.comef77ec22013-05-29 15:39:54 +0000233 if (fFlags & kScale_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000234 fFullName.append("_scale");
reed@google.comef77ec22013-05-29 15:39:54 +0000235 }
236 if (fFlags & kRotate_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000237 fFullName.append("_rotate");
reed@google.comef77ec22013-05-29 15:39:54 +0000238 }
239 if (isBilerp(fFlags)) {
240 fFullName.append("_bilerp");
241 } else if (isBicubic(fFlags)) {
242 fFullName.append("_bicubic");
243 }
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000244
245 return fFullName.c_str();
246 }
247
248 virtual void onDraw(SkCanvas* canvas) {
249 SkISize dim = canvas->getDeviceSize();
reed@google.comef77ec22013-05-29 15:39:54 +0000250 if (fFlags & kScale_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000251 const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
252 const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000253
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000254 canvas->translate(x, y);
255 // just enough so we can't take the sprite case
256 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
257 canvas->translate(-x, -y);
258 }
reed@google.comef77ec22013-05-29 15:39:54 +0000259 if (fFlags & kRotate_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000260 const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
261 const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000262
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000263 canvas->translate(x, y);
264 canvas->rotate(SkIntToScalar(35));
265 canvas->translate(-x, -y);
266 }
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000267 INHERITED::onDraw(canvas);
268 }
269
reed@google.com9cfc83c2013-07-22 17:18:18 +0000270 virtual void setupPaint(SkPaint* paint) SK_OVERRIDE {
271 this->INHERITED::setupPaint(paint);
272
273 int index = 0;
274 if (fFlags & kBilerp_Flag) {
275 index |= 1;
276 }
277 if (fFlags & kBicubic_Flag) {
278 index |= 2;
279 }
280 static const SkPaint::FilterLevel gLevels[] = {
281 SkPaint::kNone_FilterLevel,
282 SkPaint::kLow_FilterLevel,
283 SkPaint::kMedium_FilterLevel,
284 SkPaint::kHigh_FilterLevel
285 };
286 paint->setFilterLevel(gLevels[index]);
287}
288
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000289private:
290 typedef BitmapBench INHERITED;
291};
292
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000293/** Verify optimizations that test source alpha values. */
294
295class SourceAlphaBitmapBench : public BitmapBench {
296public:
297 enum SourceAlpha { kOpaque_SourceAlpha, kTransparent_SourceAlpha,
298 kTwoStripes_SourceAlpha, kThreeStripes_SourceAlpha};
299private:
300 SkString fFullName;
301 SourceAlpha fSourceAlpha;
302public:
303 SourceAlphaBitmapBench(void* param, SourceAlpha alpha, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +0000304 bool forceUpdate = false, bool bitmapVolatile = false)
305 : INHERITED(param, false, c, forceUpdate, bitmapVolatile)
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000306 , fSourceAlpha(alpha) {
307 }
308
309protected:
310 virtual const char* onGetName() {
311 fFullName.set(INHERITED::onGetName());
312
313 if (fSourceAlpha == kOpaque_SourceAlpha) {
314 fFullName.append("_source_opaque");
315 } else if (fSourceAlpha == kTransparent_SourceAlpha) {
316 fFullName.append("_source_transparent");
317 } else if (fSourceAlpha == kTwoStripes_SourceAlpha) {
318 fFullName.append("_source_stripes_two");
319 } else if (fSourceAlpha == kThreeStripes_SourceAlpha) {
320 fFullName.append("_source_stripes_three");
321 }
322
323 return fFullName.c_str();
324 }
325
326 virtual void onDrawIntoBitmap(const SkBitmap& bm) SK_OVERRIDE {
327 const int w = bm.width();
328 const int h = bm.height();
329
330 if (kOpaque_SourceAlpha == fSourceAlpha) {
331 bm.eraseColor(SK_ColorBLACK);
332 } else if (kTransparent_SourceAlpha == fSourceAlpha) {
333 bm.eraseColor(0);
334 } else if (kTwoStripes_SourceAlpha == fSourceAlpha) {
335 bm.eraseColor(0);
336
337 SkCanvas canvas(bm);
338 SkPaint p;
339 p.setAntiAlias(false);
340 p.setStyle(SkPaint::kFill_Style);
341 p.setColor(SK_ColorRED);
342
343 // Draw red vertical stripes on transparent background
344 SkRect r;
345 for (int x = 0; x < w; x+=2)
346 {
347 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
348 canvas.drawRect(r, p);
349 }
350
351 } else if (kThreeStripes_SourceAlpha == fSourceAlpha) {
352 bm.eraseColor(0);
353
354 SkCanvas canvas(bm);
355 SkPaint p;
356 p.setAntiAlias(false);
357 p.setStyle(SkPaint::kFill_Style);
358
359 // Draw vertical stripes on transparent background with a pattern
360 // where the first pixel is fully transparent, the next is semi-transparent
361 // and the third is fully opaque.
362 SkRect r;
363 for (int x = 0; x < w; x++)
364 {
365 if (x % 3 == 0) {
366 continue; // Keep transparent
367 } else if (x % 3 == 1) {
368 p.setColor(SkColorSetARGB(127, 127, 127, 127)); // Semi-transparent
369 } else if (x % 3 == 2) {
370 p.setColor(SK_ColorRED); // Opaque
371 }
372 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
373 canvas.drawRect(r, p);
374 }
375 }
376 }
377
378private:
379 typedef BitmapBench INHERITED;
380};
reed@google.com2ece2872013-05-28 20:48:14 +0000381
382DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kARGB_8888_Config); )
383DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config); )
384DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kRGB_565_Config); )
reed@google.com2ece2872013-05-28 20:48:14 +0000385DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kIndex8_Config); )
386DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kIndex8_Config); )
387DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); )
388DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); )
reed@android.comf523e252009-01-26 23:15:37 +0000389
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000390// scale filter -> S32_opaque_D32_filter_DX_{SSE2,SSSE3} and Fact9 is also for S32_D16_filter_DX_SSE2
reed@google.comef77ec22013-05-29 15:39:54 +0000391DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); )
392DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); )
393DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kBilerp_Flag); )
394DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kBilerp_Flag); )
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000395
396// scale rotate filter -> S32_opaque_D32_filter_DXDY_{SSE2,SSSE3}
reed@google.comef77ec22013-05-29 15:39:54 +0000397DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
398DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
399DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
400DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
401
402DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag | kBicubic_Flag); )
403DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag | kBicubic_Flag); )
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000404
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000405// source alpha tests -> S32A_Opaque_BlitRow32_{arm,neon}
reed@google.com2ece2872013-05-28 20:48:14 +0000406DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kOpaque_SourceAlpha, SkBitmap::kARGB_8888_Config); )
407DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTransparent_SourceAlpha, SkBitmap::kARGB_8888_Config); )
408DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTwoStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); )
409DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kThreeStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); )