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_error.h" 18#include "gm_expectations.h" 19#include "system_preferences.h" 20#include "SkBitmap.h" 21#include "SkColorPriv.h" 22#include "SkCommandLineFlags.h" 23#include "SkData.h" 24#include "SkDeferredCanvas.h" 25#include "SkDevice.h" 26#include "SkDocument.h" 27#include "SkDrawFilter.h" 28#include "SkForceLinking.h" 29#include "SkGPipe.h" 30#include "SkGraphics.h" 31#include "SkImageDecoder.h" 32#include "SkImageEncoder.h" 33#include "SkOSFile.h" 34#include "SkPicture.h" 35#include "SkRefCnt.h" 36#include "SkScalar.h" 37#include "SkStream.h" 38#include "SkTArray.h" 39#include "SkTDict.h" 40#include "SkTileGridPicture.h" 41#include "SamplePipeControllers.h" 42 43__SK_FORCE_IMAGE_DECODER_LINKING; 44 45#ifdef SK_BUILD_FOR_WIN 46 // json includes xlocale which generates warning 4530 because we're compiling without 47 // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067 48 #pragma warning(push) 49 #pragma warning(disable : 4530) 50#endif 51#include "json/value.h" 52#ifdef SK_BUILD_FOR_WIN 53 #pragma warning(pop) 54#endif 55 56#if SK_SUPPORT_GPU 57#include "GrContextFactory.h" 58#include "SkGpuDevice.h" 59typedef GrContextFactory::GLContextType GLContextType; 60#define DEFAULT_CACHE_VALUE -1 61static int gGpuCacheSizeBytes; 62static int gGpuCacheSizeCount; 63#else 64class GrContextFactory; 65class GrContext; 66class GrSurface; 67typedef int GLContextType; 68#endif 69 70#define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") 71 72extern bool gSkSuppressFontCachePurgeSpew; 73DECLARE_bool(useDocumentInsteadOfDevice); 74 75#ifdef SK_SUPPORT_PDF 76 #include "SkPDFDevice.h" 77 #include "SkPDFDocument.h" 78#endif 79 80// Until we resolve http://code.google.com/p/skia/issues/detail?id=455 , 81// stop writing out XPS-format image baselines in gm. 82#undef SK_SUPPORT_XPS 83#ifdef SK_SUPPORT_XPS 84 #include "SkXPSDevice.h" 85#endif 86 87#ifdef SK_BUILD_FOR_MAC 88 #include "SkCGUtils.h" 89 #define CAN_IMAGE_PDF 1 90#else 91 #define CAN_IMAGE_PDF 0 92#endif 93 94using namespace skiagm; 95 96class Iter { 97public: 98 Iter() { 99 this->reset(); 100 } 101 102 void reset() { 103 fReg = GMRegistry::Head(); 104 } 105 106 GM* next() { 107 if (fReg) { 108 GMRegistry::Factory fact = fReg->factory(); 109 fReg = fReg->next(); 110 return fact(0); 111 } 112 return NULL; 113 } 114 115 static int Count() { 116 const GMRegistry* reg = GMRegistry::Head(); 117 int count = 0; 118 while (reg) { 119 count += 1; 120 reg = reg->next(); 121 } 122 return count; 123 } 124 125private: 126 const GMRegistry* fReg; 127}; 128 129// TODO(epoger): Right now, various places in this code assume that all the 130// image files read/written by GM use this file extension. 131// Search for references to this constant to find these assumptions. 132const static char kPNG_FileExtension[] = "png"; 133 134enum Backend { 135 kRaster_Backend, 136 kGPU_Backend, 137 kPDF_Backend, 138 kXPS_Backend, 139}; 140 141enum BbhType { 142 kNone_BbhType, 143 kRTree_BbhType, 144 kTileGrid_BbhType, 145}; 146 147enum ConfigFlags { 148 kNone_ConfigFlag = 0x0, 149 /* Write GM images if a write path is provided. */ 150 kWrite_ConfigFlag = 0x1, 151 /* Read reference GM images if a read path is provided. */ 152 kRead_ConfigFlag = 0x2, 153 kRW_ConfigFlag = (kWrite_ConfigFlag | kRead_ConfigFlag), 154}; 155 156struct ConfigData { 157 SkBitmap::Config fConfig; 158 Backend fBackend; 159 GLContextType fGLContextType; // GPU backend only 160 int fSampleCnt; // GPU backend only 161 ConfigFlags fFlags; 162 const char* fName; 163 bool fRunByDefault; 164}; 165 166class BWTextDrawFilter : public SkDrawFilter { 167public: 168 virtual bool filter(SkPaint*, Type) SK_OVERRIDE; 169}; 170bool BWTextDrawFilter::filter(SkPaint* p, Type t) { 171 if (kText_Type == t) { 172 p->setAntiAlias(false); 173 } 174 return true; 175} 176 177struct PipeFlagComboData { 178 const char* name; 179 uint32_t flags; 180}; 181 182static PipeFlagComboData gPipeWritingFlagCombos[] = { 183 { "", 0 }, 184 { " cross-process", SkGPipeWriter::kCrossProcess_Flag }, 185 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag 186 | SkGPipeWriter::kSharedAddressSpace_Flag } 187}; 188 189static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect); 190 191const static ErrorCombination kDefaultIgnorableErrorTypes = ErrorCombination() 192 .plus(kMissingExpectations_ErrorType) 193 .plus(kIntentionallySkipped_ErrorType); 194 195class GMMain { 196public: 197 GMMain() : fUseFileHierarchy(false), fWriteChecksumBasedFilenames(false), 198 fIgnorableErrorTypes(kDefaultIgnorableErrorTypes), 199 fMismatchPath(NULL), fMissingExpectationsPath(NULL), fTestsRun(0), 200 fRenderModesEncountered(1) {} 201 202 /** 203 * Assemble shortNamePlusConfig from (surprise!) shortName and configName. 204 * 205 * The method for doing so depends on whether we are using hierarchical naming. 206 * For example, shortName "selftest1" and configName "8888" could be assembled into 207 * either "selftest1_8888" or "8888/selftest1". 208 */ 209 SkString make_shortname_plus_config(const char *shortName, const char *configName) { 210 SkString name; 211 if (0 == strlen(configName)) { 212 name.append(shortName); 213 } else if (fUseFileHierarchy) { 214 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName); 215 } else { 216 name.appendf("%s_%s", shortName, configName); 217 } 218 return name; 219 } 220 221 /** 222 * Assemble filename, suitable for writing out the results of a particular test. 223 */ 224 SkString make_filename(const char *path, 225 const char *shortName, 226 const char *configName, 227 const char *renderModeDescriptor, 228 const char *suffix) { 229 SkString filename = make_shortname_plus_config(shortName, configName); 230 filename.append(renderModeDescriptor); 231 filename.appendUnichar('.'); 232 filename.append(suffix); 233 return SkOSPath::SkPathJoin(path, filename.c_str()); 234 } 235 236 /** 237 * Assemble filename suitable for writing out an SkBitmap. 238 */ 239 SkString make_bitmap_filename(const char *path, 240 const char *shortName, 241 const char *configName, 242 const char *renderModeDescriptor, 243 const GmResultDigest &bitmapDigest) { 244 if (fWriteChecksumBasedFilenames) { 245 SkString filename; 246 filename.append(bitmapDigest.getHashType()); 247 filename.appendUnichar('_'); 248 filename.append(shortName); 249 filename.appendUnichar('_'); 250 filename.append(bitmapDigest.getDigestValue()); 251 filename.appendUnichar('.'); 252 filename.append(kPNG_FileExtension); 253 return SkOSPath::SkPathJoin(path, filename.c_str()); 254 } else { 255 return make_filename(path, shortName, configName, renderModeDescriptor, 256 kPNG_FileExtension); 257 } 258 } 259 260 /* since PNG insists on unpremultiplying our alpha, we take no 261 precision chances and force all pixels to be 100% opaque, 262 otherwise on compare we may not get a perfect match. 263 */ 264 static void force_all_opaque(const SkBitmap& bitmap) { 265 SkBitmap::Config config = bitmap.config(); 266 switch (config) { 267 case SkBitmap::kARGB_8888_Config: 268 force_all_opaque_8888(bitmap); 269 break; 270 case SkBitmap::kRGB_565_Config: 271 // nothing to do here; 565 bitmaps are inherently opaque 272 break; 273 default: 274 gm_fprintf(stderr, "unsupported bitmap config %d\n", config); 275 DEBUGFAIL_SEE_STDERR; 276 } 277 } 278 279 static void force_all_opaque_8888(const SkBitmap& bitmap) { 280 SkAutoLockPixels lock(bitmap); 281 for (int y = 0; y < bitmap.height(); y++) { 282 for (int x = 0; x < bitmap.width(); x++) { 283 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); 284 } 285 } 286 } 287 288 static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) { 289 // TODO(epoger): Now that we have removed force_all_opaque() 290 // from this method, we should be able to get rid of the 291 // transformation to 8888 format also. 292 SkBitmap copy; 293 bitmap.copyTo(©, SkBitmap::kARGB_8888_Config); 294 return SkImageEncoder::EncodeFile(path.c_str(), copy, 295 SkImageEncoder::kPNG_Type, 100); 296 } 297 298 /** 299 * Add all render modes encountered thus far to the "modes" array. 300 */ 301 void GetRenderModesEncountered(SkTArray<SkString> &modes) { 302 SkTDict<int>::Iter iter(this->fRenderModesEncountered); 303 const char* mode; 304 while ((mode = iter.next(NULL)) != NULL) { 305 SkString modeAsString = SkString(mode); 306 // TODO(epoger): It seems a bit silly that all of these modes were 307 // recorded with a leading "-" which we have to remove here 308 // (except for mode "", which means plain old original mode). 309 // But that's how renderModeDescriptor has been passed into 310 // compare_test_results_to_reference_bitmap() historically, 311 // and changing that now may affect other parts of our code. 312 if (modeAsString.startsWith("-")) { 313 modeAsString.remove(0, 1); 314 modes.push_back(modeAsString); 315 } 316 } 317 } 318 319 /** 320 * Records the results of this test in fTestsRun and fFailedTests. 321 * 322 * We even record successes, and errors that we regard as 323 * "ignorable"; we can filter them out later. 324 */ 325 void RecordTestResults(const ErrorCombination& errorCombination, 326 const SkString& shortNamePlusConfig, 327 const char renderModeDescriptor []) { 328 // Things to do regardless of errorCombination. 329 fTestsRun++; 330 int renderModeCount = 0; 331 this->fRenderModesEncountered.find(renderModeDescriptor, &renderModeCount); 332 renderModeCount++; 333 this->fRenderModesEncountered.set(renderModeDescriptor, renderModeCount); 334 335 if (errorCombination.isEmpty()) { 336 return; 337 } 338 339 // Things to do only if there is some error condition. 340 SkString fullName = shortNamePlusConfig; 341 fullName.append(renderModeDescriptor); 342 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 343 ErrorType type = static_cast<ErrorType>(typeInt); 344 if (errorCombination.includes(type)) { 345 fFailedTests[type].push_back(fullName); 346 } 347 } 348 } 349 350 /** 351 * Return the number of significant (non-ignorable) errors we have 352 * encountered so far. 353 */ 354 int NumSignificantErrors() { 355 int significantErrors = 0; 356 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 357 ErrorType type = static_cast<ErrorType>(typeInt); 358 if (!fIgnorableErrorTypes.includes(type)) { 359 significantErrors += fFailedTests[type].count(); 360 } 361 } 362 return significantErrors; 363 } 364 365 /** 366 * Display the summary of results with this ErrorType. 367 * 368 * @param type which ErrorType 369 * @param verbose whether to be all verbose about it 370 */ 371 void DisplayResultTypeSummary(ErrorType type, bool verbose) { 372 bool isIgnorableType = fIgnorableErrorTypes.includes(type); 373 374 SkString line; 375 if (isIgnorableType) { 376 line.append("[ ] "); 377 } else { 378 line.append("[*] "); 379 } 380 381 SkTArray<SkString> *failedTestsOfThisType = &fFailedTests[type]; 382 int count = failedTestsOfThisType->count(); 383 line.appendf("%d %s", count, getErrorTypeName(type)); 384 if (!isIgnorableType || verbose) { 385 line.append(":"); 386 for (int i = 0; i < count; ++i) { 387 line.append(" "); 388 line.append((*failedTestsOfThisType)[i]); 389 } 390 } 391 gm_fprintf(stdout, "%s\n", line.c_str()); 392 } 393 394 /** 395 * List contents of fFailedTests to stdout. 396 * 397 * @param verbose whether to be all verbose about it 398 */ 399 void ListErrors(bool verbose) { 400 // First, print a single summary line. 401 SkString summary; 402 summary.appendf("Ran %d tests:", fTestsRun); 403 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 404 ErrorType type = static_cast<ErrorType>(typeInt); 405 summary.appendf(" %s=%d", getErrorTypeName(type), fFailedTests[type].count()); 406 } 407 gm_fprintf(stdout, "%s\n", summary.c_str()); 408 409 // Now, for each failure type, list the tests that failed that way. 410 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { 411 this->DisplayResultTypeSummary(static_cast<ErrorType>(typeInt), verbose); 412 } 413 gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n"); 414 } 415 416 static bool write_document(const SkString& path, SkStreamAsset* asset) { 417 SkFILEWStream stream(path.c_str()); 418 return stream.writeStream(asset, asset->getLength()); 419 } 420 421 /** 422 * Prepare an SkBitmap to render a GM into. 423 * 424 * After you've rendered the GM into the SkBitmap, you must call 425 * complete_bitmap()! 426 * 427 * @todo thudson 22 April 2011 - could refactor this to take in 428 * a factory to generate the context, always call readPixels() 429 * (logically a noop for rasters, if wasted time), and thus collapse the 430 * GPU special case and also let this be used for SkPicture testing. 431 */ 432 static void setup_bitmap(const ConfigData& gRec, SkISize& size, 433 SkBitmap* bitmap) { 434 bitmap->setConfig(gRec.fConfig, size.width(), size.height()); 435 bitmap->allocPixels(); 436 bitmap->eraseColor(SK_ColorTRANSPARENT); 437 } 438 439 /** 440 * Any finalization steps we need to perform on the SkBitmap after 441 * we have rendered the GM into it. 442 * 443 * It's too bad that we are throwing away alpha channel data 444 * we could otherwise be examining, but this had always been happening 445 * before... it was buried within the compare() method at 446 * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 . 447 * 448 * Apparently we need this, at least for bitmaps that are either: 449 * (a) destined to be written out as PNG files, or 450 * (b) compared against bitmaps read in from PNG files 451 * for the reasons described just above the force_all_opaque() method. 452 * 453 * Neglecting to do this led to the difficult-to-diagnose 454 * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating 455 * spurious pixel_error messages as of r7258') 456 * 457 * TODO(epoger): Come up with a better solution that allows us to 458 * compare full pixel data, including alpha channel, while still being 459 * robust in the face of transformations to/from PNG files. 460 * Options include: 461 * 462 * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that 463 * will be written to, or compared against, PNG files. 464 * PRO: Preserve/compare alpha channel info for the non-PNG cases 465 * (comparing different renderModes in-memory) 466 * CON: The bitmaps (and hash digests) for these non-PNG cases would be 467 * different than those for the PNG-compared cases, and in the 468 * case of a failed renderMode comparison, how would we write the 469 * image to disk for examination? 470 * 471 * 2. Always compute image hash digests from PNG format (either 472 * directly from the the bytes of a PNG file, or capturing the 473 * bytes we would have written to disk if we were writing the 474 * bitmap out as a PNG). 475 * PRO: I think this would allow us to never force opaque, and to 476 * the extent that alpha channel data can be preserved in a PNG 477 * file, we could observe it. 478 * CON: If we read a bitmap from disk, we need to take its hash digest 479 * from the source PNG (we can't compute it from the bitmap we 480 * read out of the PNG, because we will have already premultiplied 481 * the alpha). 482 * CON: Seems wasteful to convert a bitmap to PNG format just to take 483 * its hash digest. (Although we're wasting lots of effort already 484 * calling force_all_opaque().) 485 * 486 * 3. Make the alpha premultiply/unpremultiply routines 100% consistent, 487 * so we can transform images back and forth without fear of off-by-one 488 * errors. 489 * CON: Math is hard. 490 * 491 * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each 492 * channel), rather than demanding absolute equality. 493 * CON: Can't do this with hash digests. 494 */ 495 static void complete_bitmap(SkBitmap* bitmap) { 496 force_all_opaque(*bitmap); 497 } 498 499 static void installFilter(SkCanvas* canvas); 500 501 static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred) { 502 SkAutoCanvasRestore acr(canvas, true); 503 504 if (!isPDF) { 505 canvas->concat(gm->getInitialTransform()); 506 } 507 installFilter(canvas); 508 gm->setCanvasIsDeferred(isDeferred); 509 gm->draw(canvas); 510 canvas->setDrawFilter(NULL); 511 } 512 513 static ErrorCombination generate_image(GM* gm, const ConfigData& gRec, 514 GrSurface* gpuTarget, 515 SkBitmap* bitmap, 516 bool deferred) { 517 SkISize size (gm->getISize()); 518 setup_bitmap(gRec, size, bitmap); 519 520 SkAutoTUnref<SkCanvas> canvas; 521 522 if (gRec.fBackend == kRaster_Backend) { 523 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (*bitmap))); 524 if (deferred) { 525 canvas.reset(SkDeferredCanvas::Create(device)); 526 } else { 527 canvas.reset(SkNEW_ARGS(SkCanvas, (device))); 528 } 529 invokeGM(gm, canvas, false, deferred); 530 canvas->flush(); 531 } 532#if SK_SUPPORT_GPU 533 else { // GPU 534 SkAutoTUnref<SkDevice> device(SkGpuDevice::Create(gpuTarget)); 535 if (deferred) { 536 canvas.reset(SkDeferredCanvas::Create(device)); 537 } else { 538 canvas.reset(SkNEW_ARGS(SkCanvas, (device))); 539 } 540 invokeGM(gm, canvas, false, deferred); 541 // the device is as large as the current rendertarget, so 542 // we explicitly only readback the amount we expect (in 543 // size) overwrite our previous allocation 544 bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth, 545 size.fHeight); 546 canvas->readPixels(bitmap, 0, 0); 547 } 548#endif 549 complete_bitmap(bitmap); 550 return kEmpty_ErrorCombination; 551 } 552 553 static void generate_image_from_picture(GM* gm, const ConfigData& gRec, 554 SkPicture* pict, SkBitmap* bitmap, 555 SkScalar scale = SK_Scalar1, 556 bool tile = false) { 557 SkISize size = gm->getISize(); 558 setup_bitmap(gRec, size, bitmap); 559 560 if (tile) { 561 // Generate the result image by rendering to tiles and accumulating 562 // the results in 'bitmap' 563 564 // This 16x16 tiling matches the settings applied to 'pict' in 565 // 'generate_new_picture' 566 SkISize tileSize = SkISize::Make(16, 16); 567 568 SkBitmap tileBM; 569 setup_bitmap(gRec, tileSize, &tileBM); 570 SkCanvas tileCanvas(tileBM); 571 installFilter(&tileCanvas); 572 573 SkCanvas bmpCanvas(*bitmap); 574 SkPaint bmpPaint; 575 bmpPaint.setXfermodeMode(SkXfermode::kSrc_Mode); 576 577 for (int yTile = 0; yTile < (size.height()+15)/16; ++yTile) { 578 for (int xTile = 0; xTile < (size.width()+15)/16; ++xTile) { 579 int saveCount = tileCanvas.save(); 580 SkMatrix mat(tileCanvas.getTotalMatrix()); 581 mat.postTranslate(SkIntToScalar(-xTile*tileSize.width()), 582 SkIntToScalar(-yTile*tileSize.height())); 583 tileCanvas.setMatrix(mat); 584 pict->draw(&tileCanvas); 585 tileCanvas.flush(); 586 tileCanvas.restoreToCount(saveCount); 587 bmpCanvas.drawBitmap(tileBM, 588 SkIntToScalar(xTile * tileSize.width()), 589 SkIntToScalar(yTile * tileSize.height()), 590 &bmpPaint); 591 } 592 } 593 } else { 594 SkCanvas canvas(*bitmap); 595 installFilter(&canvas); 596 canvas.scale(scale, scale); 597 canvas.drawPicture(*pict); 598 complete_bitmap(bitmap); 599 } 600 } 601 602 static bool generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) { 603#ifdef SK_SUPPORT_PDF 604 SkMatrix initialTransform = gm->getInitialTransform(); 605 if (FLAGS_useDocumentInsteadOfDevice) { 606 SkISize pageISize = gm->getISize(); 607 SkAutoTUnref<SkDocument> pdfDoc(SkDocument::CreatePDF(&pdf, NULL, encode_to_dct_data)); 608 609 if (!pdfDoc.get()) { 610 return false; 611 } 612 613 SkCanvas* canvas = NULL; 614 canvas = pdfDoc->beginPage(SkIntToScalar(pageISize.width()), 615 SkIntToScalar(pageISize.height())); 616 canvas->concat(initialTransform); 617 618 invokeGM(gm, canvas, true, false); 619 620 return pdfDoc->close(); 621 } else { 622 SkISize pageSize = gm->getISize(); 623 SkPDFDevice* dev = NULL; 624 if (initialTransform.isIdentity()) { 625 dev = new SkPDFDevice(pageSize, pageSize, initialTransform); 626 } else { 627 SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()), 628 SkIntToScalar(pageSize.height())); 629 initialTransform.mapRect(&content); 630 content.intersect(0, 0, SkIntToScalar(pageSize.width()), 631 SkIntToScalar(pageSize.height())); 632 SkISize contentSize = 633 SkISize::Make(SkScalarRoundToInt(content.width()), 634 SkScalarRoundToInt(content.height())); 635 dev = new SkPDFDevice(pageSize, contentSize, initialTransform); 636 } 637 dev->setDCTEncoder(encode_to_dct_data); 638 SkAutoUnref aur(dev); 639 SkCanvas c(dev); 640 invokeGM(gm, &c, true, false); 641 SkPDFDocument doc; 642 doc.appendPage(dev); 643 doc.emitPDF(&pdf); 644 } 645#endif // SK_SUPPORT_PDF 646 return true; // Do not report failure if pdf is not supported. 647 } 648 649 static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) { 650#ifdef SK_SUPPORT_XPS 651 SkISize size = gm->getISize(); 652 653 SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()), 654 SkIntToScalar(size.height())); 655 static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254); 656 static const SkScalar upm = 72 * inchesPerMeter; 657 SkVector unitsPerMeter = SkPoint::Make(upm, upm); 658 static const SkScalar ppm = 200 * inchesPerMeter; 659 SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm); 660 661 SkXPSDevice* dev = new SkXPSDevice(); 662 SkAutoUnref aur(dev); 663 664 SkCanvas c(dev); 665 dev->beginPortfolio(&xps); 666 dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize); 667 invokeGM(gm, &c, false, false); 668 dev->endSheet(); 669 dev->endPortfolio(); 670 671#endif 672 } 673 674 ErrorCombination write_reference_image(const ConfigData& gRec, const char writePath [], 675 const char renderModeDescriptor [], 676 const char *shortName, 677 const BitmapAndDigest* bitmapAndDigest, 678 SkStreamAsset* document) { 679 SkString path; 680 bool success = false; 681 if (gRec.fBackend == kRaster_Backend || 682 gRec.fBackend == kGPU_Backend || 683 (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) { 684 685 path = make_bitmap_filename(writePath, shortName, gRec.fName, renderModeDescriptor, 686 bitmapAndDigest->fDigest); 687 success = write_bitmap(path, bitmapAndDigest->fBitmap); 688 } 689 if (kPDF_Backend == gRec.fBackend) { 690 path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, 691 "pdf"); 692 success = write_document(path, document); 693 } 694 if (kXPS_Backend == gRec.fBackend) { 695 path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, 696 "xps"); 697 success = write_document(path, document); 698 } 699 if (success) { 700 return kEmpty_ErrorCombination; 701 } else { 702 gm_fprintf(stderr, "FAILED to write %s\n", path.c_str()); 703 ErrorCombination errors(kWritingReferenceImage_ErrorType); 704 // TODO(epoger): Don't call RecordTestResults() here... 705 // Instead, we should make sure to call RecordTestResults 706 // exactly ONCE per test. (Otherwise, gmmain.fTestsRun 707 // will be incremented twice for this test: once in 708 // compare_test_results_to_stored_expectations() before 709 // that method calls this one, and again here.) 710 // 711 // When we make that change, we should probably add a 712 // WritingReferenceImage test to the gm self-tests.) 713 RecordTestResults(errors, make_shortname_plus_config(shortName, gRec.fName), 714 renderModeDescriptor); 715 return errors; 716 } 717 } 718 719 /** 720 * Log more detail about the mistmatch between expectedBitmap and 721 * actualBitmap. 722 */ 723 void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap, 724 const char *testName) { 725 const int expectedWidth = expectedBitmap.width(); 726 const int expectedHeight = expectedBitmap.height(); 727 const int width = actualBitmap.width(); 728 const int height = actualBitmap.height(); 729 if ((expectedWidth != width) || (expectedHeight != height)) { 730 gm_fprintf(stderr, "---- %s: dimension mismatch --" 731 " expected [%d %d], actual [%d %d]\n", 732 testName, expectedWidth, expectedHeight, width, height); 733 return; 734 } 735 736 if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) || 737 (SkBitmap::kARGB_8888_Config != actualBitmap.config())) { 738 gm_fprintf(stderr, "---- %s: not computing max per-channel" 739 " pixel mismatch because non-8888\n", testName); 740 return; 741 } 742 743 SkAutoLockPixels alp0(expectedBitmap); 744 SkAutoLockPixels alp1(actualBitmap); 745 int errR = 0; 746 int errG = 0; 747 int errB = 0; 748 int errA = 0; 749 int differingPixels = 0; 750 751 for (int y = 0; y < height; ++y) { 752 const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y); 753 const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y); 754 for (int x = 0; x < width; ++x) { 755 SkPMColor expectedPixel = *expectedPixelPtr++; 756 SkPMColor actualPixel = *actualPixelPtr++; 757 if (expectedPixel != actualPixel) { 758 differingPixels++; 759 errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) - 760 (int)SkGetPackedR32(actualPixel))); 761 errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) - 762 (int)SkGetPackedG32(actualPixel))); 763 errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) - 764 (int)SkGetPackedB32(actualPixel))); 765 errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) - 766 (int)SkGetPackedA32(actualPixel))); 767 } 768 } 769 } 770 gm_fprintf(stderr, "---- %s: %d (of %d) differing pixels," 771 " max per-channel mismatch R=%d G=%d B=%d A=%d\n", 772 testName, differingPixels, width*height, errR, errG, errB, errA); 773 } 774 775 /** 776 * Compares actual hash digest to expectations, returning the set of errors 777 * (if any) that we saw along the way. 778 * 779 * If fMismatchPath has been set, and there are pixel diffs, then the 780 * actual bitmap will be written out to a file within fMismatchPath. 781 * And similarly for fMissingExpectationsPath... 782 * 783 * @param expectations what expectations to compare actualBitmap against 784 * @param actualBitmapAndDigest the SkBitmap we actually generated, and its GmResultDigest 785 * @param shortName name of test, e.g. "selftest1" 786 * @param configName name of config, e.g. "8888" 787 * @param renderModeDescriptor e.g., "-rtree", "-deferred" 788 * @param addToJsonSummary whether to add these results (both actual and 789 * expected) to the JSON summary. Regardless of this setting, if 790 * we find an image mismatch in this test, we will write these 791 * results to the JSON summary. (This is so that we will always 792 * report errors across rendering modes, such as pipe vs tiled. 793 * See https://codereview.chromium.org/13650002/ ) 794 */ 795 ErrorCombination compare_to_expectations(Expectations expectations, 796 const BitmapAndDigest& actualBitmapAndDigest, 797 const char *shortName, const char *configName, 798 const char *renderModeDescriptor, 799 bool addToJsonSummary) { 800 ErrorCombination errors; 801 SkString shortNamePlusConfig = make_shortname_plus_config(shortName, configName); 802 SkString completeNameString(shortNamePlusConfig); 803 completeNameString.append(renderModeDescriptor); 804 completeNameString.append("."); 805 completeNameString.append(kPNG_FileExtension); 806 const char* completeName = completeNameString.c_str(); 807 808 if (expectations.empty()) { 809 errors.add(kMissingExpectations_ErrorType); 810 811 // Write out the "actuals" for any tests without expectations, if we have 812 // been directed to do so. 813 if (fMissingExpectationsPath) { 814 SkString path = make_bitmap_filename(fMissingExpectationsPath, shortName, 815 configName, renderModeDescriptor, 816 actualBitmapAndDigest.fDigest); 817 write_bitmap(path, actualBitmapAndDigest.fBitmap); 818 } 819 820 } else if (!expectations.match(actualBitmapAndDigest.fDigest)) { 821 addToJsonSummary = true; 822 // The error mode we record depends on whether this was running 823 // in a non-standard renderMode. 824 if ('\0' == *renderModeDescriptor) { 825 errors.add(kExpectationsMismatch_ErrorType); 826 } else { 827 errors.add(kRenderModeMismatch_ErrorType); 828 } 829 830 // Write out the "actuals" for any mismatches, if we have 831 // been directed to do so. 832 if (fMismatchPath) { 833 SkString path = make_bitmap_filename(fMismatchPath, shortName, configName, 834 renderModeDescriptor, 835 actualBitmapAndDigest.fDigest); 836 write_bitmap(path, actualBitmapAndDigest.fBitmap); 837 } 838 839 // If we have access to a single expected bitmap, log more 840 // detail about the mismatch. 841 const SkBitmap *expectedBitmapPtr = expectations.asBitmap(); 842 if (NULL != expectedBitmapPtr) { 843 report_bitmap_diffs(*expectedBitmapPtr, actualBitmapAndDigest.fBitmap, 844 completeName); 845 } 846 } 847 RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor); 848 849 if (addToJsonSummary) { 850 add_actual_results_to_json_summary(completeName, actualBitmapAndDigest.fDigest, errors, 851 expectations.ignoreFailure()); 852 add_expected_results_to_json_summary(completeName, expectations); 853 } 854 855 return errors; 856 } 857 858 /** 859 * Add this result to the appropriate JSON collection of actual results, 860 * depending on errors encountered. 861 */ 862 void add_actual_results_to_json_summary(const char testName[], 863 const GmResultDigest &actualResultDigest, 864 ErrorCombination errors, 865 bool ignoreFailure) { 866 Json::Value jsonActualResults = actualResultDigest.asJsonTypeValuePair(); 867 if (errors.isEmpty()) { 868 this->fJsonActualResults_Succeeded[testName] = jsonActualResults; 869 } else { 870 if (ignoreFailure) { 871 // TODO: Once we have added the ability to compare 872 // actual results against expectations in a JSON file 873 // (where we can set ignore-failure to either true or 874 // false), add test cases that exercise ignored 875 // failures (both for kMissingExpectations_ErrorType 876 // and kExpectationsMismatch_ErrorType). 877 this->fJsonActualResults_FailureIgnored[testName] = 878 jsonActualResults; 879 } else { 880 if (errors.includes(kMissingExpectations_ErrorType)) { 881 // TODO: What about the case where there IS an 882 // expected image hash digest, but that gm test 883 // doesn't actually run? For now, those cases 884 // will always be ignored, because gm only looks 885 // at expectations that correspond to gm tests 886 // that were actually run. 887 // 888 // Once we have the ability to express 889 // expectations as a JSON file, we should fix this 890 // (and add a test case for which an expectation 891 // is given but the test is never run). 892 this->fJsonActualResults_NoComparison[testName] = 893 jsonActualResults; 894 } 895 if (errors.includes(kExpectationsMismatch_ErrorType) || 896 errors.includes(kRenderModeMismatch_ErrorType)) { 897 this->fJsonActualResults_Failed[testName] = jsonActualResults; 898 } 899 } 900 } 901 } 902 903 /** 904 * Add this test to the JSON collection of expected results. 905 */ 906 void add_expected_results_to_json_summary(const char testName[], 907 Expectations expectations) { 908 this->fJsonExpectedResults[testName] = expectations.asJsonValue(); 909 } 910 911 /** 912 * Compare actualBitmap to expectations stored in this->fExpectationsSource. 913 * 914 * @param gm which test generated the actualBitmap 915 * @param gRec 916 * @param writePath unless this is NULL, write out actual images into this 917 * directory 918 * @param actualBitmapAndDigest ptr to bitmap generated by this run, or NULL 919 * if we don't have a usable bitmap representation 920 * @param document pdf or xps representation, if appropriate 921 */ 922 ErrorCombination compare_test_results_to_stored_expectations( 923 GM* gm, const ConfigData& gRec, const char writePath[], 924 const BitmapAndDigest* actualBitmapAndDigest, SkStreamAsset* document) { 925 926 SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), gRec.fName); 927 SkString nameWithExtension(shortNamePlusConfig); 928 nameWithExtension.append("."); 929 nameWithExtension.append(kPNG_FileExtension); 930 931 ErrorCombination errors; 932 933 if (NULL == actualBitmapAndDigest) { 934 // Note that we intentionally skipped validating the results for 935 // this test, because we don't know how to generate an SkBitmap 936 // version of the output. 937 RecordTestResults(ErrorCombination(kIntentionallySkipped_ErrorType), 938 shortNamePlusConfig, ""); 939 } else if (!(gRec.fFlags & kWrite_ConfigFlag)) { 940 // We don't record the results for this test or compare them 941 // against any expectations, because the output image isn't 942 // meaningful. 943 // See https://code.google.com/p/skia/issues/detail?id=1410 ('some 944 // GM result images not available for download from Google Storage') 945 RecordTestResults(ErrorCombination(kIntentionallySkipped_ErrorType), 946 shortNamePlusConfig, ""); 947 } else { 948 ExpectationsSource *expectationsSource = this->fExpectationsSource.get(); 949 if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { 950 /* 951 * Get the expected results for this test, as one or more allowed 952 * hash digests. The current implementation of expectationsSource 953 * get this by computing the hash digest of a single PNG file on disk. 954 * 955 * TODO(epoger): This relies on the fact that 956 * force_all_opaque() was called on the bitmap before it 957 * was written to disk as a PNG in the first place. If 958 * not, the hash digest returned here may not match the 959 * hash digest of actualBitmap, which *has* been run through 960 * force_all_opaque(). 961 * See comments above complete_bitmap() for more detail. 962 */ 963 Expectations expectations = expectationsSource->get(nameWithExtension.c_str()); 964 errors.add(compare_to_expectations(expectations, *actualBitmapAndDigest, 965 gm->shortName(), gRec.fName, "", true)); 966 } else { 967 // If we are running without expectations, we still want to 968 // record the actual results. 969 add_actual_results_to_json_summary(nameWithExtension.c_str(), 970 actualBitmapAndDigest->fDigest, 971 ErrorCombination(kMissingExpectations_ErrorType), 972 false); 973 RecordTestResults(ErrorCombination(kMissingExpectations_ErrorType), 974 shortNamePlusConfig, ""); 975 } 976 } 977 978 // TODO: Consider moving this into compare_to_expectations(), 979 // similar to fMismatchPath... for now, we don't do that, because 980 // we don't want to write out the actual bitmaps for all 981 // renderModes of all tests! That would be a lot of files. 982 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 983 errors.add(write_reference_image(gRec, writePath, "", gm->shortName(), 984 actualBitmapAndDigest, document)); 985 } 986 987 return errors; 988 } 989 990 /** 991 * Compare actualBitmap to referenceBitmap. 992 * 993 * @param shortName test name, e.g. "selftest1" 994 * @param configName configuration name, e.g. "8888" 995 * @param renderModeDescriptor 996 * @param actualBitmap actual bitmap generated by this run 997 * @param referenceBitmap bitmap we expected to be generated 998 */ 999 ErrorCombination compare_test_results_to_reference_bitmap( 1000 const char *shortName, const char *configName, const char *renderModeDescriptor, 1001 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { 1002 1003 SkASSERT(referenceBitmap); 1004 Expectations expectations(*referenceBitmap); 1005 BitmapAndDigest actualBitmapAndDigest(actualBitmap); 1006 return compare_to_expectations(expectations, actualBitmapAndDigest, shortName, 1007 configName, renderModeDescriptor, false); 1008 } 1009 1010 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags, 1011 SkScalar scale = SK_Scalar1) { 1012 // Pictures are refcounted so must be on heap 1013 SkPicture* pict; 1014 int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale)); 1015 int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale)); 1016 1017 if (kTileGrid_BbhType == bbhType) { 1018 SkTileGridPicture::TileGridInfo info; 1019 info.fMargin.setEmpty(); 1020 info.fOffset.setZero(); 1021 info.fTileInterval.set(16, 16); 1022 pict = new SkTileGridPicture(width, height, info); 1023 } else { 1024 pict = new SkPicture; 1025 } 1026 if (kNone_BbhType != bbhType) { 1027 recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag; 1028 } 1029 SkCanvas* cv = pict->beginRecording(width, height, recordFlags); 1030 cv->scale(scale, scale); 1031 invokeGM(gm, cv, false, false); 1032 pict->endRecording(); 1033 1034 return pict; 1035 } 1036 1037 static SkPicture* stream_to_new_picture(const SkPicture& src) { 1038 SkDynamicMemoryWStream storage; 1039 src.serialize(&storage); 1040 SkAutoTUnref<SkStreamAsset> pictReadback(storage.detachAsStream()); 1041 SkPicture* retval = SkPicture::CreateFromStream(pictReadback); 1042 return retval; 1043 } 1044 1045 // Test: draw into a bitmap or pdf. 1046 // Depending on flags, possibly compare to an expected image. 1047 ErrorCombination test_drawing(GM* gm, 1048 const ConfigData& gRec, 1049 const char writePath [], 1050 GrSurface* gpuTarget, 1051 SkBitmap* bitmap) { 1052 SkDynamicMemoryWStream document; 1053 1054 if (gRec.fBackend == kRaster_Backend || 1055 gRec.fBackend == kGPU_Backend) { 1056 // Early exit if we can't generate the image. 1057 ErrorCombination errors = generate_image(gm, gRec, gpuTarget, bitmap, false); 1058 if (!errors.isEmpty()) { 1059 // TODO: Add a test to exercise what the stdout and 1060 // JSON look like if we get an "early error" while 1061 // trying to generate the image. 1062 return errors; 1063 } 1064 } else if (gRec.fBackend == kPDF_Backend) { 1065#if CAN_IMAGE_PDF 1066 if (!generate_pdf(gm, document)) { 1067 errors.add(kGeneratePdfFailed_ErrorType); 1068 } else { 1069 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); 1070 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1071 path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf"); 1072 errors.add(write_document(path, documentStream)); 1073 } 1074 1075 if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) { 1076 for (int i = 0; i < pdfRasterizers.count(); i++) { 1077 SkBitmap pdfBitmap; 1078 SkASSERT(documentStream->rewind()); 1079 bool success = (*pdfRasterizers[i]->fRasterizerFunction)( 1080 documentStream.get(), &pdfBitmap); 1081 if (!success) { 1082 gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n", 1083 gm->shortName(), 1084 pdfRasterizers[i]->fName); 1085 continue; 1086 } 1087 1088 SkString configName(gRec.fName); 1089 configName.append("-"); 1090 configName.append(pdfRasterizers[i]->fName); 1091 1092 BitmapAndDigest bitmapAndDigest(pdfBitmap); 1093 errors.add(compare_test_results_to_stored_expectations( 1094 gm, gRec, configName.c_str(), &bitmapAndDigest)); 1095 1096 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { 1097 path = make_bitmap_filename(writePath, gm->shortName(), 1098 configName.c_str(), 1099 "", bitmapAndDigest.fDigest); 1100 errors.add(write_bitmap(path, bitmapAndDigest.fBitmap)); 1101 } 1102 } 1103 } else { 1104 errors.add(kIntentionallySkipped_ErrorType); 1105 } 1106 } 1107 1108#else 1109 bitmap = NULL; // we don't generate a bitmap rendering of the PDF file 1110#endif 1111 } else if (gRec.fBackend == kXPS_Backend) { 1112 generate_xps(gm, document); 1113 bitmap = NULL; // we don't generate a bitmap rendering of the XPS file 1114 } 1115 1116 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()); 1117 if (NULL == bitmap) { 1118 return compare_test_results_to_stored_expectations( 1119 gm, gRec, writePath, NULL, documentStream); 1120 } else { 1121 BitmapAndDigest bitmapAndDigest(*bitmap); 1122 return compare_test_results_to_stored_expectations( 1123 gm, gRec, writePath, &bitmapAndDigest, documentStream); 1124 } 1125 } 1126 1127 ErrorCombination test_deferred_drawing(GM* gm, 1128 const ConfigData& gRec, 1129 const SkBitmap& referenceBitmap, 1130 GrSurface* gpuTarget) { 1131 if (gRec.fBackend == kRaster_Backend || 1132 gRec.fBackend == kGPU_Backend) { 1133 const char renderModeDescriptor[] = "-deferred"; 1134 SkBitmap bitmap; 1135 // Early exit if we can't generate the image, but this is 1136 // expected in some cases, so don't report a test failure. 1137 ErrorCombination errors = generate_image(gm, gRec, gpuTarget, &bitmap, true); 1138 // TODO(epoger): This logic is the opposite of what is 1139 // described above... if we succeeded in generating the 1140 // -deferred image, we exit early! We should fix this 1141 // ASAP, because it is hiding -deferred errors... but for 1142 // now, I'm leaving the logic as it is so that the 1143 // refactoring change 1144 // https://codereview.chromium.org/12992003/ is unblocked. 1145 // 1146 // Filed as https://code.google.com/p/skia/issues/detail?id=1180 1147 // ('image-surface gm test is failing in "deferred" mode, 1148 // and gm is not reporting the failure') 1149 if (errors.isEmpty()) { 1150 // TODO(epoger): Report this as a new ErrorType, 1151 // something like kImageGeneration_ErrorType? 1152 return kEmpty_ErrorCombination; 1153 } 1154 return compare_test_results_to_reference_bitmap( 1155 gm->shortName(), gRec.fName, renderModeDescriptor, bitmap, &referenceBitmap); 1156 } 1157 return kEmpty_ErrorCombination; 1158 } 1159 1160 ErrorCombination test_pipe_playback(GM* gm, const ConfigData& gRec, 1161 const SkBitmap& referenceBitmap, bool simulateFailure) { 1162 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), 1163 gRec.fName); 1164 ErrorCombination errors; 1165 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 1166 SkString renderModeDescriptor("-pipe"); 1167 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); 1168 1169 if (gm->getFlags() & GM::kSkipPipe_Flag) { 1170 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1171 renderModeDescriptor.c_str()); 1172 errors.add(kIntentionallySkipped_ErrorType); 1173 } else { 1174 SkBitmap bitmap; 1175 SkISize size = gm->getISize(); 1176 setup_bitmap(gRec, size, &bitmap); 1177 SkCanvas canvas(bitmap); 1178 installFilter(&canvas); 1179 // Pass a decoding function so the factory GM (which has an SkBitmap 1180 // with encoded data) will not fail playback. 1181 PipeController pipeController(&canvas, &SkImageDecoder::DecodeMemory); 1182 SkGPipeWriter writer; 1183 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, 1184 gPipeWritingFlagCombos[i].flags, 1185 size.width(), size.height()); 1186 if (!simulateFailure) { 1187 invokeGM(gm, pipeCanvas, false, false); 1188 } 1189 complete_bitmap(&bitmap); 1190 writer.endRecording(); 1191 errors.add(compare_test_results_to_reference_bitmap( 1192 gm->shortName(), gRec.fName, renderModeDescriptor.c_str(), bitmap, 1193 &referenceBitmap)); 1194 if (!errors.isEmpty()) { 1195 break; 1196 } 1197 } 1198 } 1199 return errors; 1200 } 1201 1202 ErrorCombination test_tiled_pipe_playback(GM* gm, const ConfigData& gRec, 1203 const SkBitmap& referenceBitmap) { 1204 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), 1205 gRec.fName); 1206 ErrorCombination errors; 1207 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 1208 SkString renderModeDescriptor("-tiled pipe"); 1209 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); 1210 1211 if ((gm->getFlags() & GM::kSkipPipe_Flag) || 1212 (gm->getFlags() & GM::kSkipTiled_Flag)) { 1213 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1214 renderModeDescriptor.c_str()); 1215 errors.add(kIntentionallySkipped_ErrorType); 1216 } else { 1217 SkBitmap bitmap; 1218 SkISize size = gm->getISize(); 1219 setup_bitmap(gRec, size, &bitmap); 1220 SkCanvas canvas(bitmap); 1221 installFilter(&canvas); 1222 TiledPipeController pipeController(bitmap, &SkImageDecoder::DecodeMemory); 1223 SkGPipeWriter writer; 1224 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, 1225 gPipeWritingFlagCombos[i].flags, 1226 size.width(), size.height()); 1227 invokeGM(gm, pipeCanvas, false, false); 1228 complete_bitmap(&bitmap); 1229 writer.endRecording(); 1230 errors.add(compare_test_results_to_reference_bitmap(gm->shortName(), gRec.fName, 1231 renderModeDescriptor.c_str(), 1232 bitmap, &referenceBitmap)); 1233 if (!errors.isEmpty()) { 1234 break; 1235 } 1236 } 1237 } 1238 return errors; 1239 } 1240 1241 // 1242 // member variables. 1243 // They are public for now, to allow easier setting by tool_main(). 1244 // 1245 1246 bool fUseFileHierarchy, fWriteChecksumBasedFilenames; 1247 ErrorCombination fIgnorableErrorTypes; 1248 1249 const char* fMismatchPath; 1250 const char* fMissingExpectationsPath; 1251 1252 // collection of tests that have failed with each ErrorType 1253 SkTArray<SkString> fFailedTests[kLast_ErrorType+1]; 1254 int fTestsRun; 1255 SkTDict<int> fRenderModesEncountered; 1256 1257 // Where to read expectations (expected image hash digests, etc.) from. 1258 // If unset, we don't do comparisons. 1259 SkAutoTUnref<ExpectationsSource> fExpectationsSource; 1260 1261 // JSON summaries that we generate as we go (just for output). 1262 Json::Value fJsonExpectedResults; 1263 Json::Value fJsonActualResults_Failed; 1264 Json::Value fJsonActualResults_FailureIgnored; 1265 Json::Value fJsonActualResults_NoComparison; 1266 Json::Value fJsonActualResults_Succeeded; 1267 1268}; // end of GMMain class definition 1269 1270#if SK_SUPPORT_GPU 1271static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType; 1272#else 1273static const GLContextType kDontCare_GLContextType = 0; 1274#endif 1275 1276// If the platform does not support writing PNGs of PDFs then there will be no 1277// reference images to read. However, we can always write the .pdf files 1278static const ConfigFlags kPDFConfigFlags = CAN_IMAGE_PDF ? kRW_ConfigFlag : 1279 kWrite_ConfigFlag; 1280 1281static const ConfigData gRec[] = { 1282 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "8888", true }, 1283#if 0 // stop testing this (for now at least) since we want to remove support for it (soon please!!!) 1284 { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "4444", true }, 1285#endif 1286 { SkBitmap::kRGB_565_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "565", true }, 1287#if SK_SUPPORT_GPU 1288 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 0, kRW_ConfigFlag, "gpu", true }, 1289 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag, "msaa16", false}, 1290 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNative_GLContextType, 4, kRW_ConfigFlag, "msaa4", false}, 1291 /* The gpudebug context does not generate meaningful images, so don't record 1292 * the images it generates! We only run it to look for asserts. */ 1293 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kDebug_GLContextType, 0, kNone_ConfigFlag, "gpudebug", GR_DEBUG}, 1294 /* The gpunull context does the least amount of work possible and doesn't 1295 generate meaninful images, so don't record them!. It can be run to 1296 isolate the CPU-side processing expense from the GPU-side. 1297 */ 1298 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kNull_GLContextType, 0, kNone_ConfigFlag, "gpunull", GR_DEBUG}, 1299#if SK_ANGLE 1300 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 0, kRW_ConfigFlag, "angle", true }, 1301 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, 16, kRW_ConfigFlag, "anglemsaa16", true }, 1302#endif // SK_ANGLE 1303#ifdef SK_MESA 1304 { SkBitmap::kARGB_8888_Config, kGPU_Backend, GrContextFactory::kMESA_GLContextType, 0, kRW_ConfigFlag, "mesa", true }, 1305#endif // SK_MESA 1306#endif // SK_SUPPORT_GPU 1307#ifdef SK_SUPPORT_XPS 1308 /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */ 1309 { SkBitmap::kARGB_8888_Config, kXPS_Backend, kDontCare_GLContextType, 0, kWrite_ConfigFlag, "xps", true }, 1310#endif // SK_SUPPORT_XPS 1311#ifdef SK_SUPPORT_PDF 1312 { SkBitmap::kARGB_8888_Config, kPDF_Backend, kDontCare_GLContextType, 0, kPDFConfigFlags, "pdf", true }, 1313#endif // SK_SUPPORT_PDF 1314}; 1315 1316static const char kDefaultsConfigStr[] = "defaults"; 1317static const char kExcludeConfigChar = '~'; 1318 1319static SkString configUsage() { 1320 SkString result; 1321 result.appendf("Space delimited list of which configs to run. Possible options: ["); 1322 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1323 SkASSERT(gRec[i].fName != kDefaultsConfigStr); 1324 if (i > 0) { 1325 result.append("|"); 1326 } 1327 result.appendf("%s", gRec[i].fName); 1328 } 1329 result.append("]\n"); 1330 result.appendf("The default value is: \""); 1331 SkString firstDefault; 1332 SkString allButFirstDefaults; 1333 SkString nonDefault; 1334 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1335 if (gRec[i].fRunByDefault) { 1336 if (i > 0) { 1337 result.append(" "); 1338 } 1339 result.append(gRec[i].fName); 1340 if (firstDefault.isEmpty()) { 1341 firstDefault = gRec[i].fName; 1342 } else { 1343 if (!allButFirstDefaults.isEmpty()) { 1344 allButFirstDefaults.append(" "); 1345 } 1346 allButFirstDefaults.append(gRec[i].fName); 1347 } 1348 } else { 1349 nonDefault = gRec[i].fName; 1350 } 1351 } 1352 result.append("\"\n"); 1353 result.appendf("\"%s\" evaluates to the default set of configs.\n", kDefaultsConfigStr); 1354 result.appendf("Prepending \"%c\" on a config name excludes it from the set of configs to run.\n" 1355 "Exclusions always override inclusions regardless of order.\n", 1356 kExcludeConfigChar); 1357 result.appendf("E.g. \"--config %s %c%s %s\" will run these configs:\n\t%s %s", 1358 kDefaultsConfigStr, 1359 kExcludeConfigChar, 1360 firstDefault.c_str(), 1361 nonDefault.c_str(), 1362 allButFirstDefaults.c_str(), 1363 nonDefault.c_str()); 1364 return result; 1365} 1366 1367// Macro magic to convert a numeric preprocessor token into a string. 1368// Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string 1369// This should probably be moved into one of our common headers... 1370#define TOSTRING_INTERNAL(x) #x 1371#define TOSTRING(x) TOSTRING_INTERNAL(x) 1372 1373// Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). 1374DEFINE_string(config, "", configUsage().c_str()); 1375DEFINE_bool(deferred, true, "Exercise the deferred rendering test pass."); 1376DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip."); 1377DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing."); 1378#if SK_SUPPORT_GPU 1379DEFINE_string(gpuCacheSize, "", "<bytes> <count>: Limit the gpu cache to byte size or " 1380 "object count. " TOSTRING(DEFAULT_CACHE_VALUE) " for either value means " 1381 "use the default. 0 for either disables the cache."); 1382#endif 1383DEFINE_bool(hierarchy, false, "Whether to use multilevel directory structure " 1384 "when reading/writing files."); 1385DEFINE_string(ignoreErrorTypes, kDefaultIgnorableErrorTypes.asString(" ").c_str(), 1386 "Space-separated list of ErrorTypes that should be ignored. If any *other* error " 1387 "types are encountered, the tool will exit with a nonzero return value."); 1388DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n" 1389 "Multiple matches may be separated by spaces.\n" 1390 "~ causes a matching test to always be skipped\n" 1391 "^ requires the start of the test to match\n" 1392 "$ requires the end of the test to match\n" 1393 "^ and $ requires an exact match\n" 1394 "If a test does not match any list entry,\n" 1395 "it is skipped unless some list entry starts with ~"); 1396DEFINE_string(missingExpectationsPath, "", "Write images for tests without expectations " 1397 "into this directory."); 1398DEFINE_string(mismatchPath, "", "Write images for tests that failed due to " 1399 "pixel mismatches into this directory."); 1400DEFINE_string(modulo, "", "[--modulo <remainder> <divisor>]: only run tests for which " 1401 "testIndex %% divisor == remainder."); 1402DEFINE_bool(pdf, true, "Exercise the pdf rendering test pass."); 1403DEFINE_bool(pipe, true, "Exercise the SkGPipe replay test pass."); 1404DEFINE_string2(readPath, r, "", "Read reference images from this dir, and report " 1405 "any differences between those and the newly generated ones."); 1406DEFINE_bool(replay, true, "Exercise the SkPicture replay test pass."); 1407DEFINE_string2(resourcePath, i, "", "Directory that stores image resources."); 1408DEFINE_bool(rtree, true, "Exercise the R-Tree variant of SkPicture test pass."); 1409DEFINE_bool(serialize, true, "Exercise the SkPicture serialization & deserialization test pass."); 1410DEFINE_bool(simulatePipePlaybackFailure, false, "Simulate a rendering failure in pipe mode only."); 1411DEFINE_bool(tiledPipe, false, "Exercise tiled SkGPipe replay."); 1412DEFINE_bool(tileGrid, true, "Exercise the tile grid variant of SkPicture."); 1413DEFINE_string(tileGridReplayScales, "", "Space separated list of floating-point scale " 1414 "factors to be used for tileGrid playback testing. Default value: 1.0"); 1415DEFINE_bool2(verbose, v, false, "Give more detail (e.g. list all GMs run, more info about " 1416 "each test)."); 1417DEFINE_bool(writeChecksumBasedFilenames, false, "When writing out actual images, use checksum-" 1418 "based filenames, as rebaseline.py will use when downloading them from Google Storage"); 1419DEFINE_string(writeJsonSummaryPath, "", "Write a JSON-formatted result summary to this file."); 1420DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); 1421DEFINE_string2(writePicturePath, p, "", "Write .skp files into this directory."); 1422DEFINE_int32(pdfJpegQuality, -1, "Encodes images in JPEG at quality level N, " 1423 "which can be in range 0-100). N = -1 will disable JPEG compression. " 1424 "Default is N = 100, maximum quality."); 1425DEFINE_bool(useDocumentInsteadOfDevice, false, "Use SkDocument::CreateFoo instead of SkFooDevice."); 1426 1427static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) { 1428 // Filter output of warnings that JPEG is not available for the image. 1429 if (bitmap.width() >= 65500 || bitmap.height() >= 65500) return false; 1430 if (FLAGS_pdfJpegQuality == -1) return false; 1431 1432 SkIRect bitmapBounds; 1433 SkBitmap subset; 1434 const SkBitmap* bitmapToUse = &bitmap; 1435 bitmap.getBounds(&bitmapBounds); 1436 if (rect != bitmapBounds) { 1437 SkAssertResult(bitmap.extractSubset(&subset, rect)); 1438 bitmapToUse = ⊂ 1439 } 1440 1441#if defined(SK_BUILD_FOR_MAC) 1442 // Workaround bug #1043 where bitmaps with referenced pixels cause 1443 // CGImageDestinationFinalize to crash 1444 SkBitmap copy; 1445 bitmapToUse->deepCopyTo(©, bitmapToUse->config()); 1446 bitmapToUse = © 1447#endif 1448 1449 return SkImageEncoder::EncodeStream(stream, 1450 *bitmapToUse, 1451 SkImageEncoder::kJPEG_Type, 1452 FLAGS_pdfJpegQuality); 1453} 1454 1455static int findConfig(const char config[]) { 1456 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { 1457 if (!strcmp(config, gRec[i].fName)) { 1458 return (int) i; 1459 } 1460 } 1461 return -1; 1462} 1463 1464namespace skiagm { 1465#if SK_SUPPORT_GPU 1466SkAutoTUnref<GrContext> gGrContext; 1467/** 1468 * Sets the global GrContext, accessible by individual GMs 1469 */ 1470static void SetGr(GrContext* grContext) { 1471 SkSafeRef(grContext); 1472 gGrContext.reset(grContext); 1473} 1474 1475/** 1476 * Gets the global GrContext, can be called by GM tests. 1477 */ 1478GrContext* GetGr(); 1479GrContext* GetGr() { 1480 return gGrContext.get(); 1481} 1482 1483/** 1484 * Sets the global GrContext and then resets it to its previous value at 1485 * destruction. 1486 */ 1487class AutoResetGr : SkNoncopyable { 1488public: 1489 AutoResetGr() : fOld(NULL) {} 1490 void set(GrContext* context) { 1491 SkASSERT(NULL == fOld); 1492 fOld = GetGr(); 1493 SkSafeRef(fOld); 1494 SetGr(context); 1495 } 1496 ~AutoResetGr() { SetGr(fOld); SkSafeUnref(fOld); } 1497private: 1498 GrContext* fOld; 1499}; 1500#else 1501GrContext* GetGr(); 1502GrContext* GetGr() { return NULL; } 1503#endif 1504} 1505 1506template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { 1507 int index = array->find(value); 1508 if (index < 0) { 1509 *array->append() = value; 1510 } 1511} 1512 1513/** 1514 * Run this test in a number of different configs (8888, 565, PDF, 1515 * etc.), confirming that the resulting bitmaps match expectations 1516 * (which may be different for each config). 1517 * 1518 * Returns all errors encountered while doing so. 1519 */ 1520ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<size_t> &configs, 1521 GrContextFactory *grFactory); 1522ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<size_t> &configs, 1523 GrContextFactory *grFactory) { 1524 const char renderModeDescriptor[] = ""; 1525 ErrorCombination errorsForAllConfigs; 1526 uint32_t gmFlags = gm->getFlags(); 1527 1528 for (int i = 0; i < configs.count(); i++) { 1529 ConfigData config = gRec[configs[i]]; 1530 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(), 1531 config.fName); 1532 1533 // Skip any tests that we don't even need to try. 1534 // If any of these were skipped on a per-GM basis, record them as 1535 // kIntentionallySkipped. 1536 if (kPDF_Backend == config.fBackend) { 1537 if (!FLAGS_pdf) { 1538 continue; 1539 } 1540 if (gmFlags & GM::kSkipPDF_Flag) { 1541 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1542 renderModeDescriptor); 1543 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1544 continue; 1545 } 1546 } 1547 if ((gmFlags & GM::kSkip565_Flag) && 1548 (kRaster_Backend == config.fBackend) && 1549 (SkBitmap::kRGB_565_Config == config.fConfig)) { 1550 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1551 renderModeDescriptor); 1552 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1553 continue; 1554 } 1555 if ((gmFlags & GM::kSkipGPU_Flag) && 1556 kGPU_Backend == config.fBackend) { 1557 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1558 renderModeDescriptor); 1559 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); 1560 continue; 1561 } 1562 1563 // Now we know that we want to run this test and record its 1564 // success or failure. 1565 ErrorCombination errorsForThisConfig; 1566 GrSurface* gpuTarget = NULL; 1567#if SK_SUPPORT_GPU 1568 SkAutoTUnref<GrSurface> auGpuTarget; 1569 AutoResetGr autogr; 1570 if ((errorsForThisConfig.isEmpty()) && (kGPU_Backend == config.fBackend)) { 1571 GrContext* gr = grFactory->get(config.fGLContextType); 1572 bool grSuccess = false; 1573 if (gr) { 1574 // create a render target to back the device 1575 GrTextureDesc desc; 1576 desc.fConfig = kSkia8888_GrPixelConfig; 1577 desc.fFlags = kRenderTarget_GrTextureFlagBit; 1578 desc.fWidth = gm->getISize().width(); 1579 desc.fHeight = gm->getISize().height(); 1580 desc.fSampleCnt = config.fSampleCnt; 1581 auGpuTarget.reset(gr->createUncachedTexture(desc, NULL, 0)); 1582 if (NULL != auGpuTarget) { 1583 gpuTarget = auGpuTarget; 1584 grSuccess = true; 1585 autogr.set(gr); 1586 // Set the user specified cache limits if non-default. 1587 size_t bytes; 1588 int count; 1589 gr->getTextureCacheLimits(&count, &bytes); 1590 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeBytes) { 1591 bytes = static_cast<size_t>(gGpuCacheSizeBytes); 1592 } 1593 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeCount) { 1594 count = gGpuCacheSizeCount; 1595 } 1596 gr->setTextureCacheLimits(count, bytes); 1597 } 1598 } 1599 if (!grSuccess) { 1600 errorsForThisConfig.add(kNoGpuContext_ErrorType); 1601 } 1602 } 1603#endif 1604 1605 SkBitmap comparisonBitmap; 1606 1607 const char* writePath; 1608 if (FLAGS_writePath.count() == 1) { 1609 writePath = FLAGS_writePath[0]; 1610 } else { 1611 writePath = NULL; 1612 } 1613 if (errorsForThisConfig.isEmpty()) { 1614 errorsForThisConfig.add(gmmain.test_drawing(gm,config, writePath, gpuTarget, 1615 &comparisonBitmap)); 1616 } 1617 1618 if (FLAGS_deferred && errorsForThisConfig.isEmpty() && 1619 (kGPU_Backend == config.fBackend || kRaster_Backend == config.fBackend)) { 1620 errorsForThisConfig.add(gmmain.test_deferred_drawing(gm, config, comparisonBitmap, 1621 gpuTarget)); 1622 } 1623 1624 errorsForAllConfigs.add(errorsForThisConfig); 1625 } 1626 return errorsForAllConfigs; 1627} 1628 1629/** 1630 * Run this test in a number of different drawing modes (pipe, 1631 * deferred, tiled, etc.), confirming that the resulting bitmaps all 1632 * *exactly* match comparisonBitmap. 1633 * 1634 * Returns all errors encountered while doing so. 1635 */ 1636ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, 1637 const SkBitmap &comparisonBitmap, 1638 const SkTDArray<SkScalar> &tileGridReplayScales); 1639ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, 1640 const SkBitmap &comparisonBitmap, 1641 const SkTDArray<SkScalar> &tileGridReplayScales) { 1642 ErrorCombination errorsForAllModes; 1643 uint32_t gmFlags = gm->getFlags(); 1644 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->shortName(), 1645 compareConfig.fName); 1646 1647 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0); 1648 SkAutoUnref aur(pict); 1649 if (FLAGS_replay) { 1650 const char renderModeDescriptor[] = "-replay"; 1651 if (gmFlags & GM::kSkipPicture_Flag) { 1652 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1653 renderModeDescriptor); 1654 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1655 } else { 1656 SkBitmap bitmap; 1657 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); 1658 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1659 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1660 &comparisonBitmap)); 1661 } 1662 } 1663 1664 if (FLAGS_serialize) { 1665 const char renderModeDescriptor[] = "-serialize"; 1666 if (gmFlags & GM::kSkipPicture_Flag) { 1667 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1668 renderModeDescriptor); 1669 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1670 } else { 1671 SkPicture* repict = gmmain.stream_to_new_picture(*pict); 1672 SkAutoUnref aurr(repict); 1673 SkBitmap bitmap; 1674 gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap); 1675 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1676 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1677 &comparisonBitmap)); 1678 } 1679 } 1680 1681 if ((1 == FLAGS_writePicturePath.count()) && 1682 !(gmFlags & GM::kSkipPicture_Flag)) { 1683 const char* pictureSuffix = "skp"; 1684 // TODO(epoger): Make sure this still works even though the 1685 // filename now contains the config name (it used to contain 1686 // just the shortName). I think this is actually an 1687 // *improvement*, because now runs with different configs will 1688 // write out their SkPictures to separate files rather than 1689 // overwriting each other. But we should make sure it doesn't 1690 // break anybody. 1691 SkString path = gmmain.make_filename(FLAGS_writePicturePath[0], gm->shortName(), 1692 compareConfig.fName, "", pictureSuffix); 1693 SkFILEWStream stream(path.c_str()); 1694 pict->serialize(&stream); 1695 } 1696 1697 if (FLAGS_rtree) { 1698 const char renderModeDescriptor[] = "-rtree"; 1699 if (gmFlags & GM::kSkipPicture_Flag) { 1700 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1701 renderModeDescriptor); 1702 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1703 } else { 1704 SkPicture* pict = gmmain.generate_new_picture( 1705 gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag); 1706 SkAutoUnref aur(pict); 1707 SkBitmap bitmap; 1708 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); 1709 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1710 gm->shortName(), compareConfig.fName, renderModeDescriptor, bitmap, 1711 &comparisonBitmap)); 1712 } 1713 } 1714 1715 if (FLAGS_tileGrid) { 1716 for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) { 1717 SkScalar replayScale = tileGridReplayScales[scaleIndex]; 1718 SkString renderModeDescriptor("-tilegrid"); 1719 if (SK_Scalar1 != replayScale) { 1720 renderModeDescriptor += "-scale-"; 1721 renderModeDescriptor.appendScalar(replayScale); 1722 } 1723 1724 if ((gmFlags & GM::kSkipPicture_Flag) || 1725 ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)) { 1726 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlusConfig, 1727 renderModeDescriptor.c_str()); 1728 errorsForAllModes.add(kIntentionallySkipped_ErrorType); 1729 } else { 1730 // We record with the reciprocal scale to obtain a replay 1731 // result that can be validated against comparisonBitmap. 1732 SkScalar recordScale = SkScalarInvert(replayScale); 1733 SkPicture* pict = gmmain.generate_new_picture( 1734 gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag, 1735 recordScale); 1736 SkAutoUnref aur(pict); 1737 SkBitmap bitmap; 1738 // We cannot yet pass 'true' to generate_image_from_picture to 1739 // perform actual tiled rendering (see Issue 1198 - 1740 // https://code.google.com/p/skia/issues/detail?id=1198) 1741 gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap, 1742 replayScale /*, true */); 1743 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( 1744 gm->shortName(), compareConfig.fName, renderModeDescriptor.c_str(), bitmap, 1745 &comparisonBitmap)); 1746 } 1747 } 1748 } 1749 1750 // run the pipe centric GM steps 1751 if (FLAGS_pipe) { 1752 errorsForAllModes.add(gmmain.test_pipe_playback(gm, compareConfig, comparisonBitmap, 1753 FLAGS_simulatePipePlaybackFailure)); 1754 if (FLAGS_tiledPipe) { 1755 errorsForAllModes.add(gmmain.test_tiled_pipe_playback(gm, compareConfig, 1756 comparisonBitmap)); 1757 } 1758 } 1759 return errorsForAllModes; 1760} 1761 1762/** 1763 * Return a list of all entries in an array of strings as a single string 1764 * of this form: 1765 * "item1", "item2", "item3" 1766 */ 1767SkString list_all(const SkTArray<SkString> &stringArray); 1768SkString list_all(const SkTArray<SkString> &stringArray) { 1769 SkString total; 1770 for (int i = 0; i < stringArray.count(); i++) { 1771 if (i > 0) { 1772 total.append(", "); 1773 } 1774 total.append("\""); 1775 total.append(stringArray[i]); 1776 total.append("\""); 1777 } 1778 return total; 1779} 1780 1781/** 1782 * Return a list of configuration names, as a single string of this form: 1783 * "item1", "item2", "item3" 1784 * 1785 * @param configs configurations, as a list of indices into gRec 1786 */ 1787SkString list_all_config_names(const SkTDArray<size_t> &configs); 1788SkString list_all_config_names(const SkTDArray<size_t> &configs) { 1789 SkString total; 1790 for (int i = 0; i < configs.count(); i++) { 1791 if (i > 0) { 1792 total.append(", "); 1793 } 1794 total.append("\""); 1795 total.append(gRec[configs[i]].fName); 1796 total.append("\""); 1797 } 1798 return total; 1799} 1800 1801bool prepare_subdirectories(const char *root, bool useFileHierarchy, 1802 const SkTDArray<size_t> &configs); 1803bool prepare_subdirectories(const char *root, bool useFileHierarchy, 1804 const SkTDArray<size_t> &configs) { 1805 if (!sk_mkdir(root)) { 1806 return false; 1807 } 1808 if (useFileHierarchy) { 1809 for (int i = 0; i < configs.count(); i++) { 1810 ConfigData config = gRec[configs[i]]; 1811 SkString subdir; 1812 subdir.appendf("%s%c%s", root, SkPATH_SEPARATOR, config.fName); 1813 if (!sk_mkdir(subdir.c_str())) { 1814 return false; 1815 } 1816 } 1817 } 1818 return true; 1819} 1820 1821int tool_main(int argc, char** argv); 1822int tool_main(int argc, char** argv) { 1823 1824#if SK_ENABLE_INST_COUNT 1825 gPrintInstCount = true; 1826#endif 1827 1828 SkGraphics::Init(); 1829 // we don't need to see this during a run 1830 gSkSuppressFontCachePurgeSpew = true; 1831 1832 setSystemPreferences(); 1833 GMMain gmmain; 1834 1835 SkTDArray<size_t> configs; 1836 SkTDArray<size_t> excludeConfigs; 1837 bool userConfig = false; 1838 1839 SkString usage; 1840 usage.printf("Run the golden master tests.\n"); 1841 SkCommandLineFlags::SetUsage(usage.c_str()); 1842 SkCommandLineFlags::Parse(argc, argv); 1843 1844 gmmain.fUseFileHierarchy = FLAGS_hierarchy; 1845 gmmain.fWriteChecksumBasedFilenames = FLAGS_writeChecksumBasedFilenames; 1846 if (FLAGS_mismatchPath.count() == 1) { 1847 gmmain.fMismatchPath = FLAGS_mismatchPath[0]; 1848 } 1849 if (FLAGS_missingExpectationsPath.count() == 1) { 1850 gmmain.fMissingExpectationsPath = FLAGS_missingExpectationsPath[0]; 1851 } 1852 1853 for (int i = 0; i < FLAGS_config.count(); i++) { 1854 const char* config = FLAGS_config[i]; 1855 userConfig = true; 1856 bool exclude = false; 1857 if (*config == kExcludeConfigChar) { 1858 exclude = true; 1859 config += 1; 1860 } 1861 int index = findConfig(config); 1862 if (index >= 0) { 1863 if (exclude) { 1864 *excludeConfigs.append() = index; 1865 } else { 1866 appendUnique<size_t>(&configs, index); 1867 } 1868 } else if (0 == strcmp(kDefaultsConfigStr, config)) { 1869 for (size_t c = 0; c < SK_ARRAY_COUNT(gRec); ++c) { 1870 if (gRec[c].fRunByDefault) { 1871 if (exclude) { 1872 gm_fprintf(stderr, "%c%s is not allowed.\n", 1873 kExcludeConfigChar, kDefaultsConfigStr); 1874 return -1; 1875 } else { 1876 appendUnique<size_t>(&configs, c); 1877 } 1878 } 1879 } 1880 } else { 1881 gm_fprintf(stderr, "unrecognized config %s\n", config); 1882 return -1; 1883 } 1884 } 1885 1886 for (int i = 0; i < FLAGS_excludeConfig.count(); i++) { 1887 int index = findConfig(FLAGS_excludeConfig[i]); 1888 if (index >= 0) { 1889 *excludeConfigs.append() = index; 1890 } else { 1891 gm_fprintf(stderr, "unrecognized excludeConfig %s\n", FLAGS_excludeConfig[i]); 1892 return -1; 1893 } 1894 } 1895 1896 int moduloRemainder = -1; 1897 int moduloDivisor = -1; 1898 1899 if (FLAGS_modulo.count() == 2) { 1900 moduloRemainder = atoi(FLAGS_modulo[0]); 1901 moduloDivisor = atoi(FLAGS_modulo[1]); 1902 if (moduloRemainder < 0 || moduloDivisor <= 0 || moduloRemainder >= moduloDivisor) { 1903 gm_fprintf(stderr, "invalid modulo values."); 1904 return -1; 1905 } 1906 } 1907 1908 if (FLAGS_ignoreErrorTypes.count() > 0) { 1909 gmmain.fIgnorableErrorTypes = ErrorCombination(); 1910 for (int i = 0; i < FLAGS_ignoreErrorTypes.count(); i++) { 1911 ErrorType type; 1912 const char *name = FLAGS_ignoreErrorTypes[i]; 1913 if (!getErrorTypeByName(name, &type)) { 1914 gm_fprintf(stderr, "cannot find ErrorType with name '%s'\n", name); 1915 return -1; 1916 } else { 1917 gmmain.fIgnorableErrorTypes.add(type); 1918 } 1919 } 1920 } 1921 1922#if SK_SUPPORT_GPU 1923 if (FLAGS_gpuCacheSize.count() > 0) { 1924 if (FLAGS_gpuCacheSize.count() != 2) { 1925 gm_fprintf(stderr, "--gpuCacheSize requires two arguments\n"); 1926 return -1; 1927 } 1928 gGpuCacheSizeBytes = atoi(FLAGS_gpuCacheSize[0]); 1929 gGpuCacheSizeCount = atoi(FLAGS_gpuCacheSize[1]); 1930 } else { 1931 gGpuCacheSizeBytes = DEFAULT_CACHE_VALUE; 1932 gGpuCacheSizeCount = DEFAULT_CACHE_VALUE; 1933 } 1934#endif 1935 1936 SkTDArray<SkScalar> tileGridReplayScales; 1937 *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0 1938 if (FLAGS_tileGridReplayScales.count() > 0) { 1939 tileGridReplayScales.reset(); 1940 for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) { 1941 double val = atof(FLAGS_tileGridReplayScales[i]); 1942 if (0 < val) { 1943 *tileGridReplayScales.append() = SkDoubleToScalar(val); 1944 } 1945 } 1946 if (0 == tileGridReplayScales.count()) { 1947 // Should have at least one scale 1948 gm_fprintf(stderr, "--tileGridReplayScales requires at least one scale.\n"); 1949 return -1; 1950 } 1951 } 1952 1953 if (!userConfig) { 1954 // if no config is specified by user, add the defaults 1955 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1956 if (gRec[i].fRunByDefault) { 1957 *configs.append() = i; 1958 } 1959 } 1960 } 1961 // now remove any explicitly excluded configs 1962 for (int i = 0; i < excludeConfigs.count(); ++i) { 1963 int index = configs.find(excludeConfigs[i]); 1964 if (index >= 0) { 1965 configs.remove(index); 1966 // now assert that there was only one copy in configs[] 1967 SkASSERT(configs.find(excludeConfigs[i]) < 0); 1968 } 1969 } 1970 1971#if SK_SUPPORT_GPU 1972 GrContextFactory* grFactory = new GrContextFactory; 1973 for (int i = 0; i < configs.count(); ++i) { 1974 size_t index = configs[i]; 1975 if (kGPU_Backend == gRec[index].fBackend) { 1976 GrContext* ctx = grFactory->get(gRec[index].fGLContextType); 1977 if (NULL == ctx) { 1978 gm_fprintf(stderr, "GrContext could not be created for config %s." 1979 " Config will be skipped.\n", gRec[index].fName); 1980 configs.remove(i); 1981 --i; 1982 continue; 1983 } 1984 if (gRec[index].fSampleCnt > ctx->getMaxSampleCount()) { 1985 gm_fprintf(stderr, "Sample count (%d) of config %s is not supported." 1986 " Config will be skipped.\n", gRec[index].fSampleCnt, gRec[index].fName); 1987 configs.remove(i); 1988 --i; 1989 } 1990 } 1991 } 1992#else 1993 GrContextFactory* grFactory = NULL; 1994#endif 1995 1996 if (configs.isEmpty()) { 1997 gm_fprintf(stderr, "No configs to run."); 1998 return -1; 1999 } 2000 2001 // now show the user the set of configs that will be run. 2002 SkString configStr("These configs will be run: "); 2003 // show the user the config that will run. 2004 for (int i = 0; i < configs.count(); ++i) { 2005 configStr.appendf("%s%s", gRec[configs[i]].fName, (i == configs.count() - 1) ? "\n" : " "); 2006 } 2007 gm_fprintf(stdout, "%s", configStr.c_str()); 2008 2009 if (FLAGS_resourcePath.count() == 1) { 2010 GM::SetResourcePath(FLAGS_resourcePath[0]); 2011 } 2012 2013 if (FLAGS_readPath.count() == 1) { 2014 const char* readPath = FLAGS_readPath[0]; 2015 if (!sk_exists(readPath)) { 2016 gm_fprintf(stderr, "readPath %s does not exist!\n", readPath); 2017 return -1; 2018 } 2019 if (sk_isdir(readPath)) { 2020 if (FLAGS_verbose) { 2021 gm_fprintf(stdout, "reading from %s\n", readPath); 2022 } 2023 gmmain.fExpectationsSource.reset(SkNEW_ARGS( 2024 IndividualImageExpectationsSource, (readPath))); 2025 } else { 2026 if (FLAGS_verbose) { 2027 gm_fprintf(stdout, "reading expectations from JSON summary file %s\n", readPath); 2028 } 2029 gmmain.fExpectationsSource.reset(SkNEW_ARGS( 2030 JsonExpectationsSource, (readPath))); 2031 } 2032 } 2033 if (FLAGS_verbose) { 2034 if (FLAGS_writePath.count() == 1) { 2035 gm_fprintf(stdout, "writing to %s\n", FLAGS_writePath[0]); 2036 } 2037 if (NULL != gmmain.fMismatchPath) { 2038 gm_fprintf(stdout, "writing mismatches to %s\n", gmmain.fMismatchPath); 2039 } 2040 if (NULL != gmmain.fMissingExpectationsPath) { 2041 gm_fprintf(stdout, "writing images without expectations to %s\n", 2042 gmmain.fMissingExpectationsPath); 2043 } 2044 if (FLAGS_writePicturePath.count() == 1) { 2045 gm_fprintf(stdout, "writing pictures to %s\n", FLAGS_writePicturePath[0]); 2046 } 2047 if (FLAGS_resourcePath.count() == 1) { 2048 gm_fprintf(stdout, "reading resources from %s\n", FLAGS_resourcePath[0]); 2049 } 2050 } 2051 2052 if (moduloDivisor <= 0) { 2053 moduloRemainder = -1; 2054 } 2055 if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) { 2056 moduloRemainder = -1; 2057 } 2058 2059 int gmsRun = 0; 2060 int gmIndex = -1; 2061 SkString moduloStr; 2062 2063 // If we will be writing out files, prepare subdirectories. 2064 if (FLAGS_writePath.count() == 1) { 2065 if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHierarchy, configs)) { 2066 return -1; 2067 } 2068 } 2069 if (NULL != gmmain.fMismatchPath) { 2070 if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHierarchy, configs)) { 2071 return -1; 2072 } 2073 } 2074 if (NULL != gmmain.fMissingExpectationsPath) { 2075 if (!prepare_subdirectories(gmmain.fMissingExpectationsPath, gmmain.fUseFileHierarchy, 2076 configs)) { 2077 return -1; 2078 } 2079 } 2080 2081 if (FLAGS_pdfJpegQuality < -1 || FLAGS_pdfJpegQuality > 100) { 2082 gm_fprintf(stderr, "%s\n", "pdfJpegQuality must be in [-1 .. 100] range."); 2083 } 2084 2085 Iter iter; 2086 GM* gm; 2087 while ((gm = iter.next()) != NULL) { 2088 SkAutoTDelete<GM> adgm(gm); 2089 ++gmIndex; 2090 if (moduloRemainder >= 0) { 2091 if ((gmIndex % moduloDivisor) != moduloRemainder) { 2092 continue; 2093 } 2094 moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor); 2095 } 2096 2097 const char* shortName = gm->shortName(); 2098 2099 SkTDArray<const char*> matchStrs; 2100 for (int i = 0; i < FLAGS_match.count(); ++i) { 2101 matchStrs.push(FLAGS_match[i]); 2102 } 2103 if (SkCommandLineFlags::ShouldSkip(matchStrs, shortName)) { 2104 continue; 2105 } 2106 2107 gmsRun++; 2108 SkISize size = gm->getISize(); 2109 gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName, 2110 size.width(), size.height()); 2111 2112 run_multiple_configs(gmmain, gm, configs, grFactory); 2113 2114 SkBitmap comparisonBitmap; 2115 const ConfigData compareConfig = 2116 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison", false }; 2117 gmmain.generate_image(gm, compareConfig, NULL, &comparisonBitmap, false); 2118 2119 // TODO(epoger): only run this if gmmain.generate_image() succeeded? 2120 // Otherwise, what are we comparing against? 2121 run_multiple_modes(gmmain, gm, compareConfig, comparisonBitmap, tileGridReplayScales); 2122 } 2123 2124 SkTArray<SkString> modes; 2125 gmmain.GetRenderModesEncountered(modes); 2126 bool reportError = false; 2127 if (gmmain.NumSignificantErrors() > 0) { 2128 reportError = true; 2129 } 2130 int expectedNumberOfTests = gmsRun * (configs.count() + modes.count()); 2131 2132 // Output summary to stdout. 2133 if (FLAGS_verbose) { 2134 gm_fprintf(stdout, "Ran %d GMs\n", gmsRun); 2135 gm_fprintf(stdout, "... over %2d configs [%s]\n", configs.count(), 2136 list_all_config_names(configs).c_str()); 2137 gm_fprintf(stdout, "... and %2d modes [%s]\n", modes.count(), list_all(modes).c_str()); 2138 gm_fprintf(stdout, "... so there should be a total of %d tests.\n", expectedNumberOfTests); 2139 } 2140 gmmain.ListErrors(FLAGS_verbose); 2141 2142 // TODO(epoger): Enable this check for Android, too, once we resolve 2143 // https://code.google.com/p/skia/issues/detail?id=1222 2144 // ('GM is unexpectedly skipping tests on Android') 2145#ifndef SK_BUILD_FOR_ANDROID 2146 if (expectedNumberOfTests != gmmain.fTestsRun) { 2147 gm_fprintf(stderr, "expected %d tests, but ran or skipped %d tests\n", 2148 expectedNumberOfTests, gmmain.fTestsRun); 2149 reportError = true; 2150 } 2151#endif 2152 2153 if (FLAGS_writeJsonSummaryPath.count() == 1) { 2154 Json::Value root = CreateJsonTree( 2155 gmmain.fJsonExpectedResults, 2156 gmmain.fJsonActualResults_Failed, gmmain.fJsonActualResults_FailureIgnored, 2157 gmmain.fJsonActualResults_NoComparison, gmmain.fJsonActualResults_Succeeded); 2158 std::string jsonStdString = root.toStyledString(); 2159 SkFILEWStream stream(FLAGS_writeJsonSummaryPath[0]); 2160 stream.write(jsonStdString.c_str(), jsonStdString.length()); 2161 } 2162 2163#if SK_SUPPORT_GPU 2164 2165#if GR_CACHE_STATS 2166 for (int i = 0; i < configs.count(); i++) { 2167 ConfigData config = gRec[configs[i]]; 2168 2169 if (FLAGS_verbose && (kGPU_Backend == config.fBackend)) { 2170 GrContext* gr = grFactory->get(config.fGLContextType); 2171 2172 gm_fprintf(stdout, "config: %s %x\n", config.fName, gr); 2173 gr->printCacheStats(); 2174 } 2175 } 2176#endif 2177 2178 delete grFactory; 2179#endif 2180 SkGraphics::Term(); 2181 2182 return (reportError) ? -1 : 0; 2183} 2184 2185void GMMain::installFilter(SkCanvas* canvas) { 2186 if (FLAGS_forceBWtext) { 2187 canvas->setDrawFilter(SkNEW(BWTextDrawFilter))->unref(); 2188 } 2189} 2190 2191#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 2192int main(int argc, char * const argv[]) { 2193 return tool_main(argc, (char**) argv); 2194} 2195#endif 2196