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 /* 9 * Code for the "gm" (Golden Master) rendering comparison tool. 10 * 11 * If you make changes to this, re-run the self-tests at gm/tests/run.sh 12 * to make sure they still pass... you may need to change the expected 13 * results of the self-test. 14 */ 15 16 #include "gm.h" 17 #include "gm_expectations.h" 18 #include "system_preferences.h" 19 #include "SkBitmap.h" 20 #include "SkBitmapChecksummer.h" 21 #include "SkColorPriv.h" 22 #include "SkData.h" 23 #include "SkDeferredCanvas.h" 24 #include "SkDevice.h" 25 #include "SkDrawFilter.h" 26 #include "SkGPipe.h" 27 #include "SkGraphics.h" 28 #include "SkImageDecoder.h" 29 #include "SkImageEncoder.h" 30 #include "SkOSFile.h" 31 #include "SkPicture.h" 32 #include "SkRefCnt.h" 33 #include "SkStream.h" 34 #include "SkTArray.h" 35 #include "SkTileGridPicture.h" 36 #include "SamplePipeControllers.h" 37 38 #ifdef SK_BUILD_FOR_WIN 39 // json includes xlocale which generates warning 4530 because we're compiling without 40 // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067 41 #pragma warning(push) 42 #pragma warning(disable : 4530) 43 #endif 44 #include "json/value.h" 45 #ifdef SK_BUILD_FOR_WIN 46 #pragma warning(pop) 47 #endif 48 49 #if SK_SUPPORT_GPU 50 #include "GrContextFactory.h" 51 #include "GrRenderTarget.h" 52 #include "SkGpuDevice.h" 53 typedef GrContextFactory::GLContextType GLContextType; 54 #else 55 class GrContext; 56 class GrRenderTarget; 57 typedef int GLContextType; 58 #endif 59 60 static bool gForceBWtext; 61 62 extern bool gSkSuppressFontCachePurgeSpew; 63 64 #ifdef SK_SUPPORT_PDF 65 #include "SkPDFDevice.h" 66 #include "SkPDFDocument.h" 67 #endif 68 69 // Until we resolve http://code.google.com/p/skia/issues/detail?id=455 , 70 // stop writing out XPS-format image baselines in gm. 71 #undef SK_SUPPORT_XPS 72 #ifdef SK_SUPPORT_XPS 73 #include "SkXPSDevice.h" 74 #endif 75 76 #ifdef SK_BUILD_FOR_MAC 77 #include "SkCGUtils.h" 78 #define CAN_IMAGE_PDF 1 79 #else 80 #define CAN_IMAGE_PDF 0 81 #endif 82 83 // TODO(epoger): We created this ErrorBitfield so that we could record 84 // multiple error types for the same comparison. But in practice, we 85 // process its final value in switch() statements, which inherently 86 // assume that only one error type will be set. 87 // I think we should probably change this to be an enum, and thus 88 // constrain ourselves to a single error type per comparison. 89 typedef int ErrorBitfield; 90 const static ErrorBitfield ERROR_NONE = 0x00; 91 const static ErrorBitfield ERROR_NO_GPU_CONTEXT = 0x01; 92 const static ErrorBitfield ERROR_IMAGE_MISMATCH = 0x02; 93 // const static ErrorBitfield ERROR_DIMENSION_MISMATCH = 0x04; DEPRECATED in https://codereview.appspot.com/7064047 94 const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08; 95 const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10; 96 97 const static char kJsonKey_ActualResults[] = "actual-results"; 98 const static char kJsonKey_ActualResults_Failed[] = "failed"; 99 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored"; 100 const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison"; 101 const static char kJsonKey_ActualResults_Succeeded[] = "succeeded"; 102 const static char kJsonKey_ActualResults_AnyStatus_Checksum[] = "checksum"; 103 104 const static char kJsonKey_ExpectedResults[] = "expected-results"; 105 const static char kJsonKey_ExpectedResults_Checksums[] = "checksums"; 106 const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure"; 107 108 using namespace skiagm; 109 110 struct FailRec { 111 SkString fName; 112 bool fIsPixelError; 113 114 FailRec() : fIsPixelError(false) {} 115 FailRec(const SkString& name) : fName(name), fIsPixelError(false) {} 116 }; 117 118 class Iter { 119 public: 120 Iter() { 121 this->reset(); 122 } 123 124 void reset() { 125 fReg = GMRegistry::Head(); 126 } 127 128 GM* next() { 129 if (fReg) { 130 GMRegistry::Factory fact = fReg->factory(); 131 fReg = fReg->next(); 132 return fact(0); 133 } 134 return NULL; 135 } 136 137 static int Count() { 138 const GMRegistry* reg = GMRegistry::Head(); 139 int count = 0; 140 while (reg) { 141 count += 1; 142 reg = reg->next(); 143 } 144 return count; 145 } 146 147 private: 148 const GMRegistry* fReg; 149 }; 150 151 enum Backend { 152 kRaster_Backend, 153 kGPU_Backend, 154 kPDF_Backend, 155 kXPS_Backend, 156 }; 157 158 enum BbhType { 159 kNone_BbhType, 160 kRTree_BbhType, 161 kTileGrid_BbhType, 162 }; 163 164 enum ConfigFlags { 165 kNone_ConfigFlag = 0x0, 166 /* Write GM images if a write path is provided. */ 167 kWrite_ConfigFlag = 0x1, 168 /* Read reference GM images if a read path is provided. */ 169 kRead_ConfigFlag = 0x2, 170 kRW_ConfigFlag = (kWrite_ConfigFlag | kRead_ConfigFlag), 171 }; 172 173 struct ConfigData { 174 SkBitmap::Config fConfig; 175 Backend fBackend; 176 GLContextType fGLContextType; // GPU backend only 177 int fSampleCnt; // GPU backend only 178 ConfigFlags fFlags; 179 const char* fName; 180 }; 181 182 class BWTextDrawFilter : public SkDrawFilter { 183 public: 184 virtual bool filter(SkPaint*, Type) SK_OVERRIDE; 185 }; 186 bool BWTextDrawFilter::filter(SkPaint* p, Type t) { 187 if (kText_Type == t) { 188 p->setAntiAlias(false); 189 } 190 return true; 191 } 192 193 struct PipeFlagComboData { 194 const char* name; 195 uint32_t flags; 196 }; 197 198 static PipeFlagComboData gPipeWritingFlagCombos[] = { 199 { "", 0 }, 200 { " cross-process", SkGPipeWriter::kCrossProcess_Flag }, 201 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag 202 | SkGPipeWriter::kSharedAddressSpace_Flag } 203 }; 204 205 class GMMain { 206 public: 207 GMMain() { 208 // Set default values of member variables, which tool_main() 209 // may override. 210 fUseFileHierarchy = false; 211 fMismatchPath = NULL; 212 } 213 214 SkString make_name(const char shortName[], const char configName[]) { 215 SkString name; 216 if (0 == strlen(configName)) { 217 name.append(shortName); 218 } else if (fUseFileHierarchy) { 219 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName); 220 } else { 221 name.appendf("%s_%s", shortName, configName); 222 } 223 return name; 224 } 225 226 /* since PNG insists on unpremultiplying our alpha, we take no 227 precision chances and force all pixels to be 100% opaque, 228 otherwise on compare we may not get a perfect match. 229 */ 230 static void force_all_opaque(const SkBitmap& bitmap) { 231 SkBitmap::Config config = bitmap.config(); 232 switch (config) { 233 case SkBitmap::kARGB_8888_Config: 234 force_all_opaque_8888(bitmap); 235 break; 236 case SkBitmap::kRGB_565_Config: 237 // nothing to do here; 565 bitmaps are inherently opaque 238 break; 239 default: 240 fprintf(stderr, "unsupported bitmap config %d\n", config); 241 SkDEBUGFAIL("unsupported bitmap config"); 242 } 243 } 244 245 static void force_all_opaque_8888(const SkBitmap& bitmap) { 246 SkAutoLockPixels lock(bitmap); 247 for (int y = 0; y < bitmap.height(); y++) { 248 for (int x = 0; x < bitmap.width(); x++) { 249 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); 250 } 251 } 252 } 253 254 static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) { 255 // TODO(epoger): Now that we have removed force_all_opaque() 256 // from this method, we should be able to get rid of the 257 // transformation to 8888 format also. 258 SkBitmap copy; 259 bitmap.copyTo(©, SkBitmap::kARGB_8888_Config); 260 return SkImageEncoder::EncodeFile(path.c_str(), copy, 261 SkImageEncoder::kPNG_Type, 100); 262 } 263 264 // Records an error in fFailedTests, if we want to record errors 265 // of this type. 266 void RecordError(ErrorBitfield errorType, const SkString& name, 267 const char renderModeDescriptor []) { 268 bool isPixelError; 269 switch (errorType) { 270 case ERROR_NONE: 271 return; 272 case ERROR_READING_REFERENCE_IMAGE: 273 return; 274 case ERROR_IMAGE_MISMATCH: 275 isPixelError = true; 276 break; 277 default: 278 isPixelError = false; 279 break; 280 } 281 282 FailRec& rec = fFailedTests.push_back(make_name( 283 name.c_str(), renderModeDescriptor)); 284 rec.fIsPixelError = isPixelError; 285 } 286 287 // List contents of fFailedTests via SkDebug. 288 void ListErrors() { 289 for (int i = 0; i < fFailedTests.count(); ++i) { 290 if (fFailedTests[i].fIsPixelError) { 291 SkDebugf("\t\t%s pixel_error\n", fFailedTests[i].fName.c_str()); 292 } else { 293 SkDebugf("\t\t%s\n", fFailedTests[i].fName.c_str()); 294 } 295 } 296 } 297 298 static bool write_document(const SkString& path, 299 const SkDynamicMemoryWStream& document) { 300 SkFILEWStream stream(path.c_str()); 301 SkAutoDataUnref data(document.copyToData()); 302 return stream.writeData(data.get()); 303 } 304 305 /** 306 * Prepare an SkBitmap to render a GM into. 307 * 308 * After you've rendered the GM into the SkBitmap, you must call 309 * complete_bitmap()! 310 * 311 * @todo thudson 22 April 2011 - could refactor this to take in 312 * a factory to generate the context, always call readPixels() 313 * (logically a noop for rasters, if wasted time), and thus collapse the 314 * GPU special case and also let this be used for SkPicture testing. 315 */ 316 static void setup_bitmap(const ConfigData& gRec, SkISize& size, 317 SkBitmap* bitmap) { 318 bitmap->setConfig(gRec.fConfig, size.width(), size.height()); 319 bitmap->allocPixels(); 320 bitmap->eraseColor(SK_ColorTRANSPARENT); 321 } 322 323 /** 324 * Any finalization steps we need to perform on the SkBitmap after 325 * we have rendered the GM into it. 326 * 327 * It's too bad that we are throwing away alpha channel data 328 * we could otherwise be examining, but this had always been happening 329 * before... it was buried within the compare() method at 330 * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 . 331 * 332 * Apparently we need this, at least for bitmaps that are either: 333 * (a) destined to be written out as PNG files, or 334 * (b) compared against bitmaps read in from PNG files 335 * for the reasons described just above the force_all_opaque() method. 336 * 337 * Neglecting to do this led to the difficult-to-diagnose 338 * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating 339 * spurious pixel_error messages as of r7258') 340 * 341 * TODO(epoger): Come up with a better solution that allows us to 342 * compare full pixel data, including alpha channel, while still being 343 * robust in the face of transformations to/from PNG files. 344 * Options include: 345 * 346 * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that 347 * will be written to, or compared against, PNG files. 348 * PRO: Preserve/compare alpha channel info for the non-PNG cases 349 * (comparing different renderModes in-memory) 350 * CON: The bitmaps (and checksums) for these non-PNG cases would be 351 * different than those for the PNG-compared cases, and in the 352 * case of a failed renderMode comparison, how would we write the 353 * image to disk for examination? 354 * 355 * 2. Always compute image checksums from PNG format (either 356 * directly from the the bytes of a PNG file, or capturing the 357 * bytes we would have written to disk if we were writing the 358 * bitmap out as a PNG). 359 * PRO: I think this would allow us to never force opaque, and to 360 * the extent that alpha channel data can be preserved in a PNG 361 * file, we could observe it. 362 * CON: If we read a bitmap from disk, we need to take its checksum 363 * from the source PNG (we can't compute it from the bitmap we 364 * read out of the PNG, because we will have already premultiplied 365 * the alpha). 366 * CON: Seems wasteful to convert a bitmap to PNG format just to take 367 * its checksum. (Although we're wasting lots of effort already 368 * calling force_all_opaque().) 369 * 370 * 3. Make the alpha premultiply/unpremultiply routines 100% consistent, 371 * so we can transform images back and forth without fear of off-by-one 372 * errors. 373 * CON: Math is hard. 374 * 375 * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each 376 * channel), rather than demanding absolute equality. 377 * CON: Can't do this with checksums. 378 */ 379 static void complete_bitmap(SkBitmap* bitmap) { 380 force_all_opaque(*bitmap); 381 } 382 383 static void installFilter(SkCanvas* canvas) { 384 if (gForceBWtext) { 385 canvas->setDrawFilter(new BWTextDrawFilter)->unref(); 386 } 387 } 388 389 static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred) { 390 SkAutoCanvasRestore acr(canvas, true); 391 392 if (!isPDF) { 393 canvas->concat(gm->getInitialTransform()); 394 } 395 installFilter(canvas); 396 gm->setCanvasIsDeferred(isDeferred); 397 gm->draw(canvas); 398 canvas->setDrawFilter(NULL); 399 } 400 401 static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec, 402 GrContext* context, 403 GrRenderTarget* rt, 404 SkBitmap* bitmap, 405 bool deferred) { 406 SkISize size (gm->getISize()); 407 setup_bitmap(gRec, size, bitmap); 408 409 SkAutoTUnref<SkCanvas> canvas; 410 411 if (gRec.fBackend == kRaster_Backend) { 412 SkAutoTUnref<SkDevice> device(new SkDevice(*bitmap)); 413 if (deferred) { 414 canvas.reset(new SkDeferredCanvas(device)); 415 } else { 416 canvas.reset(new SkCanvas(device)); 417 } 418 invokeGM(gm, canvas, false, deferred); 419 canvas->flush(); 420 } 421 #if SK_SUPPORT_GPU 422 else { // GPU 423 if (NULL == context) { 424 return ERROR_NO_GPU_CONTEXT; 425 } 426 SkAutoTUnref<SkDevice> device(new SkGpuDevice(context, rt)); 427 if (deferred) { 428 canvas.reset(new SkDeferredCanvas(device)); 429 } else { 430 canvas.reset(new SkCanvas(device)); 431 } 432 invokeGM(gm, canvas, false, deferred); 433 // the device is as large as the current rendertarget, so 434 // we explicitly only readback the amount we expect (in 435 // size) overwrite our previous allocation 436 bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth, 437 size.fHeight); 438 canvas->readPixels(bitmap, 0, 0); 439 } 440 #endif 441 complete_bitmap(bitmap); 442 return ERROR_NONE; 443 } 444 445 static void generate_image_from_picture(GM* gm, const ConfigData& gRec, 446 SkPicture* pict, SkBitmap* bitmap, 447 SkScalar scale = SK_Scalar1) { 448 SkISize size = gm->getISize(); 449 setup_bitmap(gRec, size, bitmap); 450 SkCanvas canvas(*bitmap); 451 installFilter(&canvas); 452 canvas.scale(scale, scale); 453 canvas.drawPicture(*pict); 454 complete_bitmap(bitmap); 455 } 456 457 static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) { 458 #ifdef SK_SUPPORT_PDF 459 SkMatrix initialTransform = gm->getInitialTransform(); 460 SkISize pageSize = gm->getISize(); 461 SkPDFDevice* dev = NULL; 462 if (initialTransform.isIdentity()) { 463 dev = new SkPDFDevice(pageSize, pageSize, initialTransform); 464 } else { 465 SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()), 466 SkIntToScalar(pageSize.height())); 467 initialTransform.mapRect(&content); 468 content.intersect(0, 0, SkIntToScalar(pageSize.width()), 469 SkIntToScalar(pageSize.height())); 470 SkISize contentSize = 471 SkISize::Make(SkScalarRoundToInt(content.width()), 472 SkScalarRoundToInt(content.height())); 473 dev = new SkPDFDevice(pageSize, contentSize, initialTransform); 474 } 475 SkAutoUnref aur(dev); 476 477 SkCanvas c(dev); 478 invokeGM(gm, &c, true, false); 479 480 SkPDFDocument doc; 481 doc.appendPage(dev); 482 doc.emitPDF(&pdf); 483 #endif 484 } 485 486 static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) { 487 #ifdef SK_SUPPORT_XPS 488 SkISize size = gm->getISize(); 489 490 SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()), 491 SkIntToScalar(size.height())); 492 static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254); 493 static const SkScalar upm = 72 * inchesPerMeter; 494 SkVector unitsPerMeter = SkPoint::Make(upm, upm); 495 static const SkScalar ppm = 200 * inchesPerMeter; 496 SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm); 497 498 SkXPSDevice* dev = new SkXPSDevice(); 499 SkAutoUnref aur(dev); 500 501 SkCanvas c(dev); 502 dev->beginPortfolio(&xps); 503 dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize); 504 invokeGM(gm, &c, false, false); 505 dev->endSheet(); 506 dev->endPortfolio(); 507 508 #endif 509 } 510 511 ErrorBitfield write_reference_image( 512 const ConfigData& gRec, const char writePath [], 513 const char renderModeDescriptor [], const SkString& name, 514 SkBitmap& bitmap, SkDynamicMemoryWStream* document) { 515 SkString path; 516 bool success = false; 517 if (gRec.fBackend == kRaster_Backend || 518 gRec.fBackend == kGPU_Backend || 519 (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) { 520 521 path = make_filename(writePath, renderModeDescriptor, name.c_str(), 522 "png"); 523 success = write_bitmap(path, bitmap); 524 } 525 if (kPDF_Backend == gRec.fBackend) { 526 path = make_filename(writePath, renderModeDescriptor, name.c_str(), 527 "pdf"); 528 success = write_document(path, *document); 529 } 530 if (kXPS_Backend == gRec.fBackend) { 531 path = make_filename(writePath, renderModeDescriptor, name.c_str(), 532 "xps"); 533 success = write_document(path, *document); 534 } 535 if (success) { 536 return ERROR_NONE; 537 } else { 538 fprintf(stderr, "FAILED to write %s\n", path.c_str()); 539 RecordError(ERROR_WRITING_REFERENCE_IMAGE, name, 540 renderModeDescriptor); 541 return ERROR_WRITING_REFERENCE_IMAGE; 542 } 543 } 544 545 /** 546 * Log more detail about the mistmatch between expectedBitmap and 547 * actualBitmap. 548 */ 549 void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap, 550 const char *testName) { 551 const int expectedWidth = expectedBitmap.width(); 552 const int expectedHeight = expectedBitmap.height(); 553 const int width = actualBitmap.width(); 554 const int height = actualBitmap.height(); 555 if ((expectedWidth != width) || (expectedHeight != height)) { 556 SkDebugf("---- %s: dimension mismatch -- expected [%d %d], actual [%d %d]\n", 557 testName, expectedWidth, expectedHeight, width, height); 558 return; 559 } 560 561 if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) || 562 (SkBitmap::kARGB_8888_Config != actualBitmap.config())) { 563 SkDebugf("---- %s: not computing max per-channel pixel mismatch because non-8888\n", 564 testName); 565 return; 566 } 567 568 SkAutoLockPixels alp0(expectedBitmap); 569 SkAutoLockPixels alp1(actualBitmap); 570 int errR = 0; 571 int errG = 0; 572 int errB = 0; 573 int errA = 0; 574 int differingPixels = 0; 575 576 for (int y = 0; y < height; ++y) { 577 const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y); 578 const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y); 579 for (int x = 0; x < width; ++x) { 580 SkPMColor expectedPixel = *expectedPixelPtr++; 581 SkPMColor actualPixel = *actualPixelPtr++; 582 if (expectedPixel != actualPixel) { 583 differingPixels++; 584 errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) - 585 (int)SkGetPackedR32(actualPixel))); 586 errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) - 587 (int)SkGetPackedG32(actualPixel))); 588 errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) - 589 (int)SkGetPackedB32(actualPixel))); 590 errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) - 591 (int)SkGetPackedA32(actualPixel))); 592 } 593 } 594 } 595 SkDebugf("---- %s: %d (of %d) differing pixels, max per-channel mismatch" 596 " R=%d G=%d B=%d A=%d\n", 597 testName, differingPixels, width*height, errR, errG, errB, errA); 598 } 599 600 /** 601 * Compares actual checksum to expectations. 602 * Returns ERROR_NONE if they match, or some particular error code otherwise 603 * 604 * If fMismatchPath has been set, and there are pixel diffs, then the 605 * actual bitmap will be written out to a file within fMismatchPath. 606 * 607 * @param expectations what expectations to compare actualBitmap against 608 * @param actualBitmap the image we actually generated 609 * @param baseNameString name of test without renderModeDescriptor added 610 * @param renderModeDescriptor e.g., "-rtree", "-deferred" 611 * @param addToJsonSummary whether to add these results (both actual and 612 * expected) to the JSON summary 613 * 614 * TODO: For now, addToJsonSummary is only set to true within 615 * compare_test_results_to_stored_expectations(), so results of our 616 * in-memory comparisons (Rtree vs regular, etc.) are not written to the 617 * JSON summary. We may wish to change that. 618 */ 619 ErrorBitfield compare_to_expectations(Expectations expectations, 620 const SkBitmap& actualBitmap, 621 const SkString& baseNameString, 622 const char renderModeDescriptor[], 623 bool addToJsonSummary=false) { 624 ErrorBitfield retval; 625 Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap); 626 SkString completeNameString = baseNameString; 627 completeNameString.append(renderModeDescriptor); 628 const char* completeName = completeNameString.c_str(); 629 630 if (expectations.empty()) { 631 retval = ERROR_READING_REFERENCE_IMAGE; 632 } else if (expectations.match(actualChecksum)) { 633 retval = ERROR_NONE; 634 } else { 635 retval = ERROR_IMAGE_MISMATCH; 636 637 // Write out the "actuals" for any mismatches, if we have 638 // been directed to do so. 639 if (fMismatchPath) { 640 SkString path = 641 make_filename(fMismatchPath, renderModeDescriptor, 642 baseNameString.c_str(), "png"); 643 write_bitmap(path, actualBitmap); 644 } 645 646 // If we have access to a single expected bitmap, log more 647 // detail about the mismatch. 648 const SkBitmap *expectedBitmapPtr = expectations.asBitmap(); 649 if (NULL != expectedBitmapPtr) { 650 report_bitmap_diffs(*expectedBitmapPtr, actualBitmap, completeName); 651 } 652 } 653 RecordError(retval, baseNameString, renderModeDescriptor); 654 655 if (addToJsonSummary) { 656 add_actual_results_to_json_summary(completeName, actualChecksum, 657 retval, 658 expectations.ignoreFailure()); 659 add_expected_results_to_json_summary(completeName, expectations); 660 } 661 662 return retval; 663 } 664 665 /** 666 * Add this result to the appropriate JSON collection of actual results, 667 * depending on status. 668 */ 669 void add_actual_results_to_json_summary(const char testName[], 670 Checksum actualChecksum, 671 ErrorBitfield result, 672 bool ignoreFailure) { 673 Json::Value actualResults; 674 actualResults[kJsonKey_ActualResults_AnyStatus_Checksum] = 675 asJsonValue(actualChecksum); 676 if (ERROR_NONE == result) { 677 this->fJsonActualResults_Succeeded[testName] = actualResults; 678 } else { 679 if (ignoreFailure) { 680 // TODO: Once we have added the ability to compare 681 // actual results against expectations in a JSON file 682 // (where we can set ignore-failure to either true or 683 // false), add test cases that exercise ignored 684 // failures (both for ERROR_READING_REFERENCE_IMAGE 685 // and ERROR_IMAGE_MISMATCH). 686 this->fJsonActualResults_FailureIgnored[testName] = 687 actualResults; 688 } else { 689 switch(result) { 690 case ERROR_READING_REFERENCE_IMAGE: 691 // TODO: What about the case where there IS an 692 // expected image checksum, but that gm test 693 // doesn't actually run? For now, those cases 694 // will always be ignored, because gm only looks 695 // at expectations that correspond to gm tests 696 // that were actually run. 697 // 698 // Once we have the ability to express 699 // expectations as a JSON file, we should fix this 700 // (and add a test case for which an expectation 701 // is given but the test is never run). 702 this->fJsonActualResults_NoComparison[testName] = 703 actualResults; 704 break; 705 case ERROR_IMAGE_MISMATCH: 706 this->fJsonActualResults_Failed[testName] = actualResults; 707 break; 708 default: 709 fprintf(stderr, "encountered unexpected result %d\n", 710 result); 711 SkDEBUGFAIL("encountered unexpected result"); 712 break; 713 } 714 } 715 } 716 } 717 718 /** 719 * Add this test to the JSON collection of expected results. 720 */ 721 void add_expected_results_to_json_summary(const char testName[], 722 Expectations expectations) { 723 // For now, we assume that this collection starts out empty and we 724 // just fill it in as we go; once gm accepts a JSON file as input, 725 // we'll have to change that. 726 Json::Value expectedResults; 727 expectedResults[kJsonKey_ExpectedResults_Checksums] = 728 expectations.allowedChecksumsAsJson(); 729 expectedResults[kJsonKey_ExpectedResults_IgnoreFailure] = 730 expectations.ignoreFailure(); 731 this->fJsonExpectedResults[testName] = expectedResults; 732 } 733 734 /** 735 * Compare actualBitmap to expectations stored in this->fExpectationsSource. 736 * 737 * @param gm which test generated the actualBitmap 738 * @param gRec 739 * @param writePath unless this is NULL, write out actual images into this 740 * directory 741 * @param actualBitmap bitmap generated by this run 742 * @param pdf 743 */ 744 ErrorBitfield compare_test_results_to_stored_expectations( 745 GM* gm, const ConfigData& gRec, const char writePath[], 746 SkBitmap& actualBitmap, SkDynamicMemoryWStream* pdf) { 747 748 SkString name = make_name(gm->shortName(), gRec.fName); 749 ErrorBitfield retval = ERROR_NONE; 750 751 ExpectationsSource *expectationsSource = 752 this->fExpectationsSource.get(); 753 if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { 754 /* 755 * Get the expected results for this test, as one or more allowed 756 * checksums. The current implementation of expectationsSource 757 * get this by computing the checksum of a single PNG file on disk. 758 * 759 * TODO(epoger): This relies on the fact that 760 * force_all_opaque() was called on the bitmap before it 761 * was written to disk as a PNG in the first place. If 762 * not, the checksum returned here may not match the 763 * checksum of actualBitmap, which *has* been run through 764 * force_all_opaque(). 765 * See comments above complete_bitmap() for more detail. 766 */ 767 Expectations expectations = expectationsSource->get(name.c_str()); 768 retval |= compare_to_expectations(expectations, actualBitmap, 769 name, "", true); 770 } else { 771 // If we are running without expectations, we still want to 772 // record the actual results. 773 Checksum actualChecksum = 774 SkBitmapChecksummer::Compute64(actualBitmap); 775 add_actual_results_to_json_summary(name.c_str(), actualChecksum, 776 ERROR_READING_REFERENCE_IMAGE, 777 false); 778 } 779 780 // TODO: Consider moving this into compare_to_expectations(), 781 // similar to fMismatchPath... for now, we don't do that, because 782 // we don't want to write out the actual bitmaps for all 783 // renderModes of all tests! That would be a lot of files. 784 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 785 retval |= write_reference_image(gRec, writePath, "", 786 name, actualBitmap, pdf); 787 } 788 789 return retval; 790 } 791 792 /** 793 * Compare actualBitmap to referenceBitmap. 794 * 795 * @param gm which test generated the bitmap 796 * @param gRec 797 * @param renderModeDescriptor 798 * @param actualBitmap actual bitmap generated by this run 799 * @param referenceBitmap bitmap we expected to be generated 800 */ 801 ErrorBitfield compare_test_results_to_reference_bitmap( 802 GM* gm, const ConfigData& gRec, const char renderModeDescriptor [], 803 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { 804 805 SkASSERT(referenceBitmap); 806 SkString name = make_name(gm->shortName(), gRec.fName); 807 Expectations expectations(*referenceBitmap); 808 return compare_to_expectations(expectations, actualBitmap, 809 name, renderModeDescriptor); 810 } 811 812 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags, 813 SkScalar scale = SK_Scalar1) { 814 // Pictures are refcounted so must be on heap 815 SkPicture* pict; 816 int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale)); 817 int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale)); 818 819 if (kTileGrid_BbhType == bbhType) { 820 pict = new SkTileGridPicture(16, 16, width, height); 821 } else { 822 pict = new SkPicture; 823 } 824 if (kNone_BbhType != bbhType) { 825 recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag; 826 } 827 SkCanvas* cv = pict->beginRecording(width, height, recordFlags); 828 cv->scale(scale, scale); 829 invokeGM(gm, cv, false, false); 830 pict->endRecording(); 831 832 return pict; 833 } 834 835 static SkPicture* stream_to_new_picture(const SkPicture& src) { 836 837 // To do in-memory commiunications with a stream, we need to: 838 // * create a dynamic memory stream 839 // * copy it into a buffer 840 // * create a read stream from it 841 // ?!?! 842 843 SkDynamicMemoryWStream storage; 844 src.serialize(&storage); 845 846 int streamSize = storage.getOffset(); 847 SkAutoMalloc dstStorage(streamSize); 848 void* dst = dstStorage.get(); 849 //char* dst = new char [streamSize]; 850 //@todo thudson 22 April 2011 when can we safely delete [] dst? 851 storage.copyTo(dst); 852 SkMemoryStream pictReadback(dst, streamSize); 853 SkPicture* retval = new SkPicture (&pictReadback); 854 return retval; 855 } 856 857 // Test: draw into a bitmap or pdf. 858 // Depending on flags, possibly compare to an expected image. 859 ErrorBitfield test_drawing(GM* gm, 860 const ConfigData& gRec, 861 const char writePath [], 862 GrContext* context, 863 GrRenderTarget* rt, 864 SkBitmap* bitmap) { 865 SkDynamicMemoryWStream document; 866 867 if (gRec.fBackend == kRaster_Backend || 868 gRec.fBackend == kGPU_Backend) { 869 // Early exit if we can't generate the image. 870 ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap, 871 false); 872 if (ERROR_NONE != errors) { 873 // TODO: Add a test to exercise what the stdout and 874 // JSON look like if we get an "early error" while 875 // trying to generate the image. 876 return errors; 877 } 878 } else if (gRec.fBackend == kPDF_Backend) { 879 generate_pdf(gm, document); 880 #if CAN_IMAGE_PDF 881 SkAutoDataUnref data(document.copyToData()); 882 SkMemoryStream stream(data->data(), data->size()); 883 SkPDFDocumentToBitmap(&stream, bitmap); 884 #endif 885 } else if (gRec.fBackend == kXPS_Backend) { 886 generate_xps(gm, document); 887 } 888 return compare_test_results_to_stored_expectations( 889 gm, gRec, writePath, *bitmap, &document); 890 } 891 892 ErrorBitfield test_deferred_drawing(GM* gm, 893 const ConfigData& gRec, 894 const SkBitmap& referenceBitmap, 895 GrContext* context, 896 GrRenderTarget* rt) { 897 SkDynamicMemoryWStream document; 898 899 if (gRec.fBackend == kRaster_Backend || 900 gRec.fBackend == kGPU_Backend) { 901 SkBitmap bitmap; 902 // Early exit if we can't generate the image, but this is 903 // expected in some cases, so don't report a test failure. 904 if (!generate_image(gm, gRec, context, rt, &bitmap, true)) { 905 return ERROR_NONE; 906 } 907 return compare_test_results_to_reference_bitmap( 908 gm, gRec, "-deferred", bitmap, &referenceBitmap); 909 } 910 return ERROR_NONE; 911 } 912 913 ErrorBitfield test_pipe_playback(GM* gm, 914 const ConfigData& gRec, 915 const SkBitmap& referenceBitmap) { 916 ErrorBitfield errors = ERROR_NONE; 917 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 918 SkBitmap bitmap; 919 SkISize size = gm->getISize(); 920 setup_bitmap(gRec, size, &bitmap); 921 SkCanvas canvas(bitmap); 922 PipeController pipeController(&canvas); 923 SkGPipeWriter writer; 924 SkCanvas* pipeCanvas = writer.startRecording( 925 &pipeController, gPipeWritingFlagCombos[i].flags); 926 invokeGM(gm, pipeCanvas, false, false); 927 complete_bitmap(&bitmap); 928 writer.endRecording(); 929 SkString string("-pipe"); 930 string.append(gPipeWritingFlagCombos[i].name); 931 errors |= compare_test_results_to_reference_bitmap( 932 gm, gRec, string.c_str(), bitmap, &referenceBitmap); 933 if (errors != ERROR_NONE) { 934 break; 935 } 936 } 937 return errors; 938 } 939 940 ErrorBitfield test_tiled_pipe_playback( 941 GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap) { 942 ErrorBitfield errors = ERROR_NONE; 943 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 944 SkBitmap bitmap; 945 SkISize size = gm->getISize(); 946 setup_bitmap(gRec, size, &bitmap); 947 SkCanvas canvas(bitmap); 948 TiledPipeController pipeController(bitmap); 949 SkGPipeWriter writer; 950 SkCanvas* pipeCanvas = writer.startRecording( 951 &pipeController, gPipeWritingFlagCombos[i].flags); 952 invokeGM(gm, pipeCanvas, false, false); 953 complete_bitmap(&bitmap); 954 writer.endRecording(); 955 SkString string("-tiled pipe"); 956 string.append(gPipeWritingFlagCombos[i].name); 957 errors |= compare_test_results_to_reference_bitmap( 958 gm, gRec, string.c_str(), bitmap, &referenceBitmap); 959 if (errors != ERROR_NONE) { 960 break; 961 } 962 } 963 return errors; 964 } 965 966 // 967 // member variables. 968 // They are public for now, to allow easier setting by tool_main(). 969 // 970 971 bool fUseFileHierarchy; 972 973 const char* fMismatchPath; 974 975 // information about all failed tests we have encountered so far 976 SkTArray<FailRec> fFailedTests; 977 978 // Where to read expectations (expected image checksums, etc.) from. 979 // If unset, we don't do comparisons. 980 SkAutoTUnref<ExpectationsSource> fExpectationsSource; 981 982 // JSON summaries that we generate as we go (just for output). 983 Json::Value fJsonExpectedResults; 984 Json::Value fJsonActualResults_Failed; 985 Json::Value fJsonActualResults_FailureIgnored; 986 Json::Value fJsonActualResults_NoComparison; 987 Json::Value fJsonActualResults_Succeeded; 988 989 }; // end of GMMain class definition 990 991 #if SK_SUPPORT_GPU 992 static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType; 993 #else 994 static const GLContextType kDontCare_GLContextType = 0; 995 #endif 996 997 // If the platform does not support writing PNGs of PDFs then there will be no 998 // reference images to read. However, we can always write the .pdf files 999 static const ConfigFlags kPDFConfigFlags = CAN_IMAGE_PDF ? kRW_ConfigFlag : 1000 kWrite_ConfigFlag; 1001 1002 static const ConfigData gRec[] = { 1003 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "8888" }, 1004 #if 0 // stop testing this (for now at least) since we want to remove support for it (soon please!!!) 1005 { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "4444" }, 1006 #endif 1007 { SkBitmap::kRGB_565_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "565" }, 1008 #if defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU 1009 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 0, kRW_ConfigFlag, "gpu" }, 1010 #ifndef SK_BUILD_FOR_ANDROID 1011 // currently we don't want to run MSAA tests on Android 1012 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag, "msaa16" }, 1013 #endif 1014 /* The debug context does not generate images */ 1015 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kDebug_GLContextType, 0, kNone_ConfigFlag, "debug" }, 1016 #if SK_ANGLE 1017 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 0, kRW_ConfigFlag, "angle" }, 1018 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 16, kRW_ConfigFlag, "anglemsaa16" }, 1019 #endif // SK_ANGLE 1020 #ifdef SK_MESA 1021 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kMESA_GLContextType, 0, kRW_ConfigFlag, "mesa" }, 1022 #endif // SK_MESA 1023 #endif // defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU 1024 #ifdef SK_SUPPORT_XPS 1025 /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */ 1026 { SkBitmap::kARGB_8888_Config, kXPS_Backend, kDontCare_GLContextType, 0, kWrite_ConfigFlag, "xps" }, 1027 #endif // SK_SUPPORT_XPS 1028 #ifdef SK_SUPPORT_PDF 1029 { SkBitmap::kARGB_8888_Config, kPDF_Backend, kDontCare_GLContextType, 0, kPDFConfigFlags, "pdf" }, 1030 #endif // SK_SUPPORT_PDF 1031 }; 1032 1033 static void usage(const char * argv0) { 1034 SkDebugf("%s\n", argv0); 1035 SkDebugf(" [--config "); 1036 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1037 if (i > 0) { 1038 SkDebugf("|"); 1039 } 1040 SkDebugf(gRec[i].fName); 1041 } 1042 SkDebugf("]:\n run these configurations\n"); 1043 SkDebugf( 1044 // Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). 1045 // It would probably be better if we allowed both yes-and-no settings for each 1046 // one, e.g.: 1047 // [--replay|--noreplay]: whether to exercise SkPicture replay; default is yes 1048 " [--nodeferred]: skip the deferred rendering test pass\n" 1049 " [--disable-missing-warning]: don't print a message to stderr if\n" 1050 " unable to read a reference image for any tests (NOT default behavior)\n" 1051 " [--enable-missing-warning]: print message to stderr (but don't fail) if\n" 1052 " unable to read a reference image for any tests (default behavior)\n" 1053 " [--exclude-config]: disable this config (may be used multiple times)\n" 1054 " [--forceBWtext]: disable text anti-aliasing\n" 1055 " [--help|-h]: show this help message\n" 1056 " [--hierarchy|--nohierarchy]: whether to use multilevel directory structure\n" 1057 " when reading/writing files; default is no\n" 1058 " [--match <substring>]: only run tests whose name includes this substring\n" 1059 " [--mismatchPath <path>]: write images for tests that failed due to\n" 1060 " pixel mismatched into this directory" 1061 " [--modulo <remainder> <divisor>]: only run tests for which \n" 1062 " testIndex %% divisor == remainder\n" 1063 " [--nopdf]: skip the pdf rendering test pass\n" 1064 " [--nopipe]: Skip SkGPipe replay\n" 1065 " [--readPath|-r <path>]: read reference images from this dir, and report\n" 1066 " any differences between those and the newly generated ones\n" 1067 " [--noreplay]: do not exercise SkPicture replay\n" 1068 " [--resourcePath|-i <path>]: directory that stores image resources\n" 1069 " [--nortree]: Do not exercise the R-Tree variant of SkPicture\n" 1070 " [--noserialize]: do not exercise SkPicture serialization & deserialization\n" 1071 " [--notexturecache]: disable the gpu texture cache\n" 1072 " [--tiledPipe]: Exercise tiled SkGPipe replay\n" 1073 " [--notileGrid]: Do not exercise the tile grid variant of SkPicture\n" 1074 " [--tileGridReplayScales <scales>]: Comma separated list of floating-point scale\n" 1075 " factors to be used for tileGrid playback testing. Default value: 1.0\n" 1076 " [--writeJsonSummary <path>]: write a JSON-formatted result summary to this file\n" 1077 " [--verbose] print diagnostics (e.g. list each config to be tested)\n" 1078 " [--writePath|-w <path>]: write rendered images into this directory\n" 1079 " [--writePicturePath|-wp <path>]: write .skp files into this directory\n" 1080 ); 1081 } 1082 1083 static int findConfig(const char config[]) { 1084 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { 1085 if (!strcmp(config, gRec[i].fName)) { 1086 return i; 1087 } 1088 } 1089 return -1; 1090 } 1091 1092 static bool skip_name(const SkTDArray<const char*> array, const char name[]) { 1093 if (0 == array.count()) { 1094 // no names, so don't skip anything 1095 return false; 1096 } 1097 for (int i = 0; i < array.count(); ++i) { 1098 if (strstr(name, array[i])) { 1099 // found the name, so don't skip 1100 return false; 1101 } 1102 } 1103 return true; 1104 } 1105 1106 namespace skiagm { 1107 #if SK_SUPPORT_GPU 1108 SkAutoTUnref<GrContext> gGrContext; 1109 /** 1110 * Sets the global GrContext, accessible by individual GMs 1111 */ 1112 static void SetGr(GrContext* grContext) { 1113 SkSafeRef(grContext); 1114 gGrContext.reset(grContext); 1115 } 1116 1117 /** 1118 * Gets the global GrContext, can be called by GM tests. 1119 */ 1120 GrContext* GetGr(); 1121 GrContext* GetGr() { 1122 return gGrContext.get(); 1123 } 1124 1125 /** 1126 * Sets the global GrContext and then resets it to its previous value at 1127 * destruction. 1128 */ 1129 class AutoResetGr : SkNoncopyable { 1130 public: 1131 AutoResetGr() : fOld(NULL) {} 1132 void set(GrContext* context) { 1133 SkASSERT(NULL == fOld); 1134 fOld = GetGr(); 1135 SkSafeRef(fOld); 1136 SetGr(context); 1137 } 1138 ~AutoResetGr() { SetGr(fOld); SkSafeUnref(fOld); } 1139 private: 1140 GrContext* fOld; 1141 }; 1142 #else 1143 GrContext* GetGr() { return NULL; } 1144 #endif 1145 } 1146 1147 template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { 1148 int index = array->find(value); 1149 if (index < 0) { 1150 *array->append() = value; 1151 } 1152 } 1153 1154 int tool_main(int argc, char** argv); 1155 int tool_main(int argc, char** argv) { 1156 1157 #if SK_ENABLE_INST_COUNT 1158 gPrintInstCount = true; 1159 #endif 1160 1161 SkGraphics::Init(); 1162 // we don't need to see this during a run 1163 gSkSuppressFontCachePurgeSpew = true; 1164 1165 setSystemPreferences(); 1166 GMMain gmmain; 1167 1168 const char* writeJsonSummaryPath = NULL;// if non-null, where we write the JSON summary 1169 const char* writePath = NULL; // if non-null, where we write the originals 1170 const char* writePicturePath = NULL; // if non-null, where we write serialized pictures 1171 const char* readPath = NULL; // if non-null, were we read from to compare 1172 const char* resourcePath = NULL;// if non-null, where we read from for image resources 1173 1174 // if true, emit a message when we can't find a reference image to compare 1175 bool notifyMissingReadReference = true; 1176 1177 SkTDArray<const char*> fMatches; 1178 1179 bool doPDF = true; 1180 bool doReplay = true; 1181 bool doPipe = true; 1182 bool doTiledPipe = false; 1183 bool doSerialize = true; 1184 bool doDeferred = true; 1185 bool doRTree = true; 1186 bool doTileGrid = true; 1187 bool doVerbose = false; 1188 bool disableTextureCache = false; 1189 SkTDArray<size_t> configs; 1190 SkTDArray<size_t> excludeConfigs; 1191 SkTDArray<SkScalar> tileGridReplayScales; 1192 *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0 1193 bool userConfig = false; 1194 1195 int moduloRemainder = -1; 1196 int moduloDivisor = -1; 1197 1198 const char* const commandName = argv[0]; 1199 char* const* stop = argv + argc; 1200 for (++argv; argv < stop; ++argv) { 1201 if (strcmp(*argv, "--config") == 0) { 1202 argv++; 1203 if (argv < stop) { 1204 int index = findConfig(*argv); 1205 if (index >= 0) { 1206 appendUnique<size_t>(&configs, index); 1207 userConfig = true; 1208 } else { 1209 SkString str; 1210 str.printf("unrecognized config %s\n", *argv); 1211 SkDebugf(str.c_str()); 1212 usage(commandName); 1213 return -1; 1214 } 1215 } else { 1216 SkDebugf("missing arg for --config\n"); 1217 usage(commandName); 1218 return -1; 1219 } 1220 } else if (strcmp(*argv, "--exclude-config") == 0) { 1221 argv++; 1222 if (argv < stop) { 1223 int index = findConfig(*argv); 1224 if (index >= 0) { 1225 *excludeConfigs.append() = index; 1226 } else { 1227 SkString str; 1228 str.printf("unrecognized exclude-config %s\n", *argv); 1229 SkDebugf(str.c_str()); 1230 usage(commandName); 1231 return -1; 1232 } 1233 } else { 1234 SkDebugf("missing arg for --exclude-config\n"); 1235 usage(commandName); 1236 return -1; 1237 } 1238 } else if (strcmp(*argv, "--nodeferred") == 0) { 1239 doDeferred = false; 1240 } else if (strcmp(*argv, "--disable-missing-warning") == 0) { 1241 notifyMissingReadReference = false; 1242 } else if (strcmp(*argv, "--mismatchPath") == 0) { 1243 argv++; 1244 if (argv < stop && **argv) { 1245 gmmain.fMismatchPath = *argv; 1246 } 1247 } else if (strcmp(*argv, "--nortree") == 0) { 1248 doRTree = false; 1249 } else if (strcmp(*argv, "--notileGrid") == 0) { 1250 doTileGrid = false; 1251 } else if (strcmp(*argv, "--tileGridReplayScales") == 0) { 1252 tileGridReplayScales.reset(); 1253 ++argv; 1254 if (argv < stop) { 1255 char* token = strtok(*argv, ","); 1256 while (NULL != token) { 1257 double val = atof(token); 1258 if (0 < val) { 1259 *tileGridReplayScales.append() = SkDoubleToScalar(val); 1260 } 1261 token = strtok(NULL, ","); 1262 } 1263 } 1264 if (0 == tileGridReplayScales.count()) { 1265 // Should have at least one scale 1266 usage(commandName); 1267 return -1; 1268 } 1269 } else if (strcmp(*argv, "--enable-missing-warning") == 0) { 1270 notifyMissingReadReference = true; 1271 } else if (strcmp(*argv, "--forceBWtext") == 0) { 1272 gForceBWtext = true; 1273 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { 1274 usage(commandName); 1275 return -1; 1276 } else if (strcmp(*argv, "--hierarchy") == 0) { 1277 gmmain.fUseFileHierarchy = true; 1278 } else if (strcmp(*argv, "--nohierarchy") == 0) { 1279 gmmain.fUseFileHierarchy = false; 1280 } else if (strcmp(*argv, "--match") == 0) { 1281 ++argv; 1282 if (argv < stop && **argv) { 1283 // just record the ptr, no need for a deep copy 1284 *fMatches.append() = *argv; 1285 } 1286 } else if (strcmp(*argv, "--modulo") == 0) { 1287 ++argv; 1288 if (argv >= stop) { 1289 continue; 1290 } 1291 moduloRemainder = atoi(*argv); 1292 1293 ++argv; 1294 if (argv >= stop) { 1295 continue; 1296 } 1297 moduloDivisor = atoi(*argv); 1298 if (moduloRemainder < 0 || moduloDivisor <= 0 || moduloRemainder >= moduloDivisor) { 1299 SkDebugf("invalid modulo values."); 1300 return -1; 1301 } 1302 } else if (strcmp(*argv, "--nopdf") == 0) { 1303 doPDF = false; 1304 } else if (strcmp(*argv, "--nopipe") == 0) { 1305 doPipe = false; 1306 } else if ((0 == strcmp(*argv, "--readPath")) || 1307 (0 == strcmp(*argv, "-r"))) { 1308 argv++; 1309 if (argv < stop && **argv) { 1310 readPath = *argv; 1311 } 1312 } else if (strcmp(*argv, "--noreplay") == 0) { 1313 doReplay = false; 1314 } else if ((0 == strcmp(*argv, "--resourcePath")) || 1315 (0 == strcmp(*argv, "-i"))) { 1316 argv++; 1317 if (argv < stop && **argv) { 1318 resourcePath = *argv; 1319 } 1320 } else if (strcmp(*argv, "--serialize") == 0) { 1321 doSerialize = true; 1322 } else if (strcmp(*argv, "--noserialize") == 0) { 1323 doSerialize = false; 1324 } else if (strcmp(*argv, "--notexturecache") == 0) { 1325 disableTextureCache = true; 1326 } else if (strcmp(*argv, "--tiledPipe") == 0) { 1327 doTiledPipe = true; 1328 } else if (!strcmp(*argv, "--verbose") || !strcmp(*argv, "-v")) { 1329 doVerbose = true; 1330 } else if ((0 == strcmp(*argv, "--writePath")) || 1331 (0 == strcmp(*argv, "-w"))) { 1332 argv++; 1333 if (argv < stop && **argv) { 1334 writePath = *argv; 1335 } 1336 } else if (0 == strcmp(*argv, "--writeJsonSummary")) { 1337 argv++; 1338 if (argv < stop && **argv) { 1339 writeJsonSummaryPath = *argv; 1340 } 1341 } else if ((0 == strcmp(*argv, "--writePicturePath")) || 1342 (0 == strcmp(*argv, "-wp"))) { 1343 argv++; 1344 if (argv < stop && **argv) { 1345 writePicturePath = *argv; 1346 } 1347 } else { 1348 usage(commandName); 1349 return -1; 1350 } 1351 } 1352 if (argv != stop) { 1353 usage(commandName); 1354 return -1; 1355 } 1356 1357 if (!userConfig) { 1358 // if no config is specified by user, we add them all. 1359 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1360 *configs.append() = i; 1361 } 1362 } 1363 // now remove any explicitly excluded configs 1364 for (int i = 0; i < excludeConfigs.count(); ++i) { 1365 int index = configs.find(excludeConfigs[i]); 1366 if (index >= 0) { 1367 configs.remove(index); 1368 // now assert that there was only one copy in configs[] 1369 SkASSERT(configs.find(excludeConfigs[i]) < 0); 1370 } 1371 } 1372 1373 if (doVerbose) { 1374 SkString str; 1375 str.printf("gm: %d configs:", configs.count()); 1376 for (int i = 0; i < configs.count(); ++i) { 1377 str.appendf(" %s", gRec[configs[i]].fName); 1378 } 1379 SkDebugf("%s\n", str.c_str()); 1380 } 1381 1382 GM::SetResourcePath(resourcePath); 1383 1384 if (readPath) { 1385 if (!sk_exists(readPath)) { 1386 fprintf(stderr, "readPath %s does not exist!\n", readPath); 1387 return -1; 1388 } 1389 if (sk_isdir(readPath)) { 1390 fprintf(stderr, "reading from %s\n", readPath); 1391 gmmain.fExpectationsSource.reset(SkNEW_ARGS( 1392 IndividualImageExpectationsSource, 1393 (readPath, notifyMissingReadReference))); 1394 } else { 1395 fprintf(stderr, "reading expectations from JSON summary file %s ", 1396 readPath); 1397 fprintf(stderr, "BUT WE DON'T KNOW HOW TO DO THIS YET!\n"); 1398 return -1; 1399 } 1400 } 1401 if (writePath) { 1402 fprintf(stderr, "writing to %s\n", writePath); 1403 } 1404 if (writePicturePath) { 1405 fprintf(stderr, "writing pictures to %s\n", writePicturePath); 1406 } 1407 if (resourcePath) { 1408 fprintf(stderr, "reading resources from %s\n", resourcePath); 1409 } 1410 1411 if (moduloDivisor <= 0) { 1412 moduloRemainder = -1; 1413 } 1414 if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) { 1415 moduloRemainder = -1; 1416 } 1417 1418 // Accumulate success of all tests. 1419 int testsRun = 0; 1420 int testsPassed = 0; 1421 int testsFailed = 0; 1422 int testsMissingReferenceImages = 0; 1423 1424 #if SK_SUPPORT_GPU 1425 GrContextFactory* grFactory = new GrContextFactory; 1426 if (disableTextureCache) { 1427 skiagm::GetGr()->setTextureCacheLimits(0, 0); 1428 } 1429 #endif 1430 1431 int gmIndex = -1; 1432 SkString moduloStr; 1433 1434 // If we will be writing out files, prepare subdirectories. 1435 if (writePath) { 1436 if (!sk_mkdir(writePath)) { 1437 return -1; 1438 } 1439 if (gmmain.fUseFileHierarchy) { 1440 for (int i = 0; i < configs.count(); i++) { 1441 ConfigData config = gRec[configs[i]]; 1442 SkString subdir; 1443 subdir.appendf("%s%c%s", writePath, SkPATH_SEPARATOR, 1444 config.fName); 1445 if (!sk_mkdir(subdir.c_str())) { 1446 return -1; 1447 } 1448 } 1449 } 1450 } 1451 1452 Iter iter; 1453 GM* gm; 1454 while ((gm = iter.next()) != NULL) { 1455 1456 ++gmIndex; 1457 if (moduloRemainder >= 0) { 1458 if ((gmIndex % moduloDivisor) != moduloRemainder) { 1459 continue; 1460 } 1461 moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor); 1462 } 1463 1464 const char* shortName = gm->shortName(); 1465 if (skip_name(fMatches, shortName)) { 1466 SkDELETE(gm); 1467 continue; 1468 } 1469 1470 SkISize size = gm->getISize(); 1471 SkDebugf("%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName, 1472 size.width(), size.height()); 1473 1474 ErrorBitfield testErrors = ERROR_NONE; 1475 uint32_t gmFlags = gm->getFlags(); 1476 1477 for (int i = 0; i < configs.count(); i++) { 1478 ConfigData config = gRec[configs[i]]; 1479 1480 // Skip any tests that we don't even need to try. 1481 if ((kPDF_Backend == config.fBackend) && 1482 (!doPDF || (gmFlags & GM::kSkipPDF_Flag))) 1483 { 1484 continue; 1485 } 1486 if ((gmFlags & GM::kSkip565_Flag) && 1487 (kRaster_Backend == config.fBackend) && 1488 (SkBitmap::kRGB_565_Config == config.fConfig)) { 1489 continue; 1490 } 1491 1492 // Now we know that we want to run this test and record its 1493 // success or failure. 1494 ErrorBitfield renderErrors = ERROR_NONE; 1495 GrRenderTarget* renderTarget = NULL; 1496 #if SK_SUPPORT_GPU 1497 SkAutoTUnref<GrRenderTarget> rt; 1498 AutoResetGr autogr; 1499 if ((ERROR_NONE == renderErrors) && 1500 kGPU_Backend == config.fBackend) { 1501 GrContext* gr = grFactory->get(config.fGLContextType); 1502 bool grSuccess = false; 1503 if (gr) { 1504 // create a render target to back the device 1505 GrTextureDesc desc; 1506 desc.fConfig = kSkia8888_PM_GrPixelConfig; 1507 desc.fFlags = kRenderTarget_GrTextureFlagBit; 1508 desc.fWidth = gm->getISize().width(); 1509 desc.fHeight = gm->getISize().height(); 1510 desc.fSampleCnt = config.fSampleCnt; 1511 GrTexture* tex = gr->createUncachedTexture(desc, NULL, 0); 1512 if (tex) { 1513 rt.reset(tex->asRenderTarget()); 1514 rt.get()->ref(); 1515 tex->unref(); 1516 autogr.set(gr); 1517 renderTarget = rt.get(); 1518 grSuccess = NULL != renderTarget; 1519 } 1520 } 1521 if (!grSuccess) { 1522 renderErrors |= ERROR_NO_GPU_CONTEXT; 1523 } 1524 } 1525 #endif 1526 1527 SkBitmap comparisonBitmap; 1528 1529 if (ERROR_NONE == renderErrors) { 1530 renderErrors |= gmmain.test_drawing(gm, config, writePath, 1531 GetGr(), 1532 renderTarget, 1533 &comparisonBitmap); 1534 } 1535 1536 if (doDeferred && !renderErrors && 1537 (kGPU_Backend == config.fBackend || 1538 kRaster_Backend == config.fBackend)) { 1539 renderErrors |= gmmain.test_deferred_drawing(gm, config, 1540 comparisonBitmap, 1541 GetGr(), 1542 renderTarget); 1543 } 1544 1545 testErrors |= renderErrors; 1546 } 1547 1548 SkBitmap comparisonBitmap; 1549 const ConfigData compareConfig = 1550 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison" }; 1551 testErrors |= gmmain.generate_image(gm, compareConfig, NULL, NULL, &comparisonBitmap, false); 1552 1553 // run the picture centric GM steps 1554 if (!(gmFlags & GM::kSkipPicture_Flag)) { 1555 1556 ErrorBitfield pictErrors = ERROR_NONE; 1557 1558 //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm)); 1559 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0); 1560 SkAutoUnref aur(pict); 1561 1562 if ((ERROR_NONE == testErrors) && doReplay) { 1563 SkBitmap bitmap; 1564 gmmain.generate_image_from_picture(gm, compareConfig, pict, 1565 &bitmap); 1566 pictErrors |= gmmain.compare_test_results_to_reference_bitmap( 1567 gm, compareConfig, "-replay", bitmap, &comparisonBitmap); 1568 } 1569 1570 if ((ERROR_NONE == testErrors) && 1571 (ERROR_NONE == pictErrors) && 1572 doSerialize) { 1573 SkPicture* repict = gmmain.stream_to_new_picture(*pict); 1574 SkAutoUnref aurr(repict); 1575 1576 SkBitmap bitmap; 1577 gmmain.generate_image_from_picture(gm, compareConfig, repict, 1578 &bitmap); 1579 pictErrors |= gmmain.compare_test_results_to_reference_bitmap( 1580 gm, compareConfig, "-serialize", bitmap, &comparisonBitmap); 1581 } 1582 1583 if (writePicturePath) { 1584 const char* pictureSuffix = "skp"; 1585 SkString path = make_filename(writePicturePath, "", 1586 gm->shortName(), 1587 pictureSuffix); 1588 SkFILEWStream stream(path.c_str()); 1589 pict->serialize(&stream); 1590 } 1591 1592 testErrors |= pictErrors; 1593 } 1594 1595 // TODO: add a test in which the RTree rendering results in a 1596 // different bitmap than the standard rendering. It should 1597 // show up as failed in the JSON summary, and should be listed 1598 // in the stdout also. 1599 if (!(gmFlags & GM::kSkipPicture_Flag) && doRTree) { 1600 SkPicture* pict = gmmain.generate_new_picture( 1601 gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag); 1602 SkAutoUnref aur(pict); 1603 SkBitmap bitmap; 1604 gmmain.generate_image_from_picture(gm, compareConfig, pict, 1605 &bitmap); 1606 testErrors |= gmmain.compare_test_results_to_reference_bitmap( 1607 gm, compareConfig, "-rtree", bitmap, &comparisonBitmap); 1608 } 1609 1610 if (!(gmFlags & GM::kSkipPicture_Flag) && doTileGrid) { 1611 for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) { 1612 SkScalar replayScale = tileGridReplayScales[scaleIndex]; 1613 if ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1) 1614 continue; 1615 // We record with the reciprocal scale to obtain a replay 1616 // result that can be validated against comparisonBitmap. 1617 SkScalar recordScale = SkScalarInvert(replayScale); 1618 SkPicture* pict = gmmain.generate_new_picture( 1619 gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag, 1620 recordScale); 1621 SkAutoUnref aur(pict); 1622 SkBitmap bitmap; 1623 gmmain.generate_image_from_picture(gm, compareConfig, pict, 1624 &bitmap, replayScale); 1625 SkString suffix("-tilegrid"); 1626 if (SK_Scalar1 != replayScale) { 1627 suffix += "-scale-"; 1628 suffix.appendScalar(replayScale); 1629 } 1630 testErrors |= gmmain.compare_test_results_to_reference_bitmap( 1631 gm, compareConfig, suffix.c_str(), bitmap, 1632 &comparisonBitmap); 1633 } 1634 } 1635 1636 // run the pipe centric GM steps 1637 if (!(gmFlags & GM::kSkipPipe_Flag)) { 1638 1639 ErrorBitfield pipeErrors = ERROR_NONE; 1640 1641 if ((ERROR_NONE == testErrors) && doPipe) { 1642 pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig, 1643 comparisonBitmap); 1644 } 1645 1646 if ((ERROR_NONE == testErrors) && 1647 (ERROR_NONE == pipeErrors) && 1648 doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) { 1649 pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig, 1650 comparisonBitmap); 1651 } 1652 1653 testErrors |= pipeErrors; 1654 } 1655 1656 // Update overall results. 1657 // We only tabulate the particular error types that we currently 1658 // care about (e.g., missing reference images). Later on, if we 1659 // want to also tabulate other error types, we can do so. 1660 testsRun++; 1661 if (!gmmain.fExpectationsSource.get() || 1662 (ERROR_READING_REFERENCE_IMAGE & testErrors)) { 1663 testsMissingReferenceImages++; 1664 } else if (ERROR_NONE == testErrors) { 1665 testsPassed++; 1666 } else { 1667 testsFailed++; 1668 } 1669 1670 SkDELETE(gm); 1671 } 1672 SkDebugf("Ran %d tests: %d passed, %d failed, %d missing reference images\n", 1673 testsRun, testsPassed, testsFailed, testsMissingReferenceImages); 1674 gmmain.ListErrors(); 1675 1676 if (NULL != writeJsonSummaryPath) { 1677 Json::Value actualResults; 1678 actualResults[kJsonKey_ActualResults_Failed] = 1679 gmmain.fJsonActualResults_Failed; 1680 actualResults[kJsonKey_ActualResults_FailureIgnored] = 1681 gmmain.fJsonActualResults_FailureIgnored; 1682 actualResults[kJsonKey_ActualResults_NoComparison] = 1683 gmmain.fJsonActualResults_NoComparison; 1684 actualResults[kJsonKey_ActualResults_Succeeded] = 1685 gmmain.fJsonActualResults_Succeeded; 1686 Json::Value root; 1687 root[kJsonKey_ActualResults] = actualResults; 1688 root[kJsonKey_ExpectedResults] = gmmain.fJsonExpectedResults; 1689 std::string jsonStdString = root.toStyledString(); 1690 SkFILEWStream stream(writeJsonSummaryPath); 1691 stream.write(jsonStdString.c_str(), jsonStdString.length()); 1692 } 1693 1694 #if SK_SUPPORT_GPU 1695 1696 #if GR_CACHE_STATS 1697 for (int i = 0; i < configs.count(); i++) { 1698 ConfigData config = gRec[configs[i]]; 1699 1700 if (kGPU_Backend == config.fBackend) { 1701 GrContext* gr = grFactory->get(config.fGLContextType); 1702 1703 SkDebugf("config: %s %x\n", config.fName, gr); 1704 gr->printCacheStats(); 1705 } 1706 } 1707 #endif 1708 1709 delete grFactory; 1710 #endif 1711 SkGraphics::Term(); 1712 1713 return (0 == testsFailed) ? 0 : -1; 1714 } 1715 1716 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 1717 int main(int argc, char * const argv[]) { 1718 return tool_main(argc, (char**) argv); 1719 } 1720 #endif 1721