1 /* 2 * Copyright 2011 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 "BenchTimer.h" 9 #include "ResultsWriter.h" 10 #include "SkBenchLogger.h" 11 #include "SkBenchmark.h" 12 #include "SkBitmapDevice.h" 13 #include "SkCanvas.h" 14 #include "SkColorPriv.h" 15 #include "SkCommandLineFlags.h" 16 #include "SkDeferredCanvas.h" 17 #include "SkGraphics.h" 18 #include "SkImageEncoder.h" 19 #include "SkOSFile.h" 20 #include "SkPicture.h" 21 #include "SkString.h" 22 23 #if SK_SUPPORT_GPU 24 #include "GrContext.h" 25 #include "GrContextFactory.h" 26 #include "GrRenderTarget.h" 27 #include "SkGpuDevice.h" 28 #include "gl/GrGLDefines.h" 29 #else 30 class GrContext; 31 #endif // SK_SUPPORT_GPU 32 33 #include <limits> 34 35 enum BenchMode { 36 kNormal_BenchMode, 37 kDeferred_BenchMode, 38 kDeferredSilent_BenchMode, 39 kRecord_BenchMode, 40 kPictureRecord_BenchMode 41 }; 42 const char* BenchMode_Name[] = { 43 "normal", "deferred", "deferredSilent", "record", "picturerecord" 44 }; 45 46 static const char kDefaultsConfigStr[] = "defaults"; 47 48 /////////////////////////////////////////////////////////////////////////////// 49 50 static void erase(SkBitmap& bm) { 51 if (bm.config() == SkBitmap::kA8_Config) { 52 bm.eraseColor(SK_ColorTRANSPARENT); 53 } else { 54 bm.eraseColor(SK_ColorWHITE); 55 } 56 } 57 58 class Iter { 59 public: 60 Iter() : fBench(BenchRegistry::Head()) {} 61 62 SkBenchmark* next() { 63 if (fBench) { 64 BenchRegistry::Factory f = fBench->factory(); 65 fBench = fBench->next(); 66 return f(); 67 } 68 return NULL; 69 } 70 71 private: 72 const BenchRegistry* fBench; 73 }; 74 75 class AutoPrePostDraw { 76 public: 77 AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) { 78 fBench->preDraw(); 79 } 80 ~AutoPrePostDraw() { 81 fBench->postDraw(); 82 } 83 private: 84 SkBenchmark* fBench; 85 }; 86 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 103 static void saveFile(const char name[], const char config[], const char dir[], 104 const SkBitmap& bm) { 105 SkBitmap copy; 106 if (!bm.copyTo(©, 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 filename; 123 make_filename(name, &filename); 124 filename.appendf("_%s.png", config); 125 SkString path = SkOSPath::SkPathJoin(dir, filename.c_str()); 126 ::remove(path.c_str()); 127 SkImageEncoder::EncodeFile(path.c_str(), copy, SkImageEncoder::kPNG_Type, 100); 128 } 129 130 static void performClip(SkCanvas* canvas, int w, int h) { 131 SkRect r; 132 133 r.set(SkIntToScalar(10), SkIntToScalar(10), 134 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); 135 canvas->clipRect(r, SkRegion::kIntersect_Op); 136 137 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), 138 SkIntToScalar(w-10), SkIntToScalar(h-10)); 139 canvas->clipRect(r, SkRegion::kXOR_Op); 140 } 141 142 static void performRotate(SkCanvas* canvas, int w, int h) { 143 const SkScalar x = SkIntToScalar(w) / 2; 144 const SkScalar y = SkIntToScalar(h) / 2; 145 146 canvas->translate(x, y); 147 canvas->rotate(SkIntToScalar(35)); 148 canvas->translate(-x, -y); 149 } 150 151 static void performScale(SkCanvas* canvas, int w, int h) { 152 const SkScalar x = SkIntToScalar(w) / 2; 153 const SkScalar y = SkIntToScalar(h) / 2; 154 155 canvas->translate(x, y); 156 // just enough so we can't take the sprite case 157 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100); 158 canvas->translate(-x, -y); 159 } 160 161 static SkBaseDevice* make_device(SkBitmap::Config config, const SkIPoint& size, 162 SkBenchmark::Backend backend, int sampleCount, GrContext* context) { 163 SkBaseDevice* device = NULL; 164 SkBitmap bitmap; 165 bitmap.setConfig(config, size.fX, size.fY); 166 167 switch (backend) { 168 case SkBenchmark::kRaster_Backend: 169 bitmap.allocPixels(); 170 erase(bitmap); 171 device = SkNEW_ARGS(SkBitmapDevice, (bitmap)); 172 break; 173 #if SK_SUPPORT_GPU 174 case SkBenchmark::kGPU_Backend: { 175 GrTextureDesc desc; 176 desc.fConfig = kSkia8888_GrPixelConfig; 177 desc.fFlags = kRenderTarget_GrTextureFlagBit; 178 desc.fWidth = size.fX; 179 desc.fHeight = size.fY; 180 desc.fSampleCnt = sampleCount; 181 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); 182 if (!texture) { 183 return NULL; 184 } 185 device = SkNEW_ARGS(SkGpuDevice, (context, texture.get())); 186 break; 187 } 188 #endif 189 case SkBenchmark::kPDF_Backend: 190 default: 191 SkDEBUGFAIL("unsupported"); 192 } 193 return device; 194 } 195 196 #if SK_SUPPORT_GPU 197 GrContextFactory gContextFactory; 198 typedef GrContextFactory::GLContextType GLContextType; 199 static const GLContextType kNative = GrContextFactory::kNative_GLContextType; 200 #if SK_ANGLE 201 static const GLContextType kANGLE = GrContextFactory::kANGLE_GLContextType; 202 #endif 203 static const GLContextType kDebug = GrContextFactory::kDebug_GLContextType; 204 static const GLContextType kNull = GrContextFactory::kNull_GLContextType; 205 #else 206 typedef int GLContextType; 207 static const GLContextType kNative = 0, kANGLE = 0, kDebug = 0, kNull = 0; 208 #endif 209 210 #ifdef SK_DEBUG 211 static const bool kIsDebug = true; 212 #else 213 static const bool kIsDebug = false; 214 #endif 215 216 static const struct Config { 217 SkBitmap::Config config; 218 const char* name; 219 int sampleCount; 220 SkBenchmark::Backend backend; 221 GLContextType contextType; 222 bool runByDefault; 223 } gConfigs[] = { 224 { SkBitmap::kNo_Config, "NONRENDERING", 0, SkBenchmark::kNonRendering_Backend, kNative, true}, 225 { SkBitmap::kARGB_8888_Config, "8888", 0, SkBenchmark::kRaster_Backend, kNative, true}, 226 { SkBitmap::kRGB_565_Config, "565", 0, SkBenchmark::kRaster_Backend, kNative, true}, 227 #if SK_SUPPORT_GPU 228 { SkBitmap::kARGB_8888_Config, "GPU", 0, SkBenchmark::kGPU_Backend, kNative, true}, 229 { SkBitmap::kARGB_8888_Config, "MSAA4", 4, SkBenchmark::kGPU_Backend, kNative, false}, 230 { SkBitmap::kARGB_8888_Config, "MSAA16", 16, SkBenchmark::kGPU_Backend, kNative, false}, 231 #if SK_ANGLE 232 { SkBitmap::kARGB_8888_Config, "ANGLE", 0, SkBenchmark::kGPU_Backend, kANGLE, true}, 233 #endif // SK_ANGLE 234 { SkBitmap::kARGB_8888_Config, "Debug", 0, SkBenchmark::kGPU_Backend, kDebug, kIsDebug}, 235 { SkBitmap::kARGB_8888_Config, "NULLGPU", 0, SkBenchmark::kGPU_Backend, kNull, true}, 236 #endif // SK_SUPPORT_GPU 237 }; 238 239 DEFINE_string(outDir, "", "If given, image of each bench will be put in outDir."); 240 DEFINE_string(timers, "cg", "Timers to display. " 241 "Options: w(all) W(all, truncated) c(pu) C(pu, truncated) g(pu)"); 242 243 DEFINE_bool(rotate, false, "Rotate canvas before bench run?"); 244 DEFINE_bool(scale, false, "Scale canvas before bench run?"); 245 DEFINE_bool(clip, false, "Clip canvas before bench run?"); 246 247 DEFINE_bool(forceAA, true, "Force anti-aliasing?"); 248 DEFINE_bool(forceFilter, false, "Force bitmap filtering?"); 249 DEFINE_string(forceDither, "default", "Force dithering: true, false, or default?"); 250 DEFINE_bool(forceBlend, false, "Force alpha blending?"); 251 252 DEFINE_int32(gpuCacheBytes, -1, "GPU cache size limit in bytes. 0 to disable cache."); 253 DEFINE_int32(gpuCacheCount, -1, "GPU cache size limit in object count. 0 to disable cache."); 254 255 DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n" 256 "Multiple matches may be separated by spaces.\n" 257 "~ causes a matching test to always be skipped\n" 258 "^ requires the start of the test to match\n" 259 "$ requires the end of the test to match\n" 260 "^ and $ requires an exact match\n" 261 "If a test does not match any list entry,\n" 262 "it is skipped unless some list entry starts with ~\n"); 263 DEFINE_string(mode, "normal", 264 "normal: draw to a normal canvas;\n" 265 "deferred: draw to a deferred canvas;\n" 266 "deferredSilent: deferred with silent playback;\n" 267 "record: draw to an SkPicture;\n" 268 "picturerecord: draw from an SkPicture to an SkPicture.\n"); 269 DEFINE_string(config, kDefaultsConfigStr, 270 "Run configs given. By default, runs the configs marked \"runByDefault\" in gConfigs."); 271 DEFINE_string(logFile, "", "Also write stdout here."); 272 DEFINE_int32(minMs, 20, "Shortest time we'll allow a benchmark to run."); 273 DEFINE_int32(maxMs, 4000, "Longest time we'll allow a benchmark to run."); 274 DEFINE_double(error, 0.01, 275 "Ratio of subsequent bench measurements must drop within 1error to converge."); 276 DEFINE_string(timeFormat, "%9.2f", "Format to print results, in milliseconds per 1000 loops."); 277 DEFINE_bool2(verbose, v, false, "Print more."); 278 DEFINE_string2(resourcePath, i, NULL, "directory for test resources."); 279 DEFINE_string(outResultsFile, "", "If given, the results will be written to the file in JSON format."); 280 281 // Has this bench converged? First arguments are milliseconds / loop iteration, 282 // last is overall runtime in milliseconds. 283 static bool HasConverged(double prevPerLoop, double currPerLoop, double currRaw) { 284 if (currRaw < FLAGS_minMs) { 285 return false; 286 } 287 const double low = 1 - FLAGS_error, high = 1 + FLAGS_error; 288 const double ratio = currPerLoop / prevPerLoop; 289 return low < ratio && ratio < high; 290 } 291 292 int tool_main(int argc, char** argv); 293 int tool_main(int argc, char** argv) { 294 #if SK_ENABLE_INST_COUNT 295 gPrintInstCount = true; 296 #endif 297 SkAutoGraphics ag; 298 SkCommandLineFlags::Parse(argc, argv); 299 300 // First, parse some flags. 301 SkBenchLogger logger; 302 if (FLAGS_logFile.count()) { 303 logger.SetLogFile(FLAGS_logFile[0]); 304 } 305 306 LoggerResultsWriter logWriter(logger, FLAGS_timeFormat[0]); 307 MultiResultsWriter writer; 308 writer.add(&logWriter); 309 SkAutoTDelete<JSONResultsWriter> jsonWriter; 310 if (FLAGS_outResultsFile.count()) { 311 jsonWriter.reset(SkNEW(JSONResultsWriter(FLAGS_outResultsFile[0]))); 312 writer.add(jsonWriter.get()); 313 } 314 // Instantiate after all the writers have been added to writer so that we 315 // call close() before their destructors are called on the way out. 316 CallEnd<MultiResultsWriter> ender(writer); 317 318 const uint8_t alpha = FLAGS_forceBlend ? 0x80 : 0xFF; 319 SkTriState::State dither = SkTriState::kDefault; 320 for (size_t i = 0; i < 3; i++) { 321 if (strcmp(SkTriState::Name[i], FLAGS_forceDither[0]) == 0) { 322 dither = static_cast<SkTriState::State>(i); 323 } 324 } 325 326 BenchMode benchMode = kNormal_BenchMode; 327 for (size_t i = 0; i < SK_ARRAY_COUNT(BenchMode_Name); i++) { 328 if (strcmp(FLAGS_mode[0], BenchMode_Name[i]) == 0) { 329 benchMode = static_cast<BenchMode>(i); 330 } 331 } 332 333 SkTDArray<int> configs; 334 bool runDefaultConfigs = false; 335 // Try user-given configs first. 336 for (int i = 0; i < FLAGS_config.count(); i++) { 337 for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++j) { 338 if (0 == strcmp(FLAGS_config[i], gConfigs[j].name)) { 339 *configs.append() = j; 340 } else if (0 == strcmp(FLAGS_config[i], kDefaultsConfigStr)) { 341 runDefaultConfigs = true; 342 } 343 } 344 } 345 // If there weren't any, fill in with defaults. 346 if (runDefaultConfigs) { 347 for (int i = 0; i < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++i) { 348 if (gConfigs[i].runByDefault) { 349 *configs.append() = i; 350 } 351 } 352 } 353 // Filter out things we can't run. 354 if (kNormal_BenchMode != benchMode) { 355 // Non-rendering configs only run in normal mode 356 for (int i = 0; i < configs.count(); ++i) { 357 const Config& config = gConfigs[configs[i]]; 358 if (SkBenchmark::kNonRendering_Backend == config.backend) { 359 configs.remove(i, 1); 360 --i; 361 } 362 } 363 } 364 // Set the resource path. 365 if (!FLAGS_resourcePath.isEmpty()) { 366 SkBenchmark::SetResourcePath(FLAGS_resourcePath[0]); 367 } 368 369 #if SK_SUPPORT_GPU 370 for (int i = 0; i < configs.count(); ++i) { 371 const Config& config = gConfigs[configs[i]]; 372 373 if (SkBenchmark::kGPU_Backend == config.backend) { 374 GrContext* context = gContextFactory.get(config.contextType); 375 if (NULL == context) { 376 logger.logError(SkStringPrintf( 377 "Error creating GrContext for config %s. Config will be skipped.\n", 378 config.name)); 379 configs.remove(i); 380 --i; 381 continue; 382 } 383 if (config.sampleCount > context->getMaxSampleCount()){ 384 logger.logError(SkStringPrintf( 385 "Sample count (%d) for config %s is unsupported. Config will be skipped.\n", 386 config.sampleCount, config.name)); 387 configs.remove(i); 388 --i; 389 continue; 390 } 391 } 392 } 393 #endif 394 395 // All flags should be parsed now. Report our settings. 396 if (kIsDebug) { 397 logger.logError("bench was built in Debug mode, so we're going to hide the times." 398 " It's for your own good!\n"); 399 } 400 writer.option("mode", FLAGS_mode[0]); 401 writer.option("alpha", SkStringPrintf("0x%02X", alpha).c_str()); 402 writer.option("antialias", SkStringPrintf("%d", FLAGS_forceAA).c_str()); 403 writer.option("filter", SkStringPrintf("%d", FLAGS_forceFilter).c_str()); 404 writer.option("dither", SkTriState::Name[dither]); 405 406 writer.option("rotate", SkStringPrintf("%d", FLAGS_rotate).c_str()); 407 writer.option("scale", SkStringPrintf("%d", FLAGS_scale).c_str()); 408 writer.option("clip", SkStringPrintf("%d", FLAGS_clip).c_str()); 409 410 #if defined(SK_SCALAR_IS_FIXED) 411 writer.option("scalar", "fixed"); 412 #else 413 writer.option("scalar", "float"); 414 #endif 415 416 #if defined(SK_BUILD_FOR_WIN32) 417 writer.option("system", "WIN32"); 418 #elif defined(SK_BUILD_FOR_MAC) 419 writer.option("system", "MAC"); 420 #elif defined(SK_BUILD_FOR_ANDROID) 421 writer.option("system", "ANDROID"); 422 #elif defined(SK_BUILD_FOR_UNIX) 423 writer.option("system", "UNIX"); 424 #else 425 writer.option("system", "other"); 426 #endif 427 428 #if defined(SK_DEBUG) 429 writer.option("build", "DEBUG"); 430 #else 431 writer.option("build", "RELEASE"); 432 #endif 433 434 // Set texture cache limits if non-default. 435 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 436 #if SK_SUPPORT_GPU 437 const Config& config = gConfigs[i]; 438 if (SkBenchmark::kGPU_Backend != config.backend) { 439 continue; 440 } 441 GrContext* context = gContextFactory.get(config.contextType); 442 if (NULL == context) { 443 continue; 444 } 445 446 size_t bytes; 447 int count; 448 context->getTextureCacheLimits(&count, &bytes); 449 if (-1 != FLAGS_gpuCacheBytes) { 450 bytes = static_cast<size_t>(FLAGS_gpuCacheBytes); 451 } 452 if (-1 != FLAGS_gpuCacheCount) { 453 count = FLAGS_gpuCacheCount; 454 } 455 context->setTextureCacheLimits(count, bytes); 456 #endif 457 } 458 459 // Run each bench in each configuration it supports and we asked for. 460 Iter iter; 461 SkBenchmark* bench; 462 while ((bench = iter.next()) != NULL) { 463 SkAutoTUnref<SkBenchmark> benchUnref(bench); 464 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) { 465 continue; 466 } 467 468 bench->setForceAlpha(alpha); 469 bench->setForceAA(FLAGS_forceAA); 470 bench->setForceFilter(FLAGS_forceFilter); 471 bench->setDither(dither); 472 AutoPrePostDraw appd(bench); 473 474 bool loggedBenchName = false; 475 for (int i = 0; i < configs.count(); ++i) { 476 const int configIndex = configs[i]; 477 const Config& config = gConfigs[configIndex]; 478 479 if (!bench->isSuitableFor(config.backend)) { 480 continue; 481 } 482 483 GrContext* context = NULL; 484 #if SK_SUPPORT_GPU 485 SkGLContextHelper* glContext = NULL; 486 if (SkBenchmark::kGPU_Backend == config.backend) { 487 context = gContextFactory.get(config.contextType); 488 if (NULL == context) { 489 continue; 490 } 491 glContext = gContextFactory.getGLContext(config.contextType); 492 } 493 #endif 494 SkAutoTUnref<SkBaseDevice> device; 495 SkAutoTUnref<SkCanvas> canvas; 496 SkPicture recordFrom, recordTo; 497 const SkIPoint dim = bench->getSize(); 498 499 const SkPicture::RecordingFlags kRecordFlags = 500 SkPicture::kUsePathBoundsForClip_RecordingFlag; 501 502 if (SkBenchmark::kNonRendering_Backend != config.backend) { 503 device.reset(make_device(config.config, 504 dim, 505 config.backend, 506 config.sampleCount, 507 context)); 508 if (!device.get()) { 509 logger.logError(SkStringPrintf( 510 "Device creation failure for config %s. Will skip.\n", config.name)); 511 continue; 512 } 513 514 switch(benchMode) { 515 case kDeferredSilent_BenchMode: 516 case kDeferred_BenchMode: 517 canvas.reset(SkDeferredCanvas::Create(device.get())); 518 break; 519 case kRecord_BenchMode: 520 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags))); 521 break; 522 case kPictureRecord_BenchMode: 523 bench->draw(1, recordFrom.beginRecording(dim.fX, dim.fY, kRecordFlags)); 524 recordFrom.endRecording(); 525 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags))); 526 break; 527 case kNormal_BenchMode: 528 canvas.reset(new SkCanvas(device.get())); 529 break; 530 default: 531 SkASSERT(false); 532 } 533 } 534 535 if (NULL != canvas) { 536 canvas->clear(SK_ColorWHITE); 537 if (FLAGS_clip) { performClip(canvas, dim.fX, dim.fY); } 538 if (FLAGS_scale) { performScale(canvas, dim.fX, dim.fY); } 539 if (FLAGS_rotate) { performRotate(canvas, dim.fX, dim.fY); } 540 } 541 542 if (!loggedBenchName) { 543 loggedBenchName = true; 544 writer.bench(bench->getName(), dim.fX, dim.fY); 545 } 546 547 #if SK_SUPPORT_GPU 548 SkGLContextHelper* contextHelper = NULL; 549 if (SkBenchmark::kGPU_Backend == config.backend) { 550 contextHelper = gContextFactory.getGLContext(config.contextType); 551 } 552 BenchTimer timer(contextHelper); 553 #else 554 BenchTimer timer; 555 #endif 556 557 double previous = std::numeric_limits<double>::infinity(); 558 bool converged = false; 559 560 // variables used to compute loopsPerFrame 561 double frameIntervalTime = 0.0f; 562 int frameIntervalTotalLoops = 0; 563 564 bool frameIntervalComputed = false; 565 int loopsPerFrame = 0; 566 int loopsPerIter = 0; 567 if (FLAGS_verbose) { SkDebugf("%s %s: ", bench->getName(), config.name); } 568 do { 569 // Ramp up 1 -> 2 -> 4 -> 8 -> 16 -> ... -> ~1 billion. 570 loopsPerIter = (loopsPerIter == 0) ? 1 : loopsPerIter * 2; 571 if (loopsPerIter >= (1<<30) || timer.fWall > FLAGS_maxMs) { 572 // If you find it takes more than a billion loops to get up to 20ms of runtime, 573 // you've got a computer clocked at several THz or have a broken benchmark. ;) 574 // "1B ought to be enough for anybody." 575 logger.logError(SkStringPrintf( 576 "\nCan't get %s %s to converge in %dms (%d loops)", 577 bench->getName(), config.name, FLAGS_maxMs, loopsPerIter)); 578 break; 579 } 580 581 if ((benchMode == kRecord_BenchMode || benchMode == kPictureRecord_BenchMode)) { 582 // Clear the recorded commands so that they do not accumulate. 583 canvas.reset(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags)); 584 } 585 586 timer.start(); 587 // Inner loop that allows us to break the run into smaller 588 // chunks (e.g. frames). This is especially useful for the GPU 589 // as we can flush and/or swap buffers to keep the GPU from 590 // queuing up too much work. 591 for (int loopCount = loopsPerIter; loopCount > 0; ) { 592 // Save and restore around each call to draw() to guarantee a pristine canvas. 593 SkAutoCanvasRestore saveRestore(canvas, true/*also save*/); 594 595 int loops; 596 if (frameIntervalComputed && loopCount > loopsPerFrame) { 597 loops = loopsPerFrame; 598 loopCount -= loopsPerFrame; 599 } else { 600 loops = loopCount; 601 loopCount = 0; 602 } 603 604 if (benchMode == kPictureRecord_BenchMode) { 605 recordFrom.draw(canvas); 606 } else { 607 bench->draw(loops, canvas); 608 } 609 610 if (kDeferredSilent_BenchMode == benchMode) { 611 static_cast<SkDeferredCanvas*>(canvas.get())->silentFlush(); 612 } else if (NULL != canvas) { 613 canvas->flush(); 614 } 615 616 #if SK_SUPPORT_GPU 617 // swap drawing buffers on each frame to prevent the GPU 618 // from queuing up too much work 619 if (NULL != glContext) { 620 glContext->swapBuffers(); 621 } 622 #endif 623 } 624 625 626 627 // Stop truncated timers before GL calls complete, and stop the full timers after. 628 timer.truncatedEnd(); 629 #if SK_SUPPORT_GPU 630 if (NULL != glContext) { 631 context->flush(); 632 SK_GL(*glContext, Finish()); 633 } 634 #endif 635 timer.end(); 636 637 // setup the frame interval for subsequent iterations 638 if (!frameIntervalComputed) { 639 frameIntervalTime += timer.fWall; 640 frameIntervalTotalLoops += loopsPerIter; 641 if (frameIntervalTime >= FLAGS_minMs) { 642 frameIntervalComputed = true; 643 loopsPerFrame = 644 (int)(((double)frameIntervalTotalLoops / frameIntervalTime) * FLAGS_minMs); 645 if (loopsPerFrame < 1) { 646 loopsPerFrame = 1; 647 } 648 // SkDebugf(" %s has %d loops in %f ms (normalized to %d)\n", 649 // bench->getName(), frameIntervalTotalLoops, 650 // timer.fWall, loopsPerFrame); 651 } 652 } 653 654 const double current = timer.fWall / loopsPerIter; 655 if (FLAGS_verbose && current > previous) { SkDebugf(""); } 656 if (FLAGS_verbose) { SkDebugf("%.3g ", current); } 657 converged = HasConverged(previous, current, timer.fWall); 658 previous = current; 659 } while (!kIsDebug && !converged); 660 if (FLAGS_verbose) { SkDebugf("\n"); } 661 662 if (FLAGS_outDir.count() && SkBenchmark::kNonRendering_Backend != config.backend) { 663 saveFile(bench->getName(), 664 config.name, 665 FLAGS_outDir[0], 666 device->accessBitmap(false)); 667 } 668 669 if (kIsDebug) { 670 // Let's not mislead ourselves by looking at Debug build bench times! 671 continue; 672 } 673 674 // Normalize to ms per 1000 iterations. 675 const double normalize = 1000.0 / loopsPerIter; 676 const struct { char shortName; const char* longName; double ms; } times[] = { 677 {'w', "msecs", normalize * timer.fWall}, 678 {'W', "Wmsecs", normalize * timer.fTruncatedWall}, 679 {'c', "cmsecs", normalize * timer.fCpu}, 680 {'C', "Cmsecs", normalize * timer.fTruncatedCpu}, 681 {'g', "gmsecs", normalize * timer.fGpu}, 682 }; 683 684 writer.config(config.name); 685 for (size_t i = 0; i < SK_ARRAY_COUNT(times); i++) { 686 if (strchr(FLAGS_timers[0], times[i].shortName) && times[i].ms > 0) { 687 writer.timer(times[i].longName, times[i].ms); 688 } 689 } 690 } 691 } 692 #if SK_SUPPORT_GPU 693 gContextFactory.destroyContexts(); 694 #endif 695 return 0; 696 } 697 698 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 699 int main(int argc, char * const argv[]) { 700 return tool_main(argc, (char**) argv); 701 } 702 #endif 703