• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
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  */
8 
9 
10 #include "BenchTimer.h"
11 
12 #include "GrContext.h"
13 #include "GrRenderTarget.h"
14 
15 #include "SkBenchmark.h"
16 #include "SkCanvas.h"
17 #include "SkColorPriv.h"
18 #include "SkGpuDevice.h"
19 #include "SkGraphics.h"
20 #include "SkImageEncoder.h"
21 #include "gl/SkNativeGLContext.h"
22 #include "gl/SkNullGLContext.h"
23 #include "SkNWayCanvas.h"
24 #include "SkPicture.h"
25 #include "SkString.h"
26 
27 #ifdef SK_BUILD_FOR_ANDROID
log_error(const char msg[])28 static void log_error(const char msg[]) { SkDebugf("%s", msg); }
log_progress(const char msg[])29 static void log_progress(const char msg[]) { SkDebugf("%s", msg); }
30 #else
log_error(const char msg[])31 static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); }
log_progress(const char msg[])32 static void log_progress(const char msg[]) { printf("%s", msg); }
33 #endif
34 
log_error(const SkString & str)35 static void log_error(const SkString& str) { log_error(str.c_str()); }
log_progress(const SkString & str)36 static void log_progress(const SkString& str) { log_progress(str.c_str()); }
37 
38 ///////////////////////////////////////////////////////////////////////////////
39 
erase(SkBitmap & bm)40 static void erase(SkBitmap& bm) {
41     if (bm.config() == SkBitmap::kA8_Config) {
42         bm.eraseColor(0);
43     } else {
44         bm.eraseColor(SK_ColorWHITE);
45     }
46 }
47 
48 #if 0
49 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
50     if (bm1.width() != bm2.width() ||
51         bm1.height() != bm2.height() ||
52         bm1.config() != bm2.config()) {
53         return false;
54     }
55 
56     size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
57     for (int y = 0; y < bm1.height(); y++) {
58         if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
59             return false;
60         }
61     }
62     return true;
63 }
64 #endif
65 
66 class Iter {
67 public:
Iter(void * param)68     Iter(void* param) {
69         fBench = BenchRegistry::Head();
70         fParam = param;
71     }
72 
next()73     SkBenchmark* next() {
74         if (fBench) {
75             BenchRegistry::Factory f = fBench->factory();
76             fBench = fBench->next();
77             return f(fParam);
78         }
79         return NULL;
80     }
81 
82 private:
83     const BenchRegistry* fBench;
84     void* fParam;
85 };
86 
make_filename(const char name[],SkString * path)87 static void make_filename(const char name[], SkString* path) {
88     path->set(name);
89     for (int i = 0; name[i]; i++) {
90         switch (name[i]) {
91             case '/':
92             case '\\':
93             case ' ':
94             case ':':
95                 path->writable_str()[i] = '-';
96                 break;
97             default:
98                 break;
99         }
100     }
101 }
102 
saveFile(const char name[],const char config[],const char dir[],const SkBitmap & bm)103 static void saveFile(const char name[], const char config[], const char dir[],
104                      const SkBitmap& bm) {
105     SkBitmap copy;
106     if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
107         return;
108     }
109 
110     if (bm.config() == SkBitmap::kA8_Config) {
111         // turn alpha into gray-scale
112         size_t size = copy.getSize() >> 2;
113         SkPMColor* p = copy.getAddr32(0, 0);
114         for (size_t i = 0; i < size; i++) {
115             int c = (*p >> SK_A32_SHIFT) & 0xFF;
116             c = 255 - c;
117             c |= (c << 24) | (c << 16) | (c << 8);
118             *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
119         }
120     }
121 
122     SkString str;
123     make_filename(name, &str);
124     str.appendf("_%s.png", config);
125     str.prepend(dir);
126     ::remove(str.c_str());
127     SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
128                                100);
129 }
130 
performClip(SkCanvas * canvas,int w,int h)131 static void performClip(SkCanvas* canvas, int w, int h) {
132     SkRect r;
133 
134     r.set(SkIntToScalar(10), SkIntToScalar(10),
135           SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
136     canvas->clipRect(r, SkRegion::kIntersect_Op);
137 
138     r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
139           SkIntToScalar(w-10), SkIntToScalar(h-10));
140     canvas->clipRect(r, SkRegion::kXOR_Op);
141 }
142 
performRotate(SkCanvas * canvas,int w,int h)143 static void performRotate(SkCanvas* canvas, int w, int h) {
144     const SkScalar x = SkIntToScalar(w) / 2;
145     const SkScalar y = SkIntToScalar(h) / 2;
146 
147     canvas->translate(x, y);
148     canvas->rotate(SkIntToScalar(35));
149     canvas->translate(-x, -y);
150 }
151 
performScale(SkCanvas * canvas,int w,int h)152 static void performScale(SkCanvas* canvas, int w, int h) {
153     const SkScalar x = SkIntToScalar(w) / 2;
154     const SkScalar y = SkIntToScalar(h) / 2;
155 
156     canvas->translate(x, y);
157     // just enough so we can't take the sprite case
158     canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
159     canvas->translate(-x, -y);
160 }
161 
parse_bool_arg(char * const * argv,char * const * stop,bool * var)162 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
163     if (argv < stop) {
164         *var = atoi(*argv) != 0;
165         return true;
166     }
167     return false;
168 }
169 
170 enum Backend {
171     kRaster_Backend,
172     kGPU_Backend,
173     kPDF_Backend,
174 };
175 
176 class GLHelper {
177 public:
GLHelper()178     GLHelper() {
179     }
180 
init(SkGLContext * glCtx,int width,int height)181     bool init(SkGLContext* glCtx, int width, int height) {
182         GrContext* grCtx;
183         GrRenderTarget* rt;
184         if (glCtx->init(width, height)) {
185             GrPlatform3DContext ctx =
186                 reinterpret_cast<GrPlatform3DContext>(glCtx->gl());
187             grCtx = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
188             if (NULL != grCtx) {
189                 GrPlatformRenderTargetDesc desc;
190                 desc.fConfig = kSkia8888_PM_GrPixelConfig;
191                 desc.fWidth = width;
192                 desc.fHeight = height;
193                 desc.fStencilBits = 8;
194                 desc.fRenderTargetHandle = glCtx->getFBOID();
195                 rt = grCtx->createPlatformRenderTarget(desc);
196                 if (NULL == rt) {
197                     grCtx->unref();
198                     return false;
199                 }
200             }
201         } else {
202             return false;
203         }
204         glCtx->ref();
205         fGLContext.reset(glCtx);
206         fGrContext.reset(grCtx);
207         fRenderTarget.reset(rt);
208         return true;
209     }
210 
isValid()211     bool isValid() {
212         return NULL != fGLContext.get();
213     }
214 
glContext()215     SkGLContext* glContext() {
216         return fGLContext.get();
217     }
218 
renderTarget()219     GrRenderTarget* renderTarget() {
220         return fRenderTarget.get();
221     }
222 
grContext()223     GrContext* grContext() {
224         return fGrContext.get();
225     }
226 private:
227     SkAutoTUnref<SkGLContext> fGLContext;
228     SkAutoTUnref<GrContext> fGrContext;
229     SkAutoTUnref<GrRenderTarget> fRenderTarget;
230 };
231 
232 static GLHelper gRealGLHelper;
233 static GLHelper gNullGLHelper;
234 
make_device(SkBitmap::Config config,const SkIPoint & size,Backend backend,GLHelper * glHelper)235 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
236                              Backend backend, GLHelper* glHelper) {
237     SkDevice* device = NULL;
238     SkBitmap bitmap;
239     bitmap.setConfig(config, size.fX, size.fY);
240 
241     switch (backend) {
242         case kRaster_Backend:
243             bitmap.allocPixels();
244             erase(bitmap);
245             device = new SkDevice(bitmap);
246             break;
247         case kGPU_Backend:
248             device = new SkGpuDevice(glHelper->grContext(),
249                                      glHelper->renderTarget());
250             break;
251         case kPDF_Backend:
252         default:
253             SkASSERT(!"unsupported");
254     }
255     return device;
256 }
257 
258 static const struct {
259     SkBitmap::Config    fConfig;
260     const char*         fName;
261     Backend             fBackend;
262     GLHelper*           fGLHelper;
263 } gConfigs[] = {
264     { SkBitmap::kARGB_8888_Config,  "8888",     kRaster_Backend, NULL },
265     { SkBitmap::kRGB_565_Config,    "565",      kRaster_Backend, NULL },
266     { SkBitmap::kARGB_8888_Config,  "GPU",      kGPU_Backend, &gRealGLHelper },
267     { SkBitmap::kARGB_8888_Config,  "NULLGPU",  kGPU_Backend, &gNullGLHelper },
268 };
269 
findConfig(const char config[])270 static int findConfig(const char config[]) {
271     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
272         if (!strcmp(config, gConfigs[i].fName)) {
273             return i;
274         }
275     }
276     return -1;
277 }
278 
determine_gpu_context_size(SkTDict<const char * > & defineDict,int * contextWidth,int * contextHeight)279 static void determine_gpu_context_size(SkTDict<const char*>& defineDict,
280                                        int* contextWidth,
281                                        int* contextHeight) {
282     Iter iter(&defineDict);
283     SkBenchmark* bench;
284     while ((bench = iter.next()) != NULL) {
285         SkIPoint dim = bench->getSize();
286         if (*contextWidth < dim.fX) {
287             *contextWidth = dim.fX;
288         }
289         if (*contextHeight < dim.fY) {
290             *contextHeight = dim.fY;
291         }
292     }
293 }
294 
skip_name(const SkTDArray<const char * > array,const char name[])295 static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
296     if (0 == array.count()) {
297         // no names, so don't skip anything
298         return false;
299     }
300     for (int i = 0; i < array.count(); ++i) {
301         if (strstr(name, array[i])) {
302             // found the name, so don't skip
303             return false;
304         }
305     }
306     return true;
307 }
308 
main(int argc,char * const argv[])309 int main (int argc, char * const argv[]) {
310     SkAutoGraphics ag;
311 
312     SkTDict<const char*> defineDict(1024);
313     int repeatDraw = 1;
314     int forceAlpha = 0xFF;
315     bool forceAA = true;
316     bool forceFilter = false;
317     SkTriState::State forceDither = SkTriState::kDefault;
318     bool timerWall = false;
319     bool timerCpu = true;
320     bool timerGpu = true;
321     bool doScale = false;
322     bool doRotate = false;
323     bool doClip = false;
324     bool hasStrokeWidth = false;
325     float strokeWidth;
326     SkTDArray<const char*> fMatches;
327 
328     SkString outDir;
329     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
330     GLHelper* glHelper = NULL;
331     const char* configName = "";
332     Backend backend = kRaster_Backend;  // for warning
333     int configCount = SK_ARRAY_COUNT(gConfigs);
334 
335     char* const* stop = argv + argc;
336     for (++argv; argv < stop; ++argv) {
337         if (strcmp(*argv, "-o") == 0) {
338             argv++;
339             if (argv < stop && **argv) {
340                 outDir.set(*argv);
341                 if (outDir.c_str()[outDir.size() - 1] != '/') {
342                     outDir.append("/");
343                 }
344             }
345         } else if (strcmp(*argv, "-repeat") == 0) {
346             argv++;
347             if (argv < stop) {
348                 repeatDraw = atoi(*argv);
349                 if (repeatDraw < 1) {
350                     repeatDraw = 1;
351                 }
352             } else {
353                 log_error("missing arg for -repeat\n");
354                 return -1;
355             }
356         } else if (strcmp(*argv, "-timers") == 0) {
357             argv++;
358             if (argv < stop) {
359                 timerWall = false;
360                 timerCpu = false;
361                 timerGpu = false;
362                 for (char* t = *argv; *t; ++t) {
363                     switch (*t) {
364                     case 'w': timerWall = true; break;
365                     case 'c': timerCpu = true; break;
366                     case 'g': timerGpu = true; break;
367                     }
368                 }
369             } else {
370                 log_error("missing arg for -timers\n");
371                 return -1;
372             }
373         } else if (!strcmp(*argv, "-rotate")) {
374             doRotate = true;
375         } else if (!strcmp(*argv, "-scale")) {
376             doScale = true;
377         } else if (!strcmp(*argv, "-clip")) {
378             doClip = true;
379         } else if (strcmp(*argv, "-forceAA") == 0) {
380             if (!parse_bool_arg(++argv, stop, &forceAA)) {
381                 log_error("missing arg for -forceAA\n");
382                 return -1;
383             }
384         } else if (strcmp(*argv, "-forceFilter") == 0) {
385             if (!parse_bool_arg(++argv, stop, &forceFilter)) {
386                 log_error("missing arg for -forceFilter\n");
387                 return -1;
388             }
389         } else if (strcmp(*argv, "-forceDither") == 0) {
390             bool tmp;
391             if (!parse_bool_arg(++argv, stop, &tmp)) {
392                 log_error("missing arg for -forceDither\n");
393                 return -1;
394             }
395             forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
396         } else if (strcmp(*argv, "-forceBlend") == 0) {
397             bool wantAlpha = false;
398             if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
399                 log_error("missing arg for -forceBlend\n");
400                 return -1;
401             }
402             forceAlpha = wantAlpha ? 0x80 : 0xFF;
403         } else if (strcmp(*argv, "-strokeWidth") == 0) {
404             argv++;
405             if (argv < stop) {
406                 const char *strokeWidthStr = *argv;
407                 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
408                   log_error("bad arg for -strokeWidth\n");
409                   return -1;
410                 }
411                 hasStrokeWidth = true;
412             } else {
413                 log_error("missing arg for -strokeWidth\n");
414                 return -1;
415             }
416         } else if (strcmp(*argv, "-match") == 0) {
417             argv++;
418             if (argv < stop) {
419                 *fMatches.append() = *argv;
420             } else {
421                 log_error("missing arg for -match\n");
422                 return -1;
423             }
424         } else if (strcmp(*argv, "-config") == 0) {
425             argv++;
426             if (argv < stop) {
427                 int index = findConfig(*argv);
428                 if (index >= 0) {
429                     outConfig = gConfigs[index].fConfig;
430                     configName = gConfigs[index].fName;
431                     backend = gConfigs[index].fBackend;
432                     glHelper = gConfigs[index].fGLHelper;
433                     configCount = 1;
434                 } else {
435                     SkString str;
436                     str.printf("unrecognized config %s\n", *argv);
437                     log_error(str);
438                     return -1;
439                 }
440             } else {
441                 log_error("missing arg for -config\n");
442                 return -1;
443             }
444         } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
445             argv++;
446             if (argv < stop) {
447                 defineDict.set(argv[-1] + 2, *argv);
448             } else {
449                 log_error("incomplete '-Dfoo bar' definition\n");
450                 return -1;
451             }
452         } else {
453             SkString str;
454             str.printf("unrecognized arg %s\n", *argv);
455             log_error(str);
456             return -1;
457         }
458     }
459 
460     // report our current settings
461     {
462         SkString str;
463         str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d",
464                    forceAlpha, forceAA, forceFilter);
465         str.appendf(" rotate=%d scale=%d clip=%d",
466                    doRotate, doScale, doClip);
467 
468         const char * ditherName;
469         switch (forceDither) {
470             case SkTriState::kDefault: ditherName = "default"; break;
471             case SkTriState::kTrue: ditherName = "true"; break;
472             case SkTriState::kFalse: ditherName = "false"; break;
473             default: ditherName = "<invalid>"; break;
474         }
475         str.appendf(" dither=%s", ditherName);
476 
477         if (hasStrokeWidth) {
478             str.appendf(" strokeWidth=%f", strokeWidth);
479         } else {
480             str.append(" strokeWidth=none");
481         }
482 
483 #if defined(SK_SCALAR_IS_FLOAT)
484         str.append(" scalar=float");
485 #elif defined(SK_SCALAR_IS_FIXED)
486         str.append(" scalar=fixed");
487 #endif
488 
489 #if defined(SK_BUILD_FOR_WIN32)
490         str.append(" system=WIN32");
491 #elif defined(SK_BUILD_FOR_MAC)
492         str.append(" system=MAC");
493 #elif defined(SK_BUILD_FOR_ANDROID)
494         str.append(" system=ANDROID");
495 #elif defined(SK_BUILD_FOR_UNIX)
496         str.append(" system=UNIX");
497 #else
498         str.append(" system=other");
499 #endif
500 
501 #if defined(SK_DEBUG)
502         str.append(" DEBUG");
503 #endif
504         str.append("\n");
505         log_progress(str);
506     }
507 
508     //Don't do GL when fixed.
509 #if !defined(SK_SCALAR_IS_FIXED)
510     int contextWidth = 1024;
511     int contextHeight = 1024;
512     determine_gpu_context_size(defineDict, &contextWidth, &contextHeight);
513     SkAutoTUnref<SkGLContext> realGLCtx(new SkNativeGLContext);
514     SkAutoTUnref<SkGLContext> nullGLCtx(new SkNullGLContext);
515     gRealGLHelper.init(realGLCtx.get(), contextWidth, contextHeight);
516     gNullGLHelper.init(nullGLCtx.get(), contextWidth, contextHeight);
517 #endif
518     BenchTimer timer = BenchTimer(gRealGLHelper.glContext());
519 
520     Iter iter(&defineDict);
521     SkBenchmark* bench;
522     while ((bench = iter.next()) != NULL) {
523         SkIPoint dim = bench->getSize();
524         if (dim.fX <= 0 || dim.fY <= 0) {
525             continue;
526         }
527 
528         bench->setForceAlpha(forceAlpha);
529         bench->setForceAA(forceAA);
530         bench->setForceFilter(forceFilter);
531         bench->setDither(forceDither);
532         if (hasStrokeWidth) {
533             bench->setStrokeWidth(strokeWidth);
534         }
535 
536         // only run benchmarks if their name contains matchStr
537         if (skip_name(fMatches, bench->getName())) {
538             continue;
539         }
540 
541         {
542             SkString str;
543             str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
544                        bench->getName());
545             log_progress(str);
546         }
547 
548         for (int configIndex = 0; configIndex < configCount; configIndex++) {
549             if (configCount > 1) {
550                 outConfig = gConfigs[configIndex].fConfig;
551                 configName = gConfigs[configIndex].fName;
552                 backend = gConfigs[configIndex].fBackend;
553                 glHelper = gConfigs[configIndex].fGLHelper;
554             }
555 
556             if (kGPU_Backend == backend &&
557                 (NULL == glHelper || !glHelper->isValid())) {
558                 continue;
559             }
560 
561             SkDevice* device = make_device(outConfig, dim, backend, glHelper);
562             SkCanvas canvas(device);
563             device->unref();
564 
565             if (doClip) {
566                 performClip(&canvas, dim.fX, dim.fY);
567             }
568             if (doScale) {
569                 performScale(&canvas, dim.fX, dim.fY);
570             }
571             if (doRotate) {
572                 performRotate(&canvas, dim.fX, dim.fY);
573             }
574 
575             //warm up caches if needed
576             if (repeatDraw > 1) {
577                 SkAutoCanvasRestore acr(&canvas, true);
578                 bench->draw(&canvas);
579                 if (glHelper) {
580                     glHelper->grContext()->flush();
581                     SK_GL(*glHelper->glContext(), Finish());
582                 }
583             }
584 
585             timer.start();
586             for (int i = 0; i < repeatDraw; i++) {
587                 SkAutoCanvasRestore acr(&canvas, true);
588                 bench->draw(&canvas);
589                 if (glHelper) {
590                     glHelper->grContext()->flush();
591                 }
592             }
593            if (glHelper) {
594                 SK_GL(*glHelper->glContext(), Finish());
595            }
596            timer.end();
597 
598             if (repeatDraw > 1) {
599                 SkString str;
600                 str.printf("  %4s:", configName);
601                 if (timerWall) {
602                     str.appendf(" msecs = %6.2f", timer.fWall / repeatDraw);
603                 }
604                 if (timerCpu) {
605                     str.appendf(" cmsecs = %6.2f", timer.fCpu / repeatDraw);
606                 }
607                 if (timerGpu && glHelper && timer.fGpu > 0) {
608                     str.appendf(" gmsecs = %6.2f", timer.fGpu / repeatDraw);
609                 }
610                 log_progress(str);
611             }
612             if (outDir.size() > 0) {
613                 saveFile(bench->getName(), configName, outDir.c_str(),
614                          device->accessBitmap(false));
615             }
616         }
617         log_progress("\n");
618     }
619 
620     return 0;
621 }
622