• 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