1 #include "SkCanvas.h" 2 #include "SkColorPriv.h" 3 #include "SkGraphics.h" 4 #include "SkImageEncoder.h" 5 #include "SkNWayCanvas.h" 6 #include "SkPicture.h" 7 #include "SkString.h" 8 #include "GrContext.h" 9 #include "SkGpuDevice.h" 10 #include "SkEGLContext.h" 11 12 #include "SkBenchmark.h" 13 #include "BenchTimer.h" 14 15 #ifdef ANDROID 16 static void log_error(const char msg[]) { SkDebugf("%s", msg); } 17 static void log_progress(const char msg[]) { SkDebugf("%s", msg); } 18 #else 19 static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); } 20 static void log_progress(const char msg[]) { printf("%s", msg); } 21 #endif 22 23 static void log_error(const SkString& str) { log_error(str.c_str()); } 24 static void log_progress(const SkString& str) { log_progress(str.c_str()); } 25 26 /////////////////////////////////////////////////////////////////////////////// 27 28 static void erase(SkBitmap& bm) { 29 if (bm.config() == SkBitmap::kA8_Config) { 30 bm.eraseColor(0); 31 } else { 32 bm.eraseColor(SK_ColorWHITE); 33 } 34 } 35 36 #if 0 37 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) { 38 if (bm1.width() != bm2.width() || 39 bm1.height() != bm2.height() || 40 bm1.config() != bm2.config()) { 41 return false; 42 } 43 44 size_t pixelBytes = bm1.width() * bm1.bytesPerPixel(); 45 for (int y = 0; y < bm1.height(); y++) { 46 if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) { 47 return false; 48 } 49 } 50 return true; 51 } 52 #endif 53 54 class Iter { 55 public: 56 Iter(void* param) { 57 fBench = BenchRegistry::Head(); 58 fParam = param; 59 } 60 61 SkBenchmark* next() { 62 if (fBench) { 63 BenchRegistry::Factory f = fBench->factory(); 64 fBench = fBench->next(); 65 return f(fParam); 66 } 67 return NULL; 68 } 69 70 private: 71 const BenchRegistry* fBench; 72 void* fParam; 73 }; 74 75 static void make_filename(const char name[], SkString* path) { 76 path->set(name); 77 for (int i = 0; name[i]; i++) { 78 switch (name[i]) { 79 case '/': 80 case '\\': 81 case ' ': 82 case ':': 83 path->writable_str()[i] = '-'; 84 break; 85 default: 86 break; 87 } 88 } 89 } 90 91 static void saveFile(const char name[], const char config[], const char dir[], 92 const SkBitmap& bm) { 93 SkBitmap copy; 94 if (!bm.copyTo(©, SkBitmap::kARGB_8888_Config)) { 95 return; 96 } 97 98 if (bm.config() == SkBitmap::kA8_Config) { 99 // turn alpha into gray-scale 100 size_t size = copy.getSize() >> 2; 101 SkPMColor* p = copy.getAddr32(0, 0); 102 for (size_t i = 0; i < size; i++) { 103 int c = (*p >> SK_A32_SHIFT) & 0xFF; 104 c = 255 - c; 105 c |= (c << 24) | (c << 16) | (c << 8); 106 *p++ = c | (SK_A32_MASK << SK_A32_SHIFT); 107 } 108 } 109 110 SkString str; 111 make_filename(name, &str); 112 str.appendf("_%s.png", config); 113 str.prepend(dir); 114 ::remove(str.c_str()); 115 SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type, 116 100); 117 } 118 119 static void performClip(SkCanvas* canvas, int w, int h) { 120 SkRect r; 121 122 r.set(SkIntToScalar(10), SkIntToScalar(10), 123 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); 124 canvas->clipRect(r, SkRegion::kIntersect_Op); 125 126 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), 127 SkIntToScalar(w-10), SkIntToScalar(h-10)); 128 canvas->clipRect(r, SkRegion::kXOR_Op); 129 } 130 131 static void performRotate(SkCanvas* canvas, int w, int h) { 132 const SkScalar x = SkIntToScalar(w) / 2; 133 const SkScalar y = SkIntToScalar(h) / 2; 134 135 canvas->translate(x, y); 136 canvas->rotate(SkIntToScalar(35)); 137 canvas->translate(-x, -y); 138 } 139 140 static void performScale(SkCanvas* canvas, int w, int h) { 141 const SkScalar x = SkIntToScalar(w) / 2; 142 const SkScalar y = SkIntToScalar(h) / 2; 143 144 canvas->translate(x, y); 145 // just enough so we can't take the sprite case 146 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100); 147 canvas->translate(-x, -y); 148 } 149 150 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) { 151 if (argv < stop) { 152 *var = atoi(*argv) != 0; 153 return true; 154 } 155 return false; 156 } 157 158 enum Backend { 159 kRaster_Backend, 160 kGPU_Backend, 161 kPDF_Backend, 162 }; 163 164 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size, 165 Backend backend, GrContext* context) { 166 SkDevice* device = NULL; 167 SkBitmap bitmap; 168 bitmap.setConfig(config, size.fX, size.fY); 169 170 switch (backend) { 171 case kRaster_Backend: 172 bitmap.allocPixels(); 173 erase(bitmap); 174 device = new SkDevice(NULL, bitmap, true); 175 break; 176 case kGPU_Backend: 177 device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget()); 178 // device->clear(0xFFFFFFFF); 179 break; 180 case kPDF_Backend: 181 default: 182 SkASSERT(!"unsupported"); 183 } 184 return device; 185 } 186 187 static const struct { 188 SkBitmap::Config fConfig; 189 const char* fName; 190 Backend fBackend; 191 } gConfigs[] = { 192 { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend }, 193 { SkBitmap::kRGB_565_Config, "565", kRaster_Backend }, 194 { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend }, 195 }; 196 197 static int findConfig(const char config[]) { 198 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) { 199 if (!strcmp(config, gConfigs[i].fName)) { 200 return i; 201 } 202 } 203 return -1; 204 } 205 206 int main (int argc, char * const argv[]) { 207 SkAutoGraphics ag; 208 209 SkTDict<const char*> defineDict(1024); 210 int repeatDraw = 1; 211 int forceAlpha = 0xFF; 212 bool forceAA = true; 213 bool forceFilter = false; 214 SkTriState::State forceDither = SkTriState::kDefault; 215 bool timerWall = false; 216 bool timerCpu = true; 217 bool timerGpu = true; 218 bool doScale = false; 219 bool doRotate = false; 220 bool doClip = false; 221 const char* matchStr = NULL; 222 bool hasStrokeWidth = false; 223 float strokeWidth; 224 225 SkString outDir; 226 SkBitmap::Config outConfig = SkBitmap::kNo_Config; 227 const char* configName = ""; 228 Backend backend = kRaster_Backend; // for warning 229 int configCount = SK_ARRAY_COUNT(gConfigs); 230 231 char* const* stop = argv + argc; 232 for (++argv; argv < stop; ++argv) { 233 if (strcmp(*argv, "-o") == 0) { 234 argv++; 235 if (argv < stop && **argv) { 236 outDir.set(*argv); 237 if (outDir.c_str()[outDir.size() - 1] != '/') { 238 outDir.append("/"); 239 } 240 } 241 } else if (strcmp(*argv, "-repeat") == 0) { 242 argv++; 243 if (argv < stop) { 244 repeatDraw = atoi(*argv); 245 if (repeatDraw < 1) { 246 repeatDraw = 1; 247 } 248 } else { 249 log_error("missing arg for -repeat\n"); 250 return -1; 251 } 252 } else if (strcmp(*argv, "-timers") == 0) { 253 argv++; 254 if (argv < stop) { 255 timerWall = false; 256 timerCpu = false; 257 timerGpu = false; 258 for (char* t = *argv; *t; ++t) { 259 switch (*t) { 260 case 'w': timerWall = true; break; 261 case 'c': timerCpu = true; break; 262 case 'g': timerGpu = true; break; 263 } 264 } 265 } else { 266 log_error("missing arg for -timers\n"); 267 return -1; 268 } 269 } else if (!strcmp(*argv, "-rotate")) { 270 doRotate = true; 271 } else if (!strcmp(*argv, "-scale")) { 272 doScale = true; 273 } else if (!strcmp(*argv, "-clip")) { 274 doClip = true; 275 } else if (strcmp(*argv, "-forceAA") == 0) { 276 if (!parse_bool_arg(++argv, stop, &forceAA)) { 277 log_error("missing arg for -forceAA\n"); 278 return -1; 279 } 280 } else if (strcmp(*argv, "-forceFilter") == 0) { 281 if (!parse_bool_arg(++argv, stop, &forceFilter)) { 282 log_error("missing arg for -forceFilter\n"); 283 return -1; 284 } 285 } else if (strcmp(*argv, "-forceDither") == 0) { 286 bool tmp; 287 if (!parse_bool_arg(++argv, stop, &tmp)) { 288 log_error("missing arg for -forceDither\n"); 289 return -1; 290 } 291 forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse; 292 } else if (strcmp(*argv, "-forceBlend") == 0) { 293 bool wantAlpha = false; 294 if (!parse_bool_arg(++argv, stop, &wantAlpha)) { 295 log_error("missing arg for -forceBlend\n"); 296 return -1; 297 } 298 forceAlpha = wantAlpha ? 0x80 : 0xFF; 299 } else if (strcmp(*argv, "-strokeWidth") == 0) { 300 argv++; 301 if (argv < stop) { 302 const char *strokeWidthStr = *argv; 303 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) { 304 log_error("bad arg for -strokeWidth\n"); 305 return -1; 306 } 307 hasStrokeWidth = true; 308 } else { 309 log_error("missing arg for -strokeWidth\n"); 310 return -1; 311 } 312 } else if (strcmp(*argv, "-match") == 0) { 313 argv++; 314 if (argv < stop) { 315 matchStr = *argv; 316 } else { 317 log_error("missing arg for -match\n"); 318 return -1; 319 } 320 } else if (strcmp(*argv, "-config") == 0) { 321 argv++; 322 if (argv < stop) { 323 int index = findConfig(*argv); 324 if (index >= 0) { 325 outConfig = gConfigs[index].fConfig; 326 configName = gConfigs[index].fName; 327 backend = gConfigs[index].fBackend; 328 configCount = 1; 329 } else { 330 SkString str; 331 str.printf("unrecognized config %s\n", *argv); 332 log_error(str); 333 return -1; 334 } 335 } else { 336 log_error("missing arg for -config\n"); 337 return -1; 338 } 339 } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) { 340 argv++; 341 if (argv < stop) { 342 defineDict.set(argv[-1] + 2, *argv); 343 } else { 344 log_error("incomplete '-Dfoo bar' definition\n"); 345 return -1; 346 } 347 } else { 348 SkString str; 349 str.printf("unrecognized arg %s\n", *argv); 350 log_error(str); 351 return -1; 352 } 353 } 354 355 // report our current settings 356 { 357 SkString str; 358 str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d\n", 359 forceAlpha, forceAA, forceFilter); 360 log_progress(str); 361 } 362 363 GrContext* context = NULL; 364 SkEGLContext eglContext; 365 if (eglContext.init(1024, 1024)) { 366 context = GrContext::CreateGLShaderContext(); 367 } 368 369 BenchTimer timer = BenchTimer(); 370 371 Iter iter(&defineDict); 372 SkBenchmark* bench; 373 while ((bench = iter.next()) != NULL) { 374 SkIPoint dim = bench->getSize(); 375 if (dim.fX <= 0 || dim.fY <= 0) { 376 continue; 377 } 378 379 bench->setForceAlpha(forceAlpha); 380 bench->setForceAA(forceAA); 381 bench->setForceFilter(forceFilter); 382 bench->setDither(forceDither); 383 if (hasStrokeWidth) { 384 bench->setStrokeWidth(strokeWidth); 385 } 386 387 // only run benchmarks if their name contains matchStr 388 if (matchStr && strstr(bench->getName(), matchStr) == NULL) { 389 continue; 390 } 391 392 { 393 SkString str; 394 str.printf("running bench [%d %d] %28s", dim.fX, dim.fY, 395 bench->getName()); 396 log_progress(str); 397 } 398 399 for (int configIndex = 0; configIndex < configCount; configIndex++) { 400 if (configCount > 1) { 401 outConfig = gConfigs[configIndex].fConfig; 402 configName = gConfigs[configIndex].fName; 403 backend = gConfigs[configIndex].fBackend; 404 } 405 406 if (kGPU_Backend == backend && NULL == context) { 407 continue; 408 } 409 410 SkDevice* device = make_device(outConfig, dim, backend, context); 411 SkCanvas canvas(device); 412 device->unref(); 413 414 if (doClip) { 415 performClip(&canvas, dim.fX, dim.fY); 416 } 417 if (doScale) { 418 performScale(&canvas, dim.fX, dim.fY); 419 } 420 if (doRotate) { 421 performRotate(&canvas, dim.fX, dim.fY); 422 } 423 424 bool gpu = kGPU_Backend == backend && context; 425 //warm up caches if needed 426 if (repeatDraw > 1) { 427 SkAutoCanvasRestore acr(&canvas, true); 428 bench->draw(&canvas); 429 if (gpu) { 430 context->flush(); 431 glFinish(); 432 } 433 } 434 435 timer.start(); 436 for (int i = 0; i < repeatDraw; i++) { 437 SkAutoCanvasRestore acr(&canvas, true); 438 bench->draw(&canvas); 439 } 440 timer.end(); 441 442 if (repeatDraw > 1) { 443 SkString str; 444 str.printf(" %4s:", configName); 445 if (timerWall) { 446 str.appendf(" msecs = %6.2f", timer.fWall / repeatDraw); 447 } 448 if (timerCpu) { 449 str.appendf(" cmsecs = %6.2f", timer.fCpu / repeatDraw); 450 } 451 if (timerGpu && gpu && timer.fGpu > 0) { 452 str.appendf(" gmsecs = %6.2f", timer.fGpu / repeatDraw); 453 } 454 log_progress(str); 455 } 456 if (outDir.size() > 0) { 457 saveFile(bench->getName(), configName, outDir.c_str(), 458 device->accessBitmap(false)); 459 } 460 } 461 log_progress("\n"); 462 } 463 464 return 0; 465 } 466