OLD | NEW |
(Empty) | |
| 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 "CrashHandler.h" |
| 21 #include "ProcStats.h" |
| 22 #include "Resources.h" |
| 23 #include "SamplePipeControllers.h" |
| 24 #include "SkBitmap.h" |
| 25 #include "SkColorPriv.h" |
| 26 #include "SkCommandLineFlags.h" |
| 27 #include "SkData.h" |
| 28 #include "SkDeferredCanvas.h" |
| 29 #include "SkDevice.h" |
| 30 #include "SkDocument.h" |
| 31 #include "SkDrawFilter.h" |
| 32 #include "SkForceLinking.h" |
| 33 #include "SkGPipe.h" |
| 34 #include "SkGraphics.h" |
| 35 #include "SkImageDecoder.h" |
| 36 #include "SkImageEncoder.h" |
| 37 #include "SkJSONCPP.h" |
| 38 #include "SkMultiPictureDraw.h" |
| 39 #include "SkOSFile.h" |
| 40 #include "SkPDFRasterizer.h" |
| 41 #include "SkPicture.h" |
| 42 #include "SkPictureRecorder.h" |
| 43 #include "SkRefCnt.h" |
| 44 #include "SkScalar.h" |
| 45 #include "SkStream.h" |
| 46 #include "SkString.h" |
| 47 #include "SkSurface.h" |
| 48 #include "SkTArray.h" |
| 49 #include "SkTDict.h" |
| 50 |
| 51 #ifdef SK_DEBUG |
| 52 static const bool kDebugOnly = true; |
| 53 #define GR_DUMP_FONT_CACHE 0 |
| 54 #else |
| 55 static const bool kDebugOnly = false; |
| 56 #endif |
| 57 |
| 58 __SK_FORCE_IMAGE_DECODER_LINKING; |
| 59 |
| 60 #if SK_SUPPORT_GPU |
| 61 #include "GrContextFactory.h" |
| 62 #include "SkGpuDevice.h" |
| 63 typedef GrContextFactory::GLContextType GLContextType; |
| 64 #define DEFAULT_CACHE_VALUE -1 |
| 65 static int gGpuCacheSizeBytes; |
| 66 static int gGpuCacheSizeCount; |
| 67 #else |
| 68 class GrContextFactory; |
| 69 class GrContext; |
| 70 class GrSurface; |
| 71 typedef int GLContextType; |
| 72 typedef int GrGLStandard; |
| 73 #endif |
| 74 |
| 75 #define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message") |
| 76 |
| 77 DECLARE_bool(useDocumentInsteadOfDevice); |
| 78 |
| 79 #ifdef SK_SUPPORT_PDF |
| 80 #include "SkPDFDevice.h" |
| 81 #include "SkPDFDocument.h" |
| 82 #endif |
| 83 |
| 84 // Until we resolve http://code.google.com/p/skia/issues/detail?id=455 , |
| 85 // stop writing out XPS-format image baselines in gm. |
| 86 #undef SK_SUPPORT_XPS |
| 87 #ifdef SK_SUPPORT_XPS |
| 88 #include "SkXPSDevice.h" |
| 89 #endif |
| 90 |
| 91 #ifdef SK_BUILD_FOR_MAC |
| 92 #include "SkCGUtils.h" |
| 93 #endif |
| 94 |
| 95 using namespace skiagm; |
| 96 |
| 97 class Iter { |
| 98 public: |
| 99 Iter() { |
| 100 this->reset(); |
| 101 } |
| 102 |
| 103 void reset() { |
| 104 fReg = GMRegistry::Head(); |
| 105 } |
| 106 |
| 107 GM* next() { |
| 108 if (fReg) { |
| 109 GMRegistry::Factory fact = fReg->factory(); |
| 110 fReg = fReg->next(); |
| 111 return fact(0); |
| 112 } |
| 113 return NULL; |
| 114 } |
| 115 |
| 116 static int Count() { |
| 117 const GMRegistry* reg = GMRegistry::Head(); |
| 118 int count = 0; |
| 119 while (reg) { |
| 120 count += 1; |
| 121 reg = reg->next(); |
| 122 } |
| 123 return count; |
| 124 } |
| 125 |
| 126 private: |
| 127 const GMRegistry* fReg; |
| 128 }; |
| 129 |
| 130 // TODO(epoger): Right now, various places in this code assume that all the |
| 131 // image files read/written by GM use this file extension. |
| 132 // Search for references to this constant to find these assumptions. |
| 133 const static char kPNG_FileExtension[] = "png"; |
| 134 |
| 135 enum Backend { |
| 136 kRaster_Backend, |
| 137 kGPU_Backend, |
| 138 kPDF_Backend, |
| 139 kXPS_Backend, |
| 140 }; |
| 141 |
| 142 enum BbhType { |
| 143 kNone_BbhType, |
| 144 kRTree_BbhType, |
| 145 }; |
| 146 |
| 147 enum 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 /* Use distance fields for rendering text */ |
| 155 kDFText_ConfigFlag = 0x4, |
| 156 kRWDFT_ConfigFlag = (kRW_ConfigFlag | kDFText_ConfigFlag), |
| 157 }; |
| 158 |
| 159 struct ConfigData { |
| 160 SkColorType fColorType; |
| 161 Backend fBackend; |
| 162 GLContextType fGLContextType; // GPU backend only |
| 163 int fSampleCnt; // GPU backend only |
| 164 ConfigFlags fFlags; |
| 165 const char* fName; |
| 166 bool fRunByDefault; |
| 167 }; |
| 168 |
| 169 struct PDFRasterizerData { |
| 170 bool (*fRasterizerFunction)(SkStream*, SkBitmap*); |
| 171 const char* fName; |
| 172 bool fRunByDefault; |
| 173 }; |
| 174 |
| 175 class BWTextDrawFilter : public SkDrawFilter { |
| 176 public: |
| 177 bool filter(SkPaint*, Type) SK_OVERRIDE; |
| 178 }; |
| 179 bool BWTextDrawFilter::filter(SkPaint* p, Type t) { |
| 180 if (kText_Type == t) { |
| 181 p->setAntiAlias(false); |
| 182 } |
| 183 return true; |
| 184 } |
| 185 |
| 186 struct PipeFlagComboData { |
| 187 const char* name; |
| 188 uint32_t flags; |
| 189 }; |
| 190 |
| 191 static PipeFlagComboData gPipeWritingFlagCombos[] = { |
| 192 { "", 0 }, |
| 193 { " cross-process", SkGPipeWriter::kCrossProcess_Flag }, |
| 194 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag |
| 195 | SkGPipeWriter::kSharedAddressSpace_Flag } |
| 196 }; |
| 197 |
| 198 static SkData* encode_to_dct_data(size_t* pixelRefOffset, const SkBitmap& bitmap
); |
| 199 DECLARE_int32(pdfRasterDpi); |
| 200 |
| 201 const static ErrorCombination kDefaultIgnorableErrorTypes = ErrorCombination() |
| 202 .plus(kMissingExpectations_ErrorType) |
| 203 .plus(kIntentionallySkipped_ErrorType); |
| 204 |
| 205 class GMMain { |
| 206 public: |
| 207 GMMain() : fUseFileHierarchy(false), fWriteChecksumBasedFilenames(false), |
| 208 fIgnorableErrorTypes(kDefaultIgnorableErrorTypes), |
| 209 fMismatchPath(NULL), fMissingExpectationsPath(NULL), fTestsRun(0)
, |
| 210 fRenderModesEncountered(1) {} |
| 211 |
| 212 /** |
| 213 * Assemble shortNamePlusConfig from (surprise!) shortName and configName. |
| 214 * |
| 215 * The method for doing so depends on whether we are using hierarchical nami
ng. |
| 216 * For example, shortName "selftest1" and configName "8888" could be assembl
ed into |
| 217 * either "selftest1_8888" or "8888/selftest1". |
| 218 */ |
| 219 SkString make_shortname_plus_config(const char *shortName, const char *confi
gName) { |
| 220 SkString name; |
| 221 if (0 == strlen(configName)) { |
| 222 name.append(shortName); |
| 223 } else if (fUseFileHierarchy) { |
| 224 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName); |
| 225 } else { |
| 226 name.appendf("%s_%s", shortName, configName); |
| 227 } |
| 228 return name; |
| 229 } |
| 230 |
| 231 /** |
| 232 * Assemble filename, suitable for writing out the results of a particular t
est. |
| 233 */ |
| 234 SkString make_filename(const char *path, |
| 235 const char *shortName, |
| 236 const char *configName, |
| 237 const char *renderModeDescriptor, |
| 238 const char *suffix) { |
| 239 SkString filename = make_shortname_plus_config(shortName, configName); |
| 240 filename.append(renderModeDescriptor); |
| 241 filename.appendUnichar('.'); |
| 242 filename.append(suffix); |
| 243 return SkOSPath::Join(path, filename.c_str()); |
| 244 } |
| 245 |
| 246 /** |
| 247 * Assemble filename suitable for writing out an SkBitmap. |
| 248 */ |
| 249 SkString makeBitmapFilename(const char *path, |
| 250 const char *shortName, |
| 251 const char *configName, |
| 252 const char *renderModeDescriptor, |
| 253 const GmResultDigest &bitmapDigest) { |
| 254 if (fWriteChecksumBasedFilenames) { |
| 255 SkString filename; |
| 256 filename.append(bitmapDigest.getHashType()); |
| 257 filename.appendUnichar('_'); |
| 258 filename.append(shortName); |
| 259 filename.appendUnichar('_'); |
| 260 filename.append(bitmapDigest.getDigestValue()); |
| 261 filename.appendUnichar('.'); |
| 262 filename.append(kPNG_FileExtension); |
| 263 return SkOSPath::Join(path, filename.c_str()); |
| 264 } else { |
| 265 return make_filename(path, shortName, configName, renderModeDescript
or, |
| 266 kPNG_FileExtension); |
| 267 } |
| 268 } |
| 269 |
| 270 /* since PNG insists on unpremultiplying our alpha, we take no |
| 271 precision chances and force all pixels to be 100% opaque, |
| 272 otherwise on compare we may not get a perfect match. |
| 273 */ |
| 274 static void force_all_opaque(const SkBitmap& bitmap) { |
| 275 SkColorType colorType = bitmap.colorType(); |
| 276 switch (colorType) { |
| 277 case kN32_SkColorType: |
| 278 force_all_opaque_8888(bitmap); |
| 279 break; |
| 280 case kRGB_565_SkColorType: |
| 281 // nothing to do here; 565 bitmaps are inherently opaque |
| 282 break; |
| 283 default: |
| 284 SkDebugf("unsupported bitmap colorType %d\n", colorType); |
| 285 DEBUGFAIL_SEE_STDERR; |
| 286 } |
| 287 } |
| 288 |
| 289 static void force_all_opaque_8888(const SkBitmap& bitmap) { |
| 290 SkAutoLockPixels lock(bitmap); |
| 291 for (int y = 0; y < bitmap.height(); y++) { |
| 292 for (int x = 0; x < bitmap.width(); x++) { |
| 293 *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); |
| 294 } |
| 295 } |
| 296 } |
| 297 |
| 298 static ErrorCombination WriteBitmap(const SkString& path, const SkBitmap& bi
tmap) { |
| 299 // TODO(epoger): Now that we have removed force_all_opaque() |
| 300 // from this method, we should be able to get rid of the |
| 301 // transformation to 8888 format also. |
| 302 SkBitmap copy; |
| 303 bitmap.copyTo(©, kN32_SkColorType); |
| 304 if (!SkImageEncoder::EncodeFile(path.c_str(), copy, |
| 305 SkImageEncoder::kPNG_Type, |
| 306 100)) { |
| 307 SkDebugf("FAILED to write bitmap: %s\n", path.c_str()); |
| 308 return ErrorCombination(kWritingReferenceImage_ErrorType); |
| 309 } |
| 310 return kEmpty_ErrorCombination; |
| 311 } |
| 312 |
| 313 /** |
| 314 * Add all render modes encountered thus far to the "modes" array. |
| 315 */ |
| 316 void GetRenderModesEncountered(SkTArray<SkString> &modes) { |
| 317 SkTDict<int>::Iter iter(this->fRenderModesEncountered); |
| 318 const char* mode; |
| 319 while ((mode = iter.next(NULL)) != NULL) { |
| 320 SkString modeAsString = SkString(mode); |
| 321 // TODO(epoger): It seems a bit silly that all of these modes were |
| 322 // recorded with a leading "-" which we have to remove here |
| 323 // (except for mode "", which means plain old original mode). |
| 324 // But that's how renderModeDescriptor has been passed into |
| 325 // compare_test_results_to_reference_bitmap() historically, |
| 326 // and changing that now may affect other parts of our code. |
| 327 if (modeAsString.startsWith("-")) { |
| 328 modeAsString.remove(0, 1); |
| 329 } |
| 330 modes.push_back(modeAsString); |
| 331 } |
| 332 } |
| 333 |
| 334 /** |
| 335 * Returns true if failures on this test should be ignored. |
| 336 */ |
| 337 bool shouldIgnoreTest(const char *name) const { |
| 338 for (int i = 0; i < fIgnorableTestNames.count(); i++) { |
| 339 if (fIgnorableTestNames[i].equals(name)) { |
| 340 return true; |
| 341 } |
| 342 } |
| 343 return false; |
| 344 } |
| 345 |
| 346 /** |
| 347 * Calls RecordTestResults to record that we skipped a test. |
| 348 * |
| 349 * Depending on the backend, this may mean that we skipped a single rendermo
de, or all |
| 350 * rendermodes; see http://skbug.com/1994 and https://codereview.chromium.or
g/129203002/ |
| 351 */ |
| 352 void RecordSkippedTest(const SkString& shortNamePlusConfig, |
| 353 const char renderModeDescriptor [], Backend backend)
{ |
| 354 if (kRaster_Backend == backend) { |
| 355 // Skipping a test on kRaster_Backend means that we will skip ALL re
nderModes |
| 356 // (as opposed to other backends, on which we only run the default r
enderMode). |
| 357 // |
| 358 // We cannot call RecordTestResults yet, because we won't know the f
ull set of |
| 359 // renderModes until we have run all tests. |
| 360 fTestsSkippedOnAllRenderModes.push_back(shortNamePlusConfig); |
| 361 } else { |
| 362 this->RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePl
usConfig, |
| 363 renderModeDescriptor); |
| 364 } |
| 365 } |
| 366 |
| 367 /** |
| 368 * Records the results of this test in fTestsRun and fFailedTests. |
| 369 * |
| 370 * We even record successes, and errors that we regard as |
| 371 * "ignorable"; we can filter them out later. |
| 372 */ |
| 373 void RecordTestResults(const ErrorCombination& errorCombination, |
| 374 const SkString& shortNamePlusConfig, |
| 375 const char renderModeDescriptor []) { |
| 376 // Things to do regardless of errorCombination. |
| 377 fTestsRun++; |
| 378 int renderModeCount = 0; |
| 379 this->fRenderModesEncountered.find(renderModeDescriptor, &renderModeCoun
t); |
| 380 renderModeCount++; |
| 381 this->fRenderModesEncountered.set(renderModeDescriptor, renderModeCount)
; |
| 382 |
| 383 if (errorCombination.isEmpty()) { |
| 384 return; |
| 385 } |
| 386 |
| 387 // Things to do only if there is some error condition. |
| 388 SkString fullName = shortNamePlusConfig; |
| 389 fullName.append(renderModeDescriptor); |
| 390 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { |
| 391 ErrorType type = static_cast<ErrorType>(typeInt); |
| 392 if (errorCombination.includes(type)) { |
| 393 fFailedTests[type].push_back(fullName); |
| 394 } |
| 395 } |
| 396 } |
| 397 |
| 398 /** |
| 399 * Return the number of significant (non-ignorable) errors we have |
| 400 * encountered so far. |
| 401 */ |
| 402 int NumSignificantErrors() { |
| 403 int significantErrors = 0; |
| 404 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { |
| 405 ErrorType type = static_cast<ErrorType>(typeInt); |
| 406 if (!fIgnorableErrorTypes.includes(type)) { |
| 407 significantErrors += fFailedTests[type].count(); |
| 408 } |
| 409 } |
| 410 return significantErrors; |
| 411 } |
| 412 |
| 413 /** |
| 414 * Display the summary of results with this ErrorType. |
| 415 * |
| 416 * @param type which ErrorType |
| 417 * @param verbose whether to be all verbose about it |
| 418 */ |
| 419 void DisplayResultTypeSummary(ErrorType type, bool verbose) { |
| 420 bool isIgnorableType = fIgnorableErrorTypes.includes(type); |
| 421 |
| 422 SkString line; |
| 423 if (isIgnorableType) { |
| 424 line.append("[ ] "); |
| 425 } else { |
| 426 line.append("[*] "); |
| 427 } |
| 428 |
| 429 SkTArray<SkString> *failedTestsOfThisType = &fFailedTests[type]; |
| 430 int count = failedTestsOfThisType->count(); |
| 431 line.appendf("%d %s", count, getErrorTypeName(type)); |
| 432 if (!isIgnorableType || verbose) { |
| 433 line.append(":"); |
| 434 for (int i = 0; i < count; ++i) { |
| 435 line.append(" "); |
| 436 line.append((*failedTestsOfThisType)[i]); |
| 437 } |
| 438 } |
| 439 SkDebugf("%s\n", line.c_str()); |
| 440 } |
| 441 |
| 442 /** |
| 443 * List contents of fFailedTests to stdout. |
| 444 * |
| 445 * @param verbose whether to be all verbose about it |
| 446 */ |
| 447 void ListErrors(bool verbose) { |
| 448 // First, print a single summary line. |
| 449 SkString summary; |
| 450 summary.appendf("Ran %d tests:", fTestsRun); |
| 451 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { |
| 452 ErrorType type = static_cast<ErrorType>(typeInt); |
| 453 summary.appendf(" %s=%d", getErrorTypeName(type), fFailedTests[type]
.count()); |
| 454 } |
| 455 SkDebugf("%s\n", summary.c_str()); |
| 456 |
| 457 // Now, for each failure type, list the tests that failed that way. |
| 458 for (int typeInt = 0; typeInt <= kLast_ErrorType; typeInt++) { |
| 459 this->DisplayResultTypeSummary(static_cast<ErrorType>(typeInt), verb
ose); |
| 460 } |
| 461 SkDebugf("(results marked with [*] will cause nonzero return value)\n"); |
| 462 } |
| 463 |
| 464 static ErrorCombination write_document(const SkString& path, SkStreamAsset*
asset) { |
| 465 SkFILEWStream stream(path.c_str()); |
| 466 if (!stream.writeStream(asset, asset->getLength())) { |
| 467 SkDebugf("FAILED to write document: %s\n", path.c_str()); |
| 468 return ErrorCombination(kWritingReferenceImage_ErrorType); |
| 469 } |
| 470 return kEmpty_ErrorCombination; |
| 471 } |
| 472 |
| 473 /** |
| 474 * Prepare an SkBitmap to render a GM into. |
| 475 * |
| 476 * After you've rendered the GM into the SkBitmap, you must call |
| 477 * complete_bitmap()! |
| 478 * |
| 479 * @todo thudson 22 April 2011 - could refactor this to take in |
| 480 * a factory to generate the context, always call readPixels() |
| 481 * (logically a noop for rasters, if wasted time), and thus collapse the |
| 482 * GPU special case and also let this be used for SkPicture testing. |
| 483 */ |
| 484 static void setup_bitmap(const ConfigData& gRec, const SkISize& size, |
| 485 SkBitmap* bitmap) { |
| 486 bitmap->allocPixels(SkImageInfo::Make(size.width(), size.height(), |
| 487 gRec.fColorType, kPremul_SkAlphaTy
pe)); |
| 488 bitmap->eraseColor(SK_ColorTRANSPARENT); |
| 489 } |
| 490 |
| 491 /** |
| 492 * Any finalization steps we need to perform on the SkBitmap after |
| 493 * we have rendered the GM into it. |
| 494 * |
| 495 * It's too bad that we are throwing away alpha channel data |
| 496 * we could otherwise be examining, but this had always been happening |
| 497 * before... it was buried within the compare() method at |
| 498 * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#3
05 . |
| 499 * |
| 500 * Apparently we need this, at least for bitmaps that are either: |
| 501 * (a) destined to be written out as PNG files, or |
| 502 * (b) compared against bitmaps read in from PNG files |
| 503 * for the reasons described just above the force_all_opaque() method. |
| 504 * |
| 505 * Neglecting to do this led to the difficult-to-diagnose |
| 506 * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating |
| 507 * spurious pixel_error messages as of r7258') |
| 508 * |
| 509 * TODO(epoger): Come up with a better solution that allows us to |
| 510 * compare full pixel data, including alpha channel, while still being |
| 511 * robust in the face of transformations to/from PNG files. |
| 512 * Options include: |
| 513 * |
| 514 * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that |
| 515 * will be written to, or compared against, PNG files. |
| 516 * PRO: Preserve/compare alpha channel info for the non-PNG cases |
| 517 * (comparing different renderModes in-memory) |
| 518 * CON: The bitmaps (and hash digests) for these non-PNG cases would be |
| 519 * different than those for the PNG-compared cases, and in the |
| 520 * case of a failed renderMode comparison, how would we write the |
| 521 * image to disk for examination? |
| 522 * |
| 523 * 2. Always compute image hash digests from PNG format (either |
| 524 * directly from the the bytes of a PNG file, or capturing the |
| 525 * bytes we would have written to disk if we were writing the |
| 526 * bitmap out as a PNG). |
| 527 * PRO: I think this would allow us to never force opaque, and to |
| 528 * the extent that alpha channel data can be preserved in a PNG |
| 529 * file, we could observe it. |
| 530 * CON: If we read a bitmap from disk, we need to take its hash digest |
| 531 * from the source PNG (we can't compute it from the bitmap we |
| 532 * read out of the PNG, because we will have already premultiplied |
| 533 * the alpha). |
| 534 * CON: Seems wasteful to convert a bitmap to PNG format just to take |
| 535 * its hash digest. (Although we're wasting lots of effort already |
| 536 * calling force_all_opaque().) |
| 537 * |
| 538 * 3. Make the alpha premultiply/unpremultiply routines 100% consistent, |
| 539 * so we can transform images back and forth without fear of off-by-one |
| 540 * errors. |
| 541 * CON: Math is hard. |
| 542 * |
| 543 * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each |
| 544 * channel), rather than demanding absolute equality. |
| 545 * CON: Can't do this with hash digests. |
| 546 */ |
| 547 static void complete_bitmap(SkBitmap* bitmap) { |
| 548 force_all_opaque(*bitmap); |
| 549 } |
| 550 |
| 551 static void InstallFilter(SkCanvas* canvas); |
| 552 |
| 553 static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred)
{ |
| 554 SkAutoCanvasRestore acr(canvas, true); |
| 555 |
| 556 if (!isPDF) { |
| 557 canvas->concat(gm->getInitialTransform()); |
| 558 } |
| 559 InstallFilter(canvas); |
| 560 gm->setCanvasIsDeferred(isDeferred); |
| 561 gm->draw(canvas); |
| 562 canvas->setDrawFilter(NULL); |
| 563 } |
| 564 |
| 565 static ErrorCombination generate_image(GM* gm, const ConfigData& gRec, |
| 566 GrSurface* gpuTarget, |
| 567 SkBitmap* bitmap, |
| 568 bool deferred) { |
| 569 const SkISize size (gm->getISize()); |
| 570 |
| 571 SkAutoTUnref<SkSurface> surface(CreateSurface(gRec, size, gpuTarget)); |
| 572 SkAutoTUnref<SkCanvas> canvas; |
| 573 |
| 574 if (deferred) { |
| 575 canvas.reset(SkDeferredCanvas::Create(surface)); |
| 576 } else { |
| 577 canvas.reset(SkRef(surface->getCanvas())); |
| 578 } |
| 579 invokeGM(gm, canvas, false, deferred); |
| 580 canvas->flush(); |
| 581 |
| 582 setup_bitmap(gRec, size, bitmap); |
| 583 surface->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowByte
s(), 0, 0); |
| 584 complete_bitmap(bitmap); |
| 585 return kEmpty_ErrorCombination; |
| 586 } |
| 587 |
| 588 static void DrawPictureToSurface(SkSurface* surf, |
| 589 const SkPicture* pict, |
| 590 SkScalar scale, |
| 591 bool tile, |
| 592 bool useMPD) { |
| 593 SkASSERT(surf->width() == pict->cullRect().width() && |
| 594 surf->height() == pict->cullRect().height()); |
| 595 |
| 596 if (tile) { |
| 597 SkMultiPictureDraw mpd; |
| 598 SkTDArray<SkSurface*> surfaces; |
| 599 |
| 600 const SkISize tileSize = SkISize::Make(16, 16); |
| 601 |
| 602 const SkImageInfo ii = surf->getCanvas()->imageInfo().makeWH(tileSiz
e.width(), |
| 603 tileSiz
e.height()); |
| 604 |
| 605 for (int tileY = 0; tileY < pict->cullRect().height(); tileY += tile
Size.height()) { |
| 606 for (int tileX = 0; tileX < pict->cullRect().width(); tileX += t
ileSize.width()) { |
| 607 |
| 608 *surfaces.append() = surf->getCanvas()->newSurface(ii); |
| 609 |
| 610 InstallFilter(surfaces.top()->getCanvas()); |
| 611 |
| 612 SkMatrix matrix; |
| 613 matrix.setTranslate(-pict->cullRect().fLeft, -pict->cullRect
().fTop); |
| 614 matrix.postTranslate(-SkIntToScalar(tileX), -SkIntToScalar(t
ileY)); |
| 615 matrix.postScale(scale, scale); |
| 616 |
| 617 if (useMPD) { |
| 618 mpd.add(surfaces.top()->getCanvas(), pict, &matrix, NULL
); |
| 619 } else { |
| 620 surfaces.top()->getCanvas()->drawPicture(pict, &matrix,
NULL); |
| 621 } |
| 622 } |
| 623 } |
| 624 |
| 625 mpd.draw(); |
| 626 |
| 627 SkPaint gatherPaint; |
| 628 gatherPaint.setXfermodeMode(SkXfermode::kSrc_Mode); |
| 629 |
| 630 int tileIndex = 0; |
| 631 for (int tileY = 0; tileY < pict->cullRect().height(); tileY += tile
Size.height()) { |
| 632 for (int tileX = 0; tileX < pict->cullRect().width(); tileX += t
ileSize.width()) { |
| 633 surf->getCanvas()->drawImage(surfaces[tileIndex]->newImageSn
apshot(), |
| 634 SkIntToScalar(tileX), SkIntToSc
alar(tileY), |
| 635 &gatherPaint); |
| 636 surfaces[tileIndex]->unref(); |
| 637 tileIndex++; |
| 638 } |
| 639 } |
| 640 } else { |
| 641 InstallFilter(surf->getCanvas()); |
| 642 |
| 643 SkMatrix matrix; |
| 644 matrix.setTranslate(-pict->cullRect().fLeft, -pict->cullRect().fTop)
; |
| 645 matrix.postScale(scale, scale); |
| 646 |
| 647 if (useMPD) { |
| 648 SkMultiPictureDraw mpd; |
| 649 mpd.add(surf->getCanvas(), pict, &matrix, NULL); |
| 650 mpd.draw(); |
| 651 } else { |
| 652 surf->getCanvas()->drawPicture(pict, &matrix, NULL); |
| 653 } |
| 654 } |
| 655 } |
| 656 |
| 657 static void generate_image_from_picture(GM* gm, const ConfigData& config, |
| 658 GrSurface* gpuTarget, |
| 659 SkPicture* pict, SkBitmap* bitmap, |
| 660 SkScalar scale = SK_Scalar1, |
| 661 bool tile = false) { |
| 662 const SkISize size = gm->getISize(); |
| 663 |
| 664 SkAutoTUnref<SkSurface> surf(CreateSurface(config, size, gpuTarget)); |
| 665 |
| 666 DrawPictureToSurface(surf, pict, scale, tile, false); |
| 667 |
| 668 setup_bitmap(config, size, bitmap); |
| 669 |
| 670 surf->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes()
, 0, 0); |
| 671 |
| 672 complete_bitmap(bitmap); |
| 673 } |
| 674 |
| 675 static bool generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) { |
| 676 #ifdef SK_SUPPORT_PDF |
| 677 SkMatrix initialTransform = gm->getInitialTransform(); |
| 678 if (FLAGS_useDocumentInsteadOfDevice) { |
| 679 SkISize pageISize = gm->getISize(); |
| 680 SkAutoTUnref<SkDocument> pdfDoc( |
| 681 SkDocument::CreatePDF(&pdf, NULL, |
| 682 encode_to_dct_data, |
| 683 SkIntToScalar(FLAGS_pdfRasterDpi))); |
| 684 |
| 685 if (!pdfDoc.get()) { |
| 686 return false; |
| 687 } |
| 688 |
| 689 SkCanvas* canvas = NULL; |
| 690 canvas = pdfDoc->beginPage(SkIntToScalar(pageISize.width()), |
| 691 SkIntToScalar(pageISize.height())); |
| 692 canvas->concat(initialTransform); |
| 693 |
| 694 invokeGM(gm, canvas, true, false); |
| 695 |
| 696 return pdfDoc->close(); |
| 697 } else { |
| 698 SkISize pageSize = gm->getISize(); |
| 699 SkPDFDevice* dev = NULL; |
| 700 if (initialTransform.isIdentity()) { |
| 701 dev = new SkPDFDevice(pageSize, pageSize, initialTransform); |
| 702 } else { |
| 703 SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()), |
| 704 SkIntToScalar(pageSize.height())
); |
| 705 initialTransform.mapRect(&content); |
| 706 if (!content.intersect(0, 0, SkIntToScalar(pageSize.width()), |
| 707 SkIntToScalar(pageSize.height()))) { |
| 708 content.setEmpty(); |
| 709 } |
| 710 SkISize contentSize = |
| 711 SkISize::Make(SkScalarRoundToInt(content.width()), |
| 712 SkScalarRoundToInt(content.height())); |
| 713 dev = new SkPDFDevice(pageSize, contentSize, initialTransform); |
| 714 } |
| 715 dev->setDCTEncoder(encode_to_dct_data); |
| 716 dev->setRasterDpi(SkIntToScalar(FLAGS_pdfRasterDpi)); |
| 717 SkAutoUnref aur(dev); |
| 718 SkCanvas c(dev); |
| 719 invokeGM(gm, &c, true, false); |
| 720 SkPDFDocument doc; |
| 721 doc.appendPage(dev); |
| 722 doc.emitPDF(&pdf); |
| 723 } |
| 724 #endif // SK_SUPPORT_PDF |
| 725 return true; // Do not report failure if pdf is not supported. |
| 726 } |
| 727 |
| 728 static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) { |
| 729 #ifdef SK_SUPPORT_XPS |
| 730 SkISize size = gm->getISize(); |
| 731 |
| 732 SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()), |
| 733 SkIntToScalar(size.height())); |
| 734 static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254); |
| 735 static const SkScalar upm = 72 * inchesPerMeter; |
| 736 SkVector unitsPerMeter = SkPoint::Make(upm, upm); |
| 737 static const SkScalar ppm = 200 * inchesPerMeter; |
| 738 SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm); |
| 739 |
| 740 SkXPSDevice* dev = new SkXPSDevice(); |
| 741 SkAutoUnref aur(dev); |
| 742 |
| 743 SkCanvas c(dev); |
| 744 dev->beginPortfolio(&xps); |
| 745 dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize); |
| 746 invokeGM(gm, &c, false, false); |
| 747 dev->endSheet(); |
| 748 dev->endPortfolio(); |
| 749 |
| 750 #endif |
| 751 } |
| 752 |
| 753 /** |
| 754 * Log more detail about the mistmatch between expectedBitmap and |
| 755 * actualBitmap. |
| 756 */ |
| 757 void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& act
ualBitmap, |
| 758 const char *testName) { |
| 759 const int expectedWidth = expectedBitmap.width(); |
| 760 const int expectedHeight = expectedBitmap.height(); |
| 761 const int width = actualBitmap.width(); |
| 762 const int height = actualBitmap.height(); |
| 763 if ((expectedWidth != width) || (expectedHeight != height)) { |
| 764 SkDebugf("---- %s: dimension mismatch -- expected [%d %d], actual [%
d %d]\n", |
| 765 testName, expectedWidth, expectedHeight, width, height); |
| 766 return; |
| 767 } |
| 768 |
| 769 if ((kN32_SkColorType != expectedBitmap.colorType()) || |
| 770 (kN32_SkColorType != actualBitmap.colorType())) { |
| 771 SkDebugf("---- %s: not computing max per-channel pixel mismatch beca
use non-8888\n", |
| 772 testName); |
| 773 return; |
| 774 } |
| 775 |
| 776 SkAutoLockPixels alp0(expectedBitmap); |
| 777 SkAutoLockPixels alp1(actualBitmap); |
| 778 int errR = 0; |
| 779 int errG = 0; |
| 780 int errB = 0; |
| 781 int errA = 0; |
| 782 int differingPixels = 0; |
| 783 |
| 784 for (int y = 0; y < height; ++y) { |
| 785 const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y); |
| 786 const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y); |
| 787 for (int x = 0; x < width; ++x) { |
| 788 SkPMColor expectedPixel = *expectedPixelPtr++; |
| 789 SkPMColor actualPixel = *actualPixelPtr++; |
| 790 if (expectedPixel != actualPixel) { |
| 791 differingPixels++; |
| 792 errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPix
el) - |
| 793 (int)SkGetPackedR32(actualPixel
))); |
| 794 errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPix
el) - |
| 795 (int)SkGetPackedG32(actualPixel
))); |
| 796 errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPix
el) - |
| 797 (int)SkGetPackedB32(actualPixel
))); |
| 798 errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPix
el) - |
| 799 (int)SkGetPackedA32(actualPixel
))); |
| 800 } |
| 801 } |
| 802 } |
| 803 SkDebugf("---- %s: %d (of %d) differing pixels, " |
| 804 "max per-channel mismatch R=%d G=%d B=%d A=%d\n", |
| 805 testName, differingPixels, width*height, errR, errG, errB, errA
); |
| 806 } |
| 807 |
| 808 /** |
| 809 * Compares actual hash digest to expectations, returning the set of errors |
| 810 * (if any) that we saw along the way. |
| 811 * |
| 812 * If fMismatchPath has been set, and there are pixel diffs, then the |
| 813 * actual bitmap will be written out to a file within fMismatchPath. |
| 814 * And similarly for fMissingExpectationsPath... |
| 815 * |
| 816 * @param expectations what expectations to compare actualBitmap against |
| 817 * @param actualBitmapAndDigest the SkBitmap we actually generated, and its
GmResultDigest |
| 818 * @param shortName name of test, e.g. "selftest1" |
| 819 * @param configName name of config, e.g. "8888" |
| 820 * @param renderModeDescriptor e.g., "-rtree", "-deferred" |
| 821 * @param addToJsonSummary whether to add these results (both actual and |
| 822 * expected) to the JSON summary. Regardless of this setting, if |
| 823 * we find an image mismatch in this test, we will write these |
| 824 * results to the JSON summary. (This is so that we will always |
| 825 * report errors across rendering modes, such as pipe vs tiled. |
| 826 * See https://codereview.chromium.org/13650002/ ) |
| 827 */ |
| 828 ErrorCombination compare_to_expectations(Expectations expectations, |
| 829 const BitmapAndDigest& actualBitmap
AndDigest, |
| 830 const char *shortName, const char *
configName, |
| 831 const char *renderModeDescriptor, |
| 832 bool addToJsonSummary) { |
| 833 ErrorCombination errors; |
| 834 SkString shortNamePlusConfig = make_shortname_plus_config(shortName, con
figName); |
| 835 SkString completeNameString(shortNamePlusConfig); |
| 836 completeNameString.append(renderModeDescriptor); |
| 837 completeNameString.append("."); |
| 838 completeNameString.append(kPNG_FileExtension); |
| 839 const char* completeName = completeNameString.c_str(); |
| 840 |
| 841 if (expectations.empty()) { |
| 842 errors.add(kMissingExpectations_ErrorType); |
| 843 |
| 844 // Write out the "actuals" for any tests without expectations, if we
have |
| 845 // been directed to do so. |
| 846 if (fMissingExpectationsPath) { |
| 847 SkString path = this->makeBitmapFilename(fMissingExpectationsPat
h, shortName, |
| 848 configName, renderModeD
escriptor, |
| 849 actualBitmapAndDigest.f
Digest); |
| 850 WriteBitmap(path, actualBitmapAndDigest.fBitmap); |
| 851 } |
| 852 |
| 853 } else if (!expectations.match(actualBitmapAndDigest.fDigest)) { |
| 854 addToJsonSummary = true; |
| 855 // The error mode we record depends on whether this was running |
| 856 // in a non-standard renderMode. |
| 857 if ('\0' == *renderModeDescriptor) { |
| 858 errors.add(kExpectationsMismatch_ErrorType); |
| 859 } else { |
| 860 errors.add(kRenderModeMismatch_ErrorType); |
| 861 } |
| 862 |
| 863 // Write out the "actuals" for any mismatches, if we have |
| 864 // been directed to do so. |
| 865 if (fMismatchPath) { |
| 866 SkString path = this->makeBitmapFilename(fMismatchPath, shortNam
e, configName, |
| 867 renderModeDescriptor, |
| 868 actualBitmapAndDigest.f
Digest); |
| 869 WriteBitmap(path, actualBitmapAndDigest.fBitmap); |
| 870 } |
| 871 |
| 872 // If we have access to a single expected bitmap, log more |
| 873 // detail about the mismatch. |
| 874 const SkBitmap *expectedBitmapPtr = expectations.asBitmap(); |
| 875 if (expectedBitmapPtr) { |
| 876 report_bitmap_diffs(*expectedBitmapPtr, actualBitmapAndDigest.fB
itmap, |
| 877 completeName); |
| 878 } |
| 879 } |
| 880 |
| 881 if (addToJsonSummary) { |
| 882 add_actual_results_to_json_summary(completeName, actualBitmapAndDige
st.fDigest, errors, |
| 883 expectations.ignoreFailure()); |
| 884 add_expected_results_to_json_summary(completeName, expectations); |
| 885 } |
| 886 |
| 887 return errors; |
| 888 } |
| 889 |
| 890 /** |
| 891 * Add this result to the appropriate JSON collection of actual results (but
just ONE), |
| 892 * depending on errors encountered. |
| 893 */ |
| 894 void add_actual_results_to_json_summary(const char testName[], |
| 895 const GmResultDigest &actualResultDi
gest, |
| 896 ErrorCombination errors, |
| 897 bool ignoreFailure) { |
| 898 Json::Value jsonActualResults = actualResultDigest.asJsonTypeValuePair()
; |
| 899 Json::Value *resultCollection = NULL; |
| 900 |
| 901 if (errors.isEmpty()) { |
| 902 resultCollection = &this->fJsonActualResults_Succeeded; |
| 903 } else if (errors.includes(kRenderModeMismatch_ErrorType)) { |
| 904 resultCollection = &this->fJsonActualResults_Failed; |
| 905 } else if (errors.includes(kExpectationsMismatch_ErrorType)) { |
| 906 if (ignoreFailure) { |
| 907 resultCollection = &this->fJsonActualResults_FailureIgnored; |
| 908 } else { |
| 909 resultCollection = &this->fJsonActualResults_Failed; |
| 910 } |
| 911 } else if (errors.includes(kMissingExpectations_ErrorType)) { |
| 912 // TODO: What about the case where there IS an expected |
| 913 // image hash digest, but that gm test doesn't actually |
| 914 // run? For now, those cases will always be ignored, |
| 915 // because gm only looks at expectations that correspond |
| 916 // to gm tests that were actually run. |
| 917 // |
| 918 // Once we have the ability to express expectations as a |
| 919 // JSON file, we should fix this (and add a test case for |
| 920 // which an expectation is given but the test is never |
| 921 // run). |
| 922 resultCollection = &this->fJsonActualResults_NoComparison; |
| 923 } |
| 924 |
| 925 // If none of the above cases match, we don't add it to ANY tally of act
ual results. |
| 926 if (resultCollection) { |
| 927 (*resultCollection)[testName] = jsonActualResults; |
| 928 } |
| 929 } |
| 930 |
| 931 /** |
| 932 * Add this test to the JSON collection of expected results. |
| 933 */ |
| 934 void add_expected_results_to_json_summary(const char testName[], |
| 935 Expectations expectations) { |
| 936 this->fJsonExpectedResults[testName] = expectations.asJsonValue(); |
| 937 } |
| 938 |
| 939 /** |
| 940 * Compare actualBitmap to expectations stored in this->fExpectationsSource. |
| 941 * |
| 942 * @param gm which test generated the actualBitmap |
| 943 * @param gRec |
| 944 * @param configName The config name to look for in the expectation file. |
| 945 * @param actualBitmapAndDigest ptr to bitmap generated by this run, or NULL |
| 946 * if we don't have a usable bitmap representation |
| 947 */ |
| 948 ErrorCombination compareTestResultsToStoredExpectations( |
| 949 GM* gm, const ConfigData& gRec, const char* configName, |
| 950 const BitmapAndDigest* actualBitmapAndDigest) { |
| 951 ErrorCombination errors; |
| 952 |
| 953 if (NULL == actualBitmapAndDigest) { |
| 954 // Note that we intentionally skipped validating the results for |
| 955 // this test, because we don't know how to generate an SkBitmap |
| 956 // version of the output. |
| 957 errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); |
| 958 } else if (!(gRec.fFlags & kWrite_ConfigFlag)) { |
| 959 // We don't record the results for this test or compare them |
| 960 // against any expectations, because the output image isn't |
| 961 // meaningful. |
| 962 // See https://code.google.com/p/skia/issues/detail?id=1410 ('some |
| 963 // GM result images not available for download from Google Storage') |
| 964 errors.add(ErrorCombination(kIntentionallySkipped_ErrorType)); |
| 965 } else { |
| 966 ExpectationsSource *expectationsSource = this->fExpectationsSource.g
et(); |
| 967 SkString nameWithExtension = make_shortname_plus_config(gm->getName(
), configName); |
| 968 nameWithExtension.append("."); |
| 969 nameWithExtension.append(kPNG_FileExtension); |
| 970 |
| 971 if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { |
| 972 /* |
| 973 * Get the expected results for this test, as one or more allowe
d |
| 974 * hash digests. The current implementation of expectationsSourc
e |
| 975 * get this by computing the hash digest of a single PNG file on
disk. |
| 976 * |
| 977 * TODO(epoger): This relies on the fact that |
| 978 * force_all_opaque() was called on the bitmap before it |
| 979 * was written to disk as a PNG in the first place. If |
| 980 * not, the hash digest returned here may not match the |
| 981 * hash digest of actualBitmap, which *has* been run through |
| 982 * force_all_opaque(). |
| 983 * See comments above complete_bitmap() for more detail. |
| 984 */ |
| 985 Expectations expectations = expectationsSource->get(nameWithExte
nsion.c_str()); |
| 986 if (this->shouldIgnoreTest(gm->getName())) { |
| 987 expectations.setIgnoreFailure(true); |
| 988 } |
| 989 errors.add(compare_to_expectations(expectations, *actualBitmapAn
dDigest, |
| 990 gm->getName(), configName, ""
, true)); |
| 991 } else { |
| 992 // If we are running without expectations, we still want to |
| 993 // record the actual results. |
| 994 add_actual_results_to_json_summary(nameWithExtension.c_str(), |
| 995 actualBitmapAndDigest->fDiges
t, |
| 996 ErrorCombination(kMissingExpe
ctations_ErrorType), |
| 997 false); |
| 998 errors.add(ErrorCombination(kMissingExpectations_ErrorType)); |
| 999 } |
| 1000 } |
| 1001 return errors; |
| 1002 } |
| 1003 |
| 1004 /** |
| 1005 * Compare actualBitmap to referenceBitmap. |
| 1006 * |
| 1007 * @param shortName test name, e.g. "selftest1" |
| 1008 * @param configName configuration name, e.g. "8888" |
| 1009 * @param renderModeDescriptor |
| 1010 * @param actualBitmap actual bitmap generated by this run |
| 1011 * @param referenceBitmap bitmap we expected to be generated |
| 1012 */ |
| 1013 ErrorCombination compare_test_results_to_reference_bitmap( |
| 1014 const char *shortName, const char *configName, const char *renderModeDes
criptor, |
| 1015 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { |
| 1016 |
| 1017 SkASSERT(referenceBitmap); |
| 1018 Expectations expectations(*referenceBitmap); |
| 1019 BitmapAndDigest actualBitmapAndDigest(actualBitmap); |
| 1020 |
| 1021 // TODO: Eliminate RecordTestResults from here. |
| 1022 // Results recording code for the test_drawing path has been refactored
so that |
| 1023 // RecordTestResults is only called once, at the topmost level. However,
the |
| 1024 // other paths have not yet been refactored, and RecordTestResults has b
een added |
| 1025 // here to maintain proper behavior for calls not coming from the test_d
rawing path. |
| 1026 ErrorCombination errors; |
| 1027 errors.add(compare_to_expectations(expectations, actualBitmapAndDigest,
shortName, |
| 1028 configName, renderModeDescriptor, fal
se)); |
| 1029 SkString shortNamePlusConfig = make_shortname_plus_config(shortName, con
figName); |
| 1030 RecordTestResults(errors, shortNamePlusConfig, renderModeDescriptor); |
| 1031 |
| 1032 return errors; |
| 1033 } |
| 1034 |
| 1035 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t rec
ordFlags, |
| 1036 SkScalar scale = SK_Scalar1) { |
| 1037 SkScalar width = SkScalarMul(SkIntToScalar(gm->getISize().width()), scal
e); |
| 1038 SkScalar height = SkScalarMul(SkIntToScalar(gm->getISize().height()), sc
ale); |
| 1039 |
| 1040 SkAutoTDelete<SkBBHFactory> factory; |
| 1041 if (kRTree_BbhType == bbhType) { |
| 1042 factory.reset(SkNEW(SkRTreeFactory)); |
| 1043 } |
| 1044 SkPictureRecorder recorder; |
| 1045 SkCanvas* cv = recorder.beginRecording(width, height, factory.get(), rec
ordFlags); |
| 1046 cv->scale(scale, scale); |
| 1047 invokeGM(gm, cv, false, false); |
| 1048 return recorder.endRecording(); |
| 1049 } |
| 1050 |
| 1051 static SkPicture* stream_to_new_picture(const SkPicture& src) { |
| 1052 SkDynamicMemoryWStream storage; |
| 1053 src.serialize(&storage); |
| 1054 SkAutoTUnref<SkStreamAsset> pictReadback(storage.detachAsStream()); |
| 1055 SkPicture* retval = SkPicture::CreateFromStream(pictReadback, |
| 1056 &SkImageDecoder::DecodeM
emory); |
| 1057 return retval; |
| 1058 } |
| 1059 |
| 1060 // Test: draw into a bitmap or pdf. |
| 1061 // Depending on flags, possibly compare to an expected image. |
| 1062 // If writePath is not NULL, also write images (or documents) to the specifi
ed path. |
| 1063 ErrorCombination test_drawing(GM* gm, const ConfigData& gRec, |
| 1064 const SkTDArray<const PDFRasterizerData*> &pdf
Rasterizers, |
| 1065 const char writePath [], |
| 1066 GrSurface* gpuTarget, |
| 1067 SkBitmap* bitmap) { |
| 1068 ErrorCombination errors; |
| 1069 SkDynamicMemoryWStream document; |
| 1070 |
| 1071 if (gRec.fBackend == kRaster_Backend || |
| 1072 gRec.fBackend == kGPU_Backend) { |
| 1073 // Early exit if we can't generate the image. |
| 1074 errors.add(generate_image(gm, gRec, gpuTarget, bitmap, false)); |
| 1075 if (!errors.isEmpty()) { |
| 1076 // TODO: Add a test to exercise what the stdout and |
| 1077 // JSON look like if we get an "early error" while |
| 1078 // trying to generate the image. |
| 1079 return errors; |
| 1080 } |
| 1081 |
| 1082 errors.add(this->writeBitmap(gm, gRec, gRec.fName, writePath, *bitma
p)); |
| 1083 } else if (gRec.fBackend == kPDF_Backend) { |
| 1084 if (!generate_pdf(gm, document)) { |
| 1085 errors.add(kGeneratePdfFailed_ErrorType); |
| 1086 } else { |
| 1087 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStre
am()); |
| 1088 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { |
| 1089 SkString path = make_filename(writePath, gm->getName(), gRec
.fName, "", "pdf"); |
| 1090 errors.add(write_document(path, documentStream)); |
| 1091 } |
| 1092 |
| 1093 if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) { |
| 1094 for (int i = 0; i < pdfRasterizers.count(); i++) { |
| 1095 SkBitmap pdfBitmap; |
| 1096 documentStream->rewind(); |
| 1097 bool success = (*pdfRasterizers[i]->fRasterizerFunction)
( |
| 1098 documentStream.get(), &pdfBitmap); |
| 1099 if (!success) { |
| 1100 SkDebugf("FAILED to render PDF for %s using renderer
%s\n", |
| 1101 gm->getName(), |
| 1102 pdfRasterizers[i]->fName); |
| 1103 continue; |
| 1104 } |
| 1105 |
| 1106 SkString configName(gRec.fName); |
| 1107 configName.append("-"); |
| 1108 configName.append(pdfRasterizers[i]->fName); |
| 1109 |
| 1110 errors.add(this->writeBitmap(gm, gRec, configName.c_str(
), |
| 1111 writePath, pdfBitmap)); |
| 1112 } |
| 1113 } else { |
| 1114 errors.add(kIntentionallySkipped_ErrorType); |
| 1115 } |
| 1116 } |
| 1117 } else if (gRec.fBackend == kXPS_Backend) { |
| 1118 generate_xps(gm, document); |
| 1119 SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream()
); |
| 1120 |
| 1121 errors.add(this->compareTestResultsToStoredExpectations( |
| 1122 gm, gRec, gRec.fName, NULL)); |
| 1123 |
| 1124 if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { |
| 1125 SkString path = make_filename(writePath, gm->getName(), gRec.fNa
me, "", "xps"); |
| 1126 errors.add(write_document(path, documentStream)); |
| 1127 } |
| 1128 } else { |
| 1129 SkASSERT(false); |
| 1130 } |
| 1131 return errors; |
| 1132 } |
| 1133 |
| 1134 ErrorCombination test_deferred_drawing(GM* gm, |
| 1135 const ConfigData& gRec, |
| 1136 const SkBitmap& referenceBitmap, |
| 1137 GrSurface* gpuTarget) { |
| 1138 if (gRec.fBackend == kRaster_Backend || |
| 1139 gRec.fBackend == kGPU_Backend) { |
| 1140 const char renderModeDescriptor[] = "-deferred"; |
| 1141 SkBitmap bitmap; |
| 1142 // Early exit if we can't generate the image, but this is |
| 1143 // expected in some cases, so don't report a test failure. |
| 1144 ErrorCombination errors = generate_image(gm, gRec, gpuTarget, &bitma
p, true); |
| 1145 // TODO(epoger): This logic is the opposite of what is |
| 1146 // described above... if we succeeded in generating the |
| 1147 // -deferred image, we exit early! We should fix this |
| 1148 // ASAP, because it is hiding -deferred errors... but for |
| 1149 // now, I'm leaving the logic as it is so that the |
| 1150 // refactoring change |
| 1151 // https://codereview.chromium.org/12992003/ is unblocked. |
| 1152 // |
| 1153 // Filed as https://code.google.com/p/skia/issues/detail?id=1180 |
| 1154 // ('image-surface gm test is failing in "deferred" mode, |
| 1155 // and gm is not reporting the failure') |
| 1156 if (errors.isEmpty()) { |
| 1157 // TODO(epoger): Report this as a new ErrorType, |
| 1158 // something like kImageGeneration_ErrorType? |
| 1159 return kEmpty_ErrorCombination; |
| 1160 } |
| 1161 return compare_test_results_to_reference_bitmap( |
| 1162 gm->getName(), gRec.fName, renderModeDescriptor, bitmap, &refere
nceBitmap); |
| 1163 } |
| 1164 return kEmpty_ErrorCombination; |
| 1165 } |
| 1166 |
| 1167 static SkSurface* CreateSurface(const ConfigData& config, |
| 1168 const SkISize& size, |
| 1169 GrSurface* gpuTarget) { |
| 1170 if (config.fBackend == kRaster_Backend) { |
| 1171 SkImageInfo ii = SkImageInfo::Make(size.width(), size.height(), |
| 1172 config.fColorType, kPremul_SkAlph
aType); |
| 1173 |
| 1174 return SkSurface::NewRaster(ii); |
| 1175 } |
| 1176 #if SK_SUPPORT_GPU |
| 1177 else { |
| 1178 uint32_t flags = (config.fFlags & kDFText_ConfigFlag) ? |
| 1179 SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; |
| 1180 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType
); |
| 1181 return SkSurface::NewRenderTargetDirect(gpuTarget->asRenderTarget(),
&props); |
| 1182 } |
| 1183 #endif |
| 1184 |
| 1185 return NULL; |
| 1186 } |
| 1187 |
| 1188 ErrorCombination writeBitmap(GM* gm, |
| 1189 const ConfigData& config, |
| 1190 const char* configName, |
| 1191 const char* writePath, |
| 1192 const SkBitmap& bitmap) { |
| 1193 ErrorCombination errors; |
| 1194 |
| 1195 BitmapAndDigest bitmapAndDigest(bitmap); |
| 1196 errors.add(this->compareTestResultsToStoredExpectations(gm, config, |
| 1197 configName, &bit
mapAndDigest)); |
| 1198 |
| 1199 if (writePath && (config.fFlags & kWrite_ConfigFlag)) { |
| 1200 SkString path; |
| 1201 |
| 1202 path = this->makeBitmapFilename(writePath, gm->getName(), configName
, |
| 1203 "", bitmapAndDigest.fDigest); |
| 1204 errors.add(WriteBitmap(path, bitmapAndDigest.fBitmap)); |
| 1205 } |
| 1206 |
| 1207 return errors; |
| 1208 } |
| 1209 |
| 1210 ErrorCombination testMPDDrawing(GM* gm, |
| 1211 const ConfigData& config, |
| 1212 const char* writePath, |
| 1213 GrSurface* gpuTarget, |
| 1214 const SkBitmap& referenceBitmap) { |
| 1215 SkASSERT(kRaster_Backend == config.fBackend || kGPU_Backend == config.fB
ackend); |
| 1216 |
| 1217 static const uint32_t kMPDFlags = SkPictureRecorder::kComputeSaveLayerIn
fo_RecordFlag; |
| 1218 |
| 1219 SkAutoTUnref<SkPicture> pict(generate_new_picture(gm, kRTree_BbhType, kM
PDFlags)); |
| 1220 |
| 1221 SkAutoTUnref<SkSurface> surf(CreateSurface(config, gm->getISize(), gpuTa
rget)); |
| 1222 |
| 1223 DrawPictureToSurface(surf, pict, SK_Scalar1, false, true); |
| 1224 |
| 1225 SkBitmap bitmap; |
| 1226 |
| 1227 setup_bitmap(config, gm->getISize(), &bitmap); |
| 1228 |
| 1229 surf->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0
, 0); |
| 1230 complete_bitmap(&bitmap); |
| 1231 |
| 1232 SkString configName(config.fName); |
| 1233 configName.append("-mpd"); |
| 1234 |
| 1235 return this->writeBitmap(gm, config, configName.c_str(), writePath, bitm
ap); |
| 1236 } |
| 1237 |
| 1238 ErrorCombination test_pipe_playback(GM* gm, const ConfigData& gRec, |
| 1239 const SkBitmap& referenceBitmap, bool si
mulateFailure) { |
| 1240 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->getN
ame(), |
| 1241 gRec.fNa
me); |
| 1242 ErrorCombination errors; |
| 1243 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { |
| 1244 SkString renderModeDescriptor("-pipe"); |
| 1245 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); |
| 1246 |
| 1247 if (gm->getFlags() & GM::kSkipPipe_Flag |
| 1248 || (gPipeWritingFlagCombos[i].flags == SkGPipeWriter::kCrossProc
ess_Flag |
| 1249 && gm->getFlags() & GM::kSkipPipeCrossProcess_Flag)) { |
| 1250 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlus
Config, |
| 1251 renderModeDescriptor.c_str()); |
| 1252 errors.add(kIntentionallySkipped_ErrorType); |
| 1253 } else { |
| 1254 SkBitmap bitmap; |
| 1255 SkISize size = gm->getISize(); |
| 1256 setup_bitmap(gRec, size, &bitmap); |
| 1257 SkCanvas canvas(bitmap); |
| 1258 InstallFilter(&canvas); |
| 1259 // Pass a decoding function so the factory GM (which has an SkBi
tmap |
| 1260 // with encoded data) will not fail playback. |
| 1261 PipeController pipeController(&canvas, &SkImageDecoder::DecodeMe
mory); |
| 1262 SkGPipeWriter writer; |
| 1263 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, |
| 1264 gPipeWritingFlagCom
bos[i].flags, |
| 1265 size.width(), size.
height()); |
| 1266 if (!simulateFailure) { |
| 1267 invokeGM(gm, pipeCanvas, false, false); |
| 1268 } |
| 1269 complete_bitmap(&bitmap); |
| 1270 writer.endRecording(); |
| 1271 errors.add(compare_test_results_to_reference_bitmap( |
| 1272 gm->getName(), gRec.fName, renderModeDescriptor.c_str(), bit
map, |
| 1273 &referenceBitmap)); |
| 1274 if (!errors.isEmpty()) { |
| 1275 break; |
| 1276 } |
| 1277 } |
| 1278 } |
| 1279 return errors; |
| 1280 } |
| 1281 |
| 1282 ErrorCombination test_tiled_pipe_playback(GM* gm, const ConfigData& gRec, |
| 1283 const SkBitmap& referenceBitmap) { |
| 1284 const SkString shortNamePlusConfig = make_shortname_plus_config(gm->getN
ame(), |
| 1285 gRec.fNa
me); |
| 1286 ErrorCombination errors; |
| 1287 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { |
| 1288 SkString renderModeDescriptor("-tiled pipe"); |
| 1289 renderModeDescriptor.append(gPipeWritingFlagCombos[i].name); |
| 1290 |
| 1291 if ((gm->getFlags() & GM::kSkipPipe_Flag) || |
| 1292 (gm->getFlags() & GM::kSkipTiled_Flag)) { |
| 1293 RecordTestResults(kIntentionallySkipped_ErrorType, shortNamePlus
Config, |
| 1294 renderModeDescriptor.c_str()); |
| 1295 errors.add(kIntentionallySkipped_ErrorType); |
| 1296 } else { |
| 1297 SkBitmap bitmap; |
| 1298 SkISize size = gm->getISize(); |
| 1299 setup_bitmap(gRec, size, &bitmap); |
| 1300 SkCanvas canvas(bitmap); |
| 1301 InstallFilter(&canvas); |
| 1302 TiledPipeController pipeController(bitmap, &SkImageDecoder::Deco
deMemory); |
| 1303 SkGPipeWriter writer; |
| 1304 SkCanvas* pipeCanvas = writer.startRecording(&pipeController, |
| 1305 gPipeWritingFlagCom
bos[i].flags, |
| 1306 size.width(), size.
height()); |
| 1307 invokeGM(gm, pipeCanvas, false, false); |
| 1308 complete_bitmap(&bitmap); |
| 1309 writer.endRecording(); |
| 1310 errors.add(compare_test_results_to_reference_bitmap(gm->getName(
), gRec.fName, |
| 1311 renderModeDe
scriptor.c_str(), |
| 1312 bitmap, &ref
erenceBitmap)); |
| 1313 if (!errors.isEmpty()) { |
| 1314 break; |
| 1315 } |
| 1316 } |
| 1317 } |
| 1318 return errors; |
| 1319 } |
| 1320 |
| 1321 // |
| 1322 // member variables. |
| 1323 // They are public for now, to allow easier setting by tool_main(). |
| 1324 // |
| 1325 |
| 1326 bool fUseFileHierarchy, fWriteChecksumBasedFilenames; |
| 1327 ErrorCombination fIgnorableErrorTypes; |
| 1328 SkTArray<SkString> fIgnorableTestNames; |
| 1329 |
| 1330 const char* fMismatchPath; |
| 1331 const char* fMissingExpectationsPath; |
| 1332 |
| 1333 // collection of tests that have failed with each ErrorType |
| 1334 SkTArray<SkString> fFailedTests[kLast_ErrorType+1]; |
| 1335 SkTArray<SkString> fTestsSkippedOnAllRenderModes; |
| 1336 int fTestsRun; |
| 1337 SkTDict<int> fRenderModesEncountered; |
| 1338 |
| 1339 // Where to read expectations (expected image hash digests, etc.) from. |
| 1340 // If unset, we don't do comparisons. |
| 1341 SkAutoTUnref<ExpectationsSource> fExpectationsSource; |
| 1342 |
| 1343 // JSON summaries that we generate as we go (just for output). |
| 1344 Json::Value fJsonExpectedResults; |
| 1345 Json::Value fJsonActualResults_Failed; |
| 1346 Json::Value fJsonActualResults_FailureIgnored; |
| 1347 Json::Value fJsonActualResults_NoComparison; |
| 1348 Json::Value fJsonActualResults_Succeeded; |
| 1349 }; // end of GMMain class definition |
| 1350 |
| 1351 #if SK_SUPPORT_GPU |
| 1352 static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_G
LContextType; |
| 1353 #else |
| 1354 static const GLContextType kDontCare_GLContextType = 0; |
| 1355 #endif |
| 1356 |
| 1357 static const ConfigData gRec[] = { |
| 1358 { kN32_SkColorType, kRaster_Backend, kDontCare_GLContextType,
0, kRW_ConfigFlag, "8888", true }, |
| 1359 { kRGB_565_SkColorType, kRaster_Backend, kDontCare_GLContextType,
0, kRW_ConfigFlag, "565", true }, |
| 1360 #if SK_SUPPORT_GPU |
| 1361 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNative_GLContextType
, 0, kRW_ConfigFlag, "gpu", true }, |
| 1362 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNative_GLContextType
, 16, kRW_ConfigFlag, "msaa16", false}, |
| 1363 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNative_GLContextType
, 4, kRW_ConfigFlag, "msaa4", false}, |
| 1364 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNVPR_GLContextType,
4, kRW_ConfigFlag, "nvprmsaa4", true }, |
| 1365 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNVPR_GLContextType,
16, kRW_ConfigFlag, "nvprmsaa16", false}, |
| 1366 /* Not quite ready to turn on distance field text baselines */ |
| 1367 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNative_GLContextType
, 0, kRWDFT_ConfigFlag, "gpudft", false }, |
| 1368 /* The gpudebug context does not generate meaningful images, so don't record |
| 1369 * the images it generates! We only run it to look for asserts. */ |
| 1370 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kDebug_GLContextType,
0, kNone_ConfigFlag, "gpudebug", kDebugOnly}, |
| 1371 /* The gpunull context does the least amount of work possible and doesn't |
| 1372 generate meaninful images, so don't record them!. It can be run to |
| 1373 isolate the CPU-side processing expense from the GPU-side. |
| 1374 */ |
| 1375 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kNull_GLContextType,
0, kNone_ConfigFlag, "gpunull", kDebugOnly}, |
| 1376 #if SK_ANGLE |
| 1377 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kANGLE_GLContextType,
0, kRW_ConfigFlag, "angle", true }, |
| 1378 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kANGLE_GLContextType,
16, kRW_ConfigFlag, "anglemsaa16", true }, |
| 1379 #endif // SK_ANGLE |
| 1380 #ifdef SK_MESA |
| 1381 { kN32_SkColorType, kGPU_Backend, GrContextFactory::kMESA_GLContextType,
0, kRW_ConfigFlag, "mesa", true }, |
| 1382 #endif // SK_MESA |
| 1383 #endif // SK_SUPPORT_GPU |
| 1384 #ifdef SK_SUPPORT_XPS |
| 1385 /* At present we have no way of comparing XPS files (either natively or by c
onverting to PNG). */ |
| 1386 { kN32_SkColorType, kXPS_Backend, kDontCare_GLContextType,
0, kWrite_ConfigFlag, "xps", true }, |
| 1387 #endif // SK_SUPPORT_XPS |
| 1388 #ifdef SK_SUPPORT_PDF |
| 1389 { kN32_SkColorType, kPDF_Backend, kDontCare_GLContextType,
0, kRW_ConfigFlag, "pdf", true }, |
| 1390 #endif // SK_SUPPORT_PDF |
| 1391 }; |
| 1392 |
| 1393 static bool SkNoRasterizePDF(SkStream*, SkBitmap*) { return false; } |
| 1394 |
| 1395 static const PDFRasterizerData kPDFRasterizers[] = { |
| 1396 #ifdef SK_BUILD_FOR_MAC |
| 1397 { &SkPDFDocumentToBitmap, "mac", true }, |
| 1398 #endif |
| 1399 #ifdef SK_BUILD_POPPLER |
| 1400 { &SkPopplerRasterizePDF, "poppler", true }, |
| 1401 #endif |
| 1402 #ifdef SK_BUILD_NATIVE_PDF_RENDERER |
| 1403 { &SkNativeRasterizePDF, "native", true }, |
| 1404 #endif // SK_BUILD_NATIVE_PDF_RENDERER |
| 1405 // The following exists so that this array is never zero length. |
| 1406 { &SkNoRasterizePDF, "none", false}, |
| 1407 }; |
| 1408 |
| 1409 static const char kDefaultsConfigStr[] = "defaults"; |
| 1410 static const char kExcludeConfigChar = '~'; |
| 1411 #if SK_SUPPORT_GPU |
| 1412 static const char kGpuAPINameGL[] = "gl"; |
| 1413 static const char kGpuAPINameGLES[] = "gles"; |
| 1414 #endif |
| 1415 |
| 1416 static SkString configUsage() { |
| 1417 SkString result; |
| 1418 result.appendf("Space delimited list of which configs to run. Possible optio
ns: ["); |
| 1419 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { |
| 1420 SkASSERT(gRec[i].fName != kDefaultsConfigStr); |
| 1421 if (i > 0) { |
| 1422 result.append("|"); |
| 1423 } |
| 1424 result.appendf("%s", gRec[i].fName); |
| 1425 } |
| 1426 result.append("]\n"); |
| 1427 result.appendf("The default value is: \""); |
| 1428 SkString firstDefault; |
| 1429 SkString allButFirstDefaults; |
| 1430 SkString nonDefault; |
| 1431 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { |
| 1432 if (gRec[i].fRunByDefault) { |
| 1433 if (i > 0) { |
| 1434 result.append(" "); |
| 1435 } |
| 1436 result.append(gRec[i].fName); |
| 1437 if (firstDefault.isEmpty()) { |
| 1438 firstDefault = gRec[i].fName; |
| 1439 } else { |
| 1440 if (!allButFirstDefaults.isEmpty()) { |
| 1441 allButFirstDefaults.append(" "); |
| 1442 } |
| 1443 allButFirstDefaults.append(gRec[i].fName); |
| 1444 } |
| 1445 } else { |
| 1446 nonDefault = gRec[i].fName; |
| 1447 } |
| 1448 } |
| 1449 result.append("\"\n"); |
| 1450 result.appendf("\"%s\" evaluates to the default set of configs.\n", kDefault
sConfigStr); |
| 1451 result.appendf("Prepending \"%c\" on a config name excludes it from the set
of configs to run.\n" |
| 1452 "Exclusions always override inclusions regardless of order.\n
", |
| 1453 kExcludeConfigChar); |
| 1454 result.appendf("E.g. \"--config %s %c%s %s\" will run these configs:\n\t%s %
s", |
| 1455 kDefaultsConfigStr, |
| 1456 kExcludeConfigChar, |
| 1457 firstDefault.c_str(), |
| 1458 nonDefault.c_str(), |
| 1459 allButFirstDefaults.c_str(), |
| 1460 nonDefault.c_str()); |
| 1461 return result; |
| 1462 } |
| 1463 |
| 1464 static SkString pdfRasterizerUsage() { |
| 1465 SkString result; |
| 1466 result.appendf("Space delimited list of which PDF rasterizers to run. Possib
le options: ["); |
| 1467 // For this (and further) loops through kPDFRasterizers, there is a typecast
to int to avoid |
| 1468 // the compiler giving an "comparison of unsigned expression < 0 is always f
alse" warning |
| 1469 // and turning it into a build-breaking error. |
| 1470 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { |
| 1471 if (i > 0) { |
| 1472 result.append(" "); |
| 1473 } |
| 1474 result.append(kPDFRasterizers[i].fName); |
| 1475 } |
| 1476 result.append("]\n"); |
| 1477 result.append("The default value is: \""); |
| 1478 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { |
| 1479 if (kPDFRasterizers[i].fRunByDefault) { |
| 1480 if (i > 0) { |
| 1481 result.append(" "); |
| 1482 } |
| 1483 result.append(kPDFRasterizers[i].fName); |
| 1484 } |
| 1485 } |
| 1486 result.append("\""); |
| 1487 return result; |
| 1488 } |
| 1489 |
| 1490 // Macro magic to convert a numeric preprocessor token into a string. |
| 1491 // Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor
-token-to-a-string |
| 1492 // This should probably be moved into one of our common headers... |
| 1493 #define TOSTRING_INTERNAL(x) #x |
| 1494 #define TOSTRING(x) TOSTRING_INTERNAL(x) |
| 1495 |
| 1496 // Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). |
| 1497 DEFINE_string(config, "", configUsage().c_str()); |
| 1498 DEFINE_bool(cpu, true, "Allows non-GPU configs to be run. Applied after --config
."); |
| 1499 DEFINE_string(pdfRasterizers, "default", pdfRasterizerUsage().c_str()); |
| 1500 DEFINE_bool(deferred, false, "Exercise the deferred rendering test pass."); |
| 1501 DEFINE_bool(mpd, false, "Exercise MultiPictureDraw."); |
| 1502 |
| 1503 DEFINE_bool(dryRun, false, "Don't actually run the tests, just print what would
have been done."); |
| 1504 DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip."); |
| 1505 DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing."); |
| 1506 #if SK_SUPPORT_GPU |
| 1507 DEFINE_string(gpuAPI, "", "Force use of specific gpu API. Using \"gl\" " |
| 1508 "forces OpenGL API. Using \"gles\" forces OpenGL ES API. " |
| 1509 "Defaults to empty string, which selects the API native to the " |
| 1510 "system."); |
| 1511 DEFINE_string(gpuCacheSize, "", "<bytes> <count>: Limit the gpu cache to byte si
ze or " |
| 1512 "object count. " TOSTRING(DEFAULT_CACHE_VALUE) " for either value
means " |
| 1513 "use the default. 0 for either disables the cache."); |
| 1514 DEFINE_bool(gpu, true, "Allows GPU configs to be run. Applied after --config."); |
| 1515 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling
back to " |
| 1516 "software path rendering."); |
| 1517 #endif |
| 1518 DEFINE_bool(hierarchy, false, "Whether to use multilevel directory structure " |
| 1519 "when reading/writing files."); |
| 1520 DEFINE_string(ignoreErrorTypes, kDefaultIgnorableErrorTypes.asString(" ").c_str(
), |
| 1521 "Space-separated list of ErrorTypes that should be ignored. If any
*other* error " |
| 1522 "types are encountered, the tool will exit with a nonzero return v
alue."); |
| 1523 DEFINE_string(ignoreFailuresFile, "", "Path to file containing a list of tests f
or which we " |
| 1524 "should ignore failures.\n" |
| 1525 "The file should list one test per line, except for comment lines
starting with #"); |
| 1526 DEFINE_bool2(leaks, l, false, "show leaked ref cnt'd objects."); |
| 1527 DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n" |
| 1528 "Multiple matches may be separated by spaces.\n" |
| 1529 "~ causes a matching test to always be skipped\n" |
| 1530 "^ requires the start of the test to match\n" |
| 1531 "$ requires the end of the test to match\n" |
| 1532 "^ and $ requires an exact match\n" |
| 1533 "If a test does not match any list entry,\n" |
| 1534 "it is skipped unless some list entry starts with ~"); |
| 1535 DEFINE_string(missingExpectationsPath, "", "Write images for tests without expec
tations " |
| 1536 "into this directory."); |
| 1537 DEFINE_string(mismatchPath, "", "Write images for tests that failed due to " |
| 1538 "pixel mismatches into this directory."); |
| 1539 DEFINE_string(modulo, "", "[--modulo <remainder> <divisor>]: only run tests for
which " |
| 1540 "testIndex %% divisor == remainder."); |
| 1541 DEFINE_bool(pipe, false, "Exercise the SkGPipe replay test pass."); |
| 1542 DEFINE_string2(readPath, r, "", "Read reference images from this dir, and report
" |
| 1543 "any differences between those and the newly generated ones."); |
| 1544 DEFINE_bool(replay, false, "Exercise the SkPicture replay test pass."); |
| 1545 |
| 1546 #ifdef SK_BUILD_FOR_ANDROID |
| 1547 DEFINE_bool(resetGpuContext, true, "Reset the GrContext prior to running each GM
."); |
| 1548 #else |
| 1549 DEFINE_bool(resetGpuContext, false, "Reset the GrContext prior to running each G
M."); |
| 1550 #endif |
| 1551 |
| 1552 DEFINE_bool(rtree, false, "Exercise the R-Tree variant of SkPicture test pass.")
; |
| 1553 DEFINE_bool(serialize, false, "Exercise the SkPicture serialization & deserializ
ation test pass."); |
| 1554 DEFINE_bool(simulatePipePlaybackFailure, false, "Simulate a rendering failure in
pipe mode only."); |
| 1555 DEFINE_bool(tiledPipe, false, "Exercise tiled SkGPipe replay."); |
| 1556 DEFINE_bool(tileGrid, false, "Exercise the tile grid variant of SkPicture."); |
| 1557 DEFINE_string(tileGridReplayScales, "", "Space separated list of floating-point
scale " |
| 1558 "factors to be used for tileGrid playback testing. Default value:
1.0"); |
| 1559 DEFINE_bool2(verbose, v, false, "Give more detail (e.g. list all GMs run, more i
nfo about " |
| 1560 "each test)."); |
| 1561 DEFINE_bool(writeChecksumBasedFilenames, false, "When writing out actual images,
use checksum-" |
| 1562 "based filenames, as rebaseline.py will use when downloading them fr
om Google Storage"); |
| 1563 DEFINE_string(writeJsonSummaryPath, "", "Write a JSON-formatted result summary t
o this file."); |
| 1564 DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); |
| 1565 DEFINE_string2(writePicturePath, p, "", "Write .skp files into this directory.")
; |
| 1566 DEFINE_int32(pdfJpegQuality, -1, "Encodes images in JPEG at quality level N, " |
| 1567 "which can be in range 0-100). N = -1 will disable JPEG compression
. " |
| 1568 "Default is N = 100, maximum quality."); |
| 1569 // TODO(edisonn): pass a matrix instead of forcePerspectiveMatrix |
| 1570 // Either the 9 numbers defining the matrix |
| 1571 // or probably more readable would be to replace it with a set of a few predicat
es |
| 1572 // Like --prerotate 100 200 10 --posttranslate 10, 10 |
| 1573 // Probably define spacial names like centerx, centery, top, bottom, left, right |
| 1574 // then we can write something reabable like --rotate centerx centery 90 |
| 1575 DEFINE_bool(forcePerspectiveMatrix, false, "Force a perspective matrix."); |
| 1576 DEFINE_bool(useDocumentInsteadOfDevice, false, "Use SkDocument::CreateFoo instea
d of SkFooDevice."); |
| 1577 DEFINE_int32(pdfRasterDpi, 72, "Scale at which at which the non suported " |
| 1578 "features in PDF are rasterized. Must be be in range 0-10000. " |
| 1579 "Default is 72. N = 0 will disable rasterizing features like " |
| 1580 "text shadows or perspective bitmaps."); |
| 1581 static SkData* encode_to_dct_data(size_t*, const SkBitmap& bitmap) { |
| 1582 // Filter output of warnings that JPEG is not available for the image. |
| 1583 if (bitmap.width() >= 65500 || bitmap.height() >= 65500) return NULL; |
| 1584 if (FLAGS_pdfJpegQuality == -1) return NULL; |
| 1585 |
| 1586 SkBitmap bm = bitmap; |
| 1587 #if defined(SK_BUILD_FOR_MAC) |
| 1588 // Workaround bug #1043 where bitmaps with referenced pixels cause |
| 1589 // CGImageDestinationFinalize to crash |
| 1590 SkBitmap copy; |
| 1591 bitmap.deepCopyTo(©); |
| 1592 bm = copy; |
| 1593 #endif |
| 1594 |
| 1595 SkPixelRef* pr = bm.pixelRef(); |
| 1596 if (pr != NULL) { |
| 1597 SkData* data = pr->refEncodedData(); |
| 1598 if (data != NULL) { |
| 1599 return data; |
| 1600 } |
| 1601 } |
| 1602 |
| 1603 return SkImageEncoder::EncodeData(bm, |
| 1604 SkImageEncoder::kJPEG_Type, |
| 1605 FLAGS_pdfJpegQuality); |
| 1606 } |
| 1607 |
| 1608 static int findConfig(const char config[]) { |
| 1609 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { |
| 1610 if (!strcmp(config, gRec[i].fName)) { |
| 1611 return (int) i; |
| 1612 } |
| 1613 } |
| 1614 return -1; |
| 1615 } |
| 1616 |
| 1617 static const PDFRasterizerData* findPDFRasterizer(const char rasterizer[]) { |
| 1618 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); i++) { |
| 1619 if (!strcmp(rasterizer, kPDFRasterizers[i].fName)) { |
| 1620 return &kPDFRasterizers[i]; |
| 1621 } |
| 1622 } |
| 1623 return NULL; |
| 1624 } |
| 1625 |
| 1626 template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { |
| 1627 int index = array->find(value); |
| 1628 if (index < 0) { |
| 1629 *array->append() = value; |
| 1630 } |
| 1631 } |
| 1632 |
| 1633 /** |
| 1634 * Run this test in a number of different drawing modes (pipe, |
| 1635 * deferred, tiled, etc.), confirming that the resulting bitmaps all |
| 1636 * *exactly* match comparisonBitmap. |
| 1637 * |
| 1638 * Returns all errors encountered while doing so. |
| 1639 */ |
| 1640 ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, |
| 1641 const ConfigData &compareConfig, GrSurface*
gpuTarget, |
| 1642 const SkBitmap &comparisonBitmap, |
| 1643 const SkTDArray<SkScalar> &tileGridReplaySca
les); |
| 1644 ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, |
| 1645 const ConfigData &compareConfig, GrSurface*
gpuTarget, |
| 1646 const SkBitmap &comparisonBitmap, |
| 1647 const SkTDArray<SkScalar> &tileGridReplaySca
les) { |
| 1648 ErrorCombination errorsForAllModes; |
| 1649 uint32_t gmFlags = gm->getFlags(); |
| 1650 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(gm->g
etName(), |
| 1651 compa
reConfig.fName); |
| 1652 |
| 1653 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0); |
| 1654 SkAutoTUnref<SkPicture> aur(pict); |
| 1655 if (FLAGS_replay) { |
| 1656 const char renderModeDescriptor[] = "-replay"; |
| 1657 if (gmFlags & GM::kSkipPicture_Flag) { |
| 1658 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNameP
lusConfig, |
| 1659 renderModeDescriptor); |
| 1660 errorsForAllModes.add(kIntentionallySkipped_ErrorType); |
| 1661 } else { |
| 1662 SkBitmap bitmap; |
| 1663 gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pic
t, &bitmap); |
| 1664 |
| 1665 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitma
p( |
| 1666 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap
, |
| 1667 &comparisonBitmap)); |
| 1668 } |
| 1669 } |
| 1670 |
| 1671 if (FLAGS_serialize) { |
| 1672 const char renderModeDescriptor[] = "-serialize"; |
| 1673 if (gmFlags & GM::kSkipPicture_Flag) { |
| 1674 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNameP
lusConfig, |
| 1675 renderModeDescriptor); |
| 1676 errorsForAllModes.add(kIntentionallySkipped_ErrorType); |
| 1677 } else { |
| 1678 SkPicture* repict = gmmain.stream_to_new_picture(*pict); |
| 1679 SkAutoTUnref<SkPicture> aurr(repict); |
| 1680 SkBitmap bitmap; |
| 1681 gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, rep
ict, &bitmap); |
| 1682 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitma
p( |
| 1683 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap
, |
| 1684 &comparisonBitmap)); |
| 1685 } |
| 1686 } |
| 1687 |
| 1688 if ((1 == FLAGS_writePicturePath.count()) && |
| 1689 !(gmFlags & GM::kSkipPicture_Flag)) { |
| 1690 const char* pictureSuffix = "skp"; |
| 1691 // TODO(epoger): Make sure this still works even though the |
| 1692 // filename now contains the config name (it used to contain |
| 1693 // just the shortName). I think this is actually an |
| 1694 // *improvement*, because now runs with different configs will |
| 1695 // write out their SkPictures to separate files rather than |
| 1696 // overwriting each other. But we should make sure it doesn't |
| 1697 // break anybody. |
| 1698 SkString path = gmmain.make_filename(FLAGS_writePicturePath[0], gm->getN
ame(), |
| 1699 compareConfig.fName, "", pictureSuf
fix); |
| 1700 SkFILEWStream stream(path.c_str()); |
| 1701 pict->serialize(&stream); |
| 1702 } |
| 1703 |
| 1704 if (FLAGS_rtree) { |
| 1705 const char renderModeDescriptor[] = "-rtree"; |
| 1706 if ((gmFlags & GM::kSkipPicture_Flag) || (gmFlags & GM::kSkipTiled_Flag)
) { |
| 1707 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNameP
lusConfig, |
| 1708 renderModeDescriptor); |
| 1709 errorsForAllModes.add(kIntentionallySkipped_ErrorType); |
| 1710 } else { |
| 1711 SkPicture* pict = gmmain.generate_new_picture(gm, kRTree_BbhType, 0)
; |
| 1712 SkAutoTUnref<SkPicture> aur(pict); |
| 1713 SkBitmap bitmap; |
| 1714 gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pic
t, &bitmap); |
| 1715 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitma
p( |
| 1716 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap
, |
| 1717 &comparisonBitmap)); |
| 1718 } |
| 1719 } |
| 1720 |
| 1721 // run the pipe centric GM steps |
| 1722 if (FLAGS_pipe) { |
| 1723 errorsForAllModes.add(gmmain.test_pipe_playback(gm, compareConfig, compa
risonBitmap, |
| 1724 FLAGS_simulatePipePlayba
ckFailure)); |
| 1725 if (FLAGS_tiledPipe) { |
| 1726 errorsForAllModes.add(gmmain.test_tiled_pipe_playback(gm, compareCon
fig, |
| 1727 comparisonBitm
ap)); |
| 1728 } |
| 1729 } |
| 1730 return errorsForAllModes; |
| 1731 } |
| 1732 |
| 1733 |
| 1734 /** |
| 1735 * Run this test in a number of different configs (8888, 565, PDF, |
| 1736 * etc.), confirming that the resulting bitmaps match expectations |
| 1737 * (which may be different for each config). |
| 1738 * |
| 1739 * Returns all errors encountered while doing so. |
| 1740 */ |
| 1741 ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, |
| 1742 const SkTDArray<size_t> &configs, |
| 1743 const SkTDArray<const PDFRasterizerData*>
&pdfRasterizers, |
| 1744 const SkTDArray<SkScalar> &tileGridReplayS
cales, |
| 1745 GrContextFactory *grFactory, |
| 1746 GrGLStandard gpuAPI); |
| 1747 ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, |
| 1748 const SkTDArray<size_t> &configs, |
| 1749 const SkTDArray<const PDFRasterizerData*>
&pdfRasterizers, |
| 1750 const SkTDArray<SkScalar> &tileGridReplayS
cales, |
| 1751 GrContextFactory *grFactory, |
| 1752 GrGLStandard gpuAPI) { |
| 1753 const char renderModeDescriptor[] = ""; |
| 1754 ErrorCombination errorsForAllConfigs; |
| 1755 uint32_t gmFlags = gm->getFlags(); |
| 1756 |
| 1757 for (int i = 0; i < configs.count(); i++) { |
| 1758 ConfigData config = gRec[configs[i]]; |
| 1759 const SkString shortNamePlusConfig = gmmain.make_shortname_plus_config(g
m->getName(), |
| 1760 c
onfig.fName); |
| 1761 |
| 1762 // Skip any tests that we don't even need to try. |
| 1763 // If any of these were skipped on a per-GM basis, record them as |
| 1764 // kIntentionallySkipped. |
| 1765 if (kPDF_Backend == config.fBackend) { |
| 1766 if (gmFlags & GM::kSkipPDF_Flag) { |
| 1767 gmmain.RecordSkippedTest(shortNamePlusConfig, |
| 1768 renderModeDescriptor, |
| 1769 config.fBackend); |
| 1770 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); |
| 1771 continue; |
| 1772 } |
| 1773 } |
| 1774 if ((gmFlags & GM::kSkip565_Flag) && |
| 1775 (kRaster_Backend == config.fBackend) && |
| 1776 (kRGB_565_SkColorType == config.fColorType)) { |
| 1777 gmmain.RecordSkippedTest(shortNamePlusConfig, |
| 1778 renderModeDescriptor, |
| 1779 config.fBackend); |
| 1780 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); |
| 1781 continue; |
| 1782 } |
| 1783 if (((gmFlags & GM::kSkipGPU_Flag) && kGPU_Backend == config.fBackend) |
| |
| 1784 ((gmFlags & GM::kGPUOnly_Flag) && kGPU_Backend != config.fBackend))
{ |
| 1785 gmmain.RecordSkippedTest(shortNamePlusConfig, |
| 1786 renderModeDescriptor, |
| 1787 config.fBackend); |
| 1788 errorsForAllConfigs.add(kIntentionallySkipped_ErrorType); |
| 1789 continue; |
| 1790 } |
| 1791 |
| 1792 // Now we know that we want to run this test and record its |
| 1793 // success or failure. |
| 1794 ErrorCombination errorsForThisConfig; |
| 1795 GrSurface* gpuTarget = NULL; |
| 1796 #if SK_SUPPORT_GPU |
| 1797 SkAutoTUnref<GrSurface> auGpuTarget; |
| 1798 if ((errorsForThisConfig.isEmpty()) && (kGPU_Backend == config.fBackend)
) { |
| 1799 if (FLAGS_resetGpuContext) { |
| 1800 grFactory->destroyContexts(); |
| 1801 } |
| 1802 GrContext* gr = grFactory->get(config.fGLContextType, gpuAPI); |
| 1803 bool grSuccess = false; |
| 1804 if (gr) { |
| 1805 // create a render target to back the device |
| 1806 GrSurfaceDesc desc; |
| 1807 desc.fConfig = kSkia8888_GrPixelConfig; |
| 1808 desc.fFlags = kRenderTarget_GrSurfaceFlag; |
| 1809 desc.fWidth = gm->getISize().width(); |
| 1810 desc.fHeight = gm->getISize().height(); |
| 1811 desc.fSampleCnt = config.fSampleCnt; |
| 1812 auGpuTarget.reset(gr->createUncachedTexture(desc, NULL, 0)); |
| 1813 if (auGpuTarget) { |
| 1814 gpuTarget = auGpuTarget; |
| 1815 grSuccess = true; |
| 1816 // Set the user specified cache limits if non-default. |
| 1817 size_t bytes; |
| 1818 int count; |
| 1819 gr->getResourceCacheLimits(&count, &bytes); |
| 1820 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeBytes) { |
| 1821 bytes = static_cast<size_t>(gGpuCacheSizeBytes); |
| 1822 } |
| 1823 if (DEFAULT_CACHE_VALUE != gGpuCacheSizeCount) { |
| 1824 count = gGpuCacheSizeCount; |
| 1825 } |
| 1826 gr->setResourceCacheLimits(count, bytes); |
| 1827 } |
| 1828 } |
| 1829 if (!grSuccess) { |
| 1830 errorsForThisConfig.add(kNoGpuContext_ErrorType); |
| 1831 } |
| 1832 } |
| 1833 #endif |
| 1834 |
| 1835 SkBitmap comparisonBitmap; |
| 1836 |
| 1837 const char* writePath; |
| 1838 if (FLAGS_writePath.count() == 1) { |
| 1839 writePath = FLAGS_writePath[0]; |
| 1840 } else { |
| 1841 writePath = NULL; |
| 1842 } |
| 1843 |
| 1844 if (errorsForThisConfig.isEmpty()) { |
| 1845 errorsForThisConfig.add(gmmain.test_drawing(gm, config, pdfRasterize
rs, |
| 1846 writePath, gpuTarget, |
| 1847 &comparisonBitmap)); |
| 1848 gmmain.RecordTestResults(errorsForThisConfig, shortNamePlusConfig, "
"); |
| 1849 } |
| 1850 |
| 1851 // TODO: run only if gmmain.test_drawing succeeded. |
| 1852 if (kRaster_Backend == config.fBackend) { |
| 1853 run_multiple_modes(gmmain, gm, config, gpuTarget, comparisonBitmap,
tileGridReplayScales); |
| 1854 } |
| 1855 |
| 1856 if (FLAGS_deferred && errorsForThisConfig.isEmpty() && |
| 1857 (kGPU_Backend == config.fBackend || kRaster_Backend == config.fBacke
nd)) { |
| 1858 errorsForThisConfig.add(gmmain.test_deferred_drawing(gm, config, com
parisonBitmap, |
| 1859 gpuTarget)); |
| 1860 } |
| 1861 |
| 1862 if (FLAGS_mpd && (kGPU_Backend == config.fBackend || kRaster_Backend ==
config.fBackend)) { |
| 1863 |
| 1864 if (gmFlags & GM::kSkipPicture_Flag) { |
| 1865 gmmain.RecordSkippedTest(shortNamePlusConfig, |
| 1866 renderModeDescriptor, |
| 1867 config.fBackend); |
| 1868 errorsForThisConfig.add(kIntentionallySkipped_ErrorType); |
| 1869 } else if (!(gmFlags & GM::kGPUOnly_Flag)) { |
| 1870 errorsForThisConfig.add(gmmain.testMPDDrawing(gm, config, |
| 1871 writePath, gpuTarg
et, |
| 1872 comparisonBitmap))
; |
| 1873 } |
| 1874 } |
| 1875 |
| 1876 errorsForAllConfigs.add(errorsForThisConfig); |
| 1877 } |
| 1878 return errorsForAllConfigs; |
| 1879 } |
| 1880 |
| 1881 |
| 1882 /** |
| 1883 * Read individual lines from a file, pushing them into the given array. |
| 1884 * |
| 1885 * @param filename path to the file to read |
| 1886 * @param lines array of strings to add the lines to |
| 1887 * @returns true if able to read lines from the file |
| 1888 */ |
| 1889 static bool read_lines_from_file(const char* filename, SkTArray<SkString> &lines
) { |
| 1890 SkAutoTUnref<SkStream> streamWrapper(SkStream::NewFromFile(filename)); |
| 1891 SkStream *stream = streamWrapper.get(); |
| 1892 if (!stream) { |
| 1893 SkDebugf("unable to read file '%s'\n", filename); |
| 1894 return false; |
| 1895 } |
| 1896 |
| 1897 char c; |
| 1898 SkString line; |
| 1899 while (1 == stream->read(&c, 1)) { |
| 1900 // If we hit either CR or LF, we've completed a line. |
| 1901 // |
| 1902 // TODO: If the file uses both CR and LF, this will return an extra blan
k |
| 1903 // line for each line of the file. Which is OK for current purposes... |
| 1904 // |
| 1905 // TODO: Does this properly handle unicode? It doesn't matter for |
| 1906 // current purposes... |
| 1907 if ((c == 0x0d) || (c == 0x0a)) { |
| 1908 lines.push_back(line); |
| 1909 line.reset(); |
| 1910 } else { |
| 1911 line.append(&c, 1); |
| 1912 } |
| 1913 } |
| 1914 lines.push_back(line); |
| 1915 return true; |
| 1916 } |
| 1917 |
| 1918 /** |
| 1919 * Return a list of all entries in an array of strings as a single string |
| 1920 * of this form: |
| 1921 * "item1", "item2", "item3" |
| 1922 */ |
| 1923 SkString list_all(const SkTArray<SkString> &stringArray); |
| 1924 SkString list_all(const SkTArray<SkString> &stringArray) { |
| 1925 SkString total; |
| 1926 for (int i = 0; i < stringArray.count(); i++) { |
| 1927 if (i > 0) { |
| 1928 total.append(", "); |
| 1929 } |
| 1930 total.append("\""); |
| 1931 total.append(stringArray[i]); |
| 1932 total.append("\""); |
| 1933 } |
| 1934 return total; |
| 1935 } |
| 1936 |
| 1937 /** |
| 1938 * Return a list of configuration names, as a single string of this form: |
| 1939 * "item1", "item2", "item3" |
| 1940 * |
| 1941 * @param configs configurations, as a list of indices into gRec |
| 1942 */ |
| 1943 SkString list_all_config_names(const SkTDArray<size_t> &configs); |
| 1944 SkString list_all_config_names(const SkTDArray<size_t> &configs) { |
| 1945 SkString total; |
| 1946 for (int i = 0; i < configs.count(); i++) { |
| 1947 if (i > 0) { |
| 1948 total.append(", "); |
| 1949 } |
| 1950 total.append("\""); |
| 1951 total.append(gRec[configs[i]].fName); |
| 1952 total.append("\""); |
| 1953 } |
| 1954 return total; |
| 1955 } |
| 1956 |
| 1957 static bool prepare_subdirectories(const char *root, bool useFileHierarchy, |
| 1958 const SkTDArray<size_t> &configs, |
| 1959 const SkTDArray<const PDFRasterizerData*>& pd
fRasterizers) { |
| 1960 if (!sk_mkdir(root)) { |
| 1961 return false; |
| 1962 } |
| 1963 if (useFileHierarchy) { |
| 1964 for (int i = 0; i < configs.count(); i++) { |
| 1965 ConfigData config = gRec[configs[i]]; |
| 1966 SkString subdir; |
| 1967 subdir.appendf("%s%c%s", root, SkPATH_SEPARATOR, config.fName); |
| 1968 if (!sk_mkdir(subdir.c_str())) { |
| 1969 return false; |
| 1970 } |
| 1971 |
| 1972 if (config.fBackend == kPDF_Backend) { |
| 1973 for (int j = 0; j < pdfRasterizers.count(); j++) { |
| 1974 SkString pdfSubdir = subdir; |
| 1975 pdfSubdir.appendf("-%s", pdfRasterizers[j]->fName); |
| 1976 if (!sk_mkdir(pdfSubdir.c_str())) { |
| 1977 return false; |
| 1978 } |
| 1979 } |
| 1980 } |
| 1981 } |
| 1982 } |
| 1983 return true; |
| 1984 } |
| 1985 |
| 1986 static bool parse_flags_configs(SkTDArray<size_t>* outConfigs, |
| 1987 GrContextFactory* grFactory, GrGLStandard gpuAPI) { |
| 1988 SkTDArray<size_t> excludeConfigs; |
| 1989 |
| 1990 for (int i = 0; i < FLAGS_config.count(); i++) { |
| 1991 const char* config = FLAGS_config[i]; |
| 1992 bool exclude = false; |
| 1993 if (*config == kExcludeConfigChar) { |
| 1994 exclude = true; |
| 1995 config += 1; |
| 1996 } |
| 1997 int index = findConfig(config); |
| 1998 if (index >= 0) { |
| 1999 if (exclude) { |
| 2000 *excludeConfigs.append() = index; |
| 2001 } else { |
| 2002 appendUnique<size_t>(outConfigs, index); |
| 2003 } |
| 2004 } else if (0 == strcmp(kDefaultsConfigStr, config)) { |
| 2005 if (exclude) { |
| 2006 SkDebugf("%c%s is not allowed.\n", |
| 2007 kExcludeConfigChar, kDefaultsConfigStr); |
| 2008 return false; |
| 2009 } |
| 2010 for (size_t c = 0; c < SK_ARRAY_COUNT(gRec); ++c) { |
| 2011 if (gRec[c].fRunByDefault) { |
| 2012 appendUnique<size_t>(outConfigs, c); |
| 2013 } |
| 2014 } |
| 2015 } else { |
| 2016 SkDebugf("unrecognized config %s\n", config); |
| 2017 return false; |
| 2018 } |
| 2019 } |
| 2020 |
| 2021 for (int i = 0; i < FLAGS_excludeConfig.count(); i++) { |
| 2022 int index = findConfig(FLAGS_excludeConfig[i]); |
| 2023 if (index >= 0) { |
| 2024 *excludeConfigs.append() = index; |
| 2025 } else { |
| 2026 SkDebugf("unrecognized excludeConfig %s\n", FLAGS_excludeConfig[i]); |
| 2027 return false; |
| 2028 } |
| 2029 } |
| 2030 |
| 2031 if (outConfigs->count() == 0) { |
| 2032 // if no config is specified by user, add the defaults |
| 2033 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { |
| 2034 if (gRec[i].fRunByDefault) { |
| 2035 *outConfigs->append() = i; |
| 2036 } |
| 2037 } |
| 2038 } |
| 2039 // now remove any explicitly excluded configs |
| 2040 for (int i = 0; i < excludeConfigs.count(); ++i) { |
| 2041 int index = outConfigs->find(excludeConfigs[i]); |
| 2042 if (index >= 0) { |
| 2043 outConfigs->remove(index); |
| 2044 // now assert that there was only one copy in configs[] |
| 2045 SkASSERT(outConfigs->find(excludeConfigs[i]) < 0); |
| 2046 } |
| 2047 } |
| 2048 |
| 2049 for (int i = 0; i < outConfigs->count(); ++i) { |
| 2050 size_t index = (*outConfigs)[i]; |
| 2051 if (kGPU_Backend == gRec[index].fBackend) { |
| 2052 #if SK_SUPPORT_GPU |
| 2053 if (!FLAGS_gpu) { |
| 2054 outConfigs->remove(i); |
| 2055 --i; |
| 2056 continue; |
| 2057 } |
| 2058 #endif |
| 2059 } else if (!FLAGS_cpu) { |
| 2060 outConfigs->remove(i); |
| 2061 --i; |
| 2062 continue; |
| 2063 } |
| 2064 #if SK_SUPPORT_GPU |
| 2065 SkASSERT(grFactory != NULL); |
| 2066 if (kGPU_Backend == gRec[index].fBackend) { |
| 2067 GrContext* ctx = grFactory->get(gRec[index].fGLContextType, gpuAPI); |
| 2068 if (NULL == ctx) { |
| 2069 SkDebugf("GrContext could not be created for config %s. Config w
ill be skipped.\n", |
| 2070 gRec[index].fName); |
| 2071 outConfigs->remove(i); |
| 2072 --i; |
| 2073 continue; |
| 2074 } |
| 2075 if (gRec[index].fSampleCnt > ctx->getMaxSampleCount()) { |
| 2076 SkDebugf("Sample count (%d) of config %s is not supported." |
| 2077 " Config will be skipped.\n", |
| 2078 gRec[index].fSampleCnt, gRec[index].fName); |
| 2079 outConfigs->remove(i); |
| 2080 --i; |
| 2081 } |
| 2082 } |
| 2083 #endif |
| 2084 } |
| 2085 |
| 2086 if (outConfigs->isEmpty()) { |
| 2087 SkDebugf("No configs to run."); |
| 2088 return false; |
| 2089 } |
| 2090 |
| 2091 // now show the user the set of configs that will be run. |
| 2092 SkString configStr("These configs will be run:"); |
| 2093 // show the user the config that will run. |
| 2094 for (int i = 0; i < outConfigs->count(); ++i) { |
| 2095 configStr.appendf(" %s", gRec[(*outConfigs)[i]].fName); |
| 2096 } |
| 2097 SkDebugf("%s\n", configStr.c_str()); |
| 2098 |
| 2099 return true; |
| 2100 } |
| 2101 |
| 2102 static bool parse_flags_pdf_rasterizers(const SkTDArray<size_t>& configs, |
| 2103 SkTDArray<const PDFRasterizerData*>* out
Rasterizers) { |
| 2104 // No need to run this check (and display the PDF rasterizers message) |
| 2105 // if no PDF backends are in the configs. |
| 2106 bool configHasPDF = false; |
| 2107 for (int i = 0; i < configs.count(); i++) { |
| 2108 if (gRec[configs[i]].fBackend == kPDF_Backend) { |
| 2109 configHasPDF = true; |
| 2110 break; |
| 2111 } |
| 2112 } |
| 2113 if (!configHasPDF) { |
| 2114 return true; |
| 2115 } |
| 2116 |
| 2117 if (FLAGS_pdfRasterizers.count() == 1 && |
| 2118 !strcmp(FLAGS_pdfRasterizers[0], "default")) { |
| 2119 for (int i = 0; i < (int)SK_ARRAY_COUNT(kPDFRasterizers); ++i) { |
| 2120 if (kPDFRasterizers[i].fRunByDefault) { |
| 2121 *outRasterizers->append() = &kPDFRasterizers[i]; |
| 2122 } |
| 2123 } |
| 2124 } else { |
| 2125 for (int i = 0; i < FLAGS_pdfRasterizers.count(); i++) { |
| 2126 const char* rasterizer = FLAGS_pdfRasterizers[i]; |
| 2127 const PDFRasterizerData* rasterizerPtr = |
| 2128 findPDFRasterizer(rasterizer); |
| 2129 if (rasterizerPtr == NULL) { |
| 2130 SkDebugf("unrecognized rasterizer %s\n", rasterizer); |
| 2131 return false; |
| 2132 } |
| 2133 appendUnique<const PDFRasterizerData*>(outRasterizers, |
| 2134 rasterizerPtr); |
| 2135 } |
| 2136 } |
| 2137 |
| 2138 // now show the user the set of configs that will be run. |
| 2139 SkString configStr("These PDF rasterizers will be run:"); |
| 2140 // show the user the config that will run. |
| 2141 for (int i = 0; i < outRasterizers->count(); ++i) { |
| 2142 configStr.appendf(" %s", (*outRasterizers)[i]->fName); |
| 2143 } |
| 2144 SkDebugf("%s\n", configStr.c_str()); |
| 2145 |
| 2146 return true; |
| 2147 } |
| 2148 |
| 2149 static bool parse_flags_ignore_error_types(ErrorCombination* outErrorTypes) { |
| 2150 if (FLAGS_ignoreErrorTypes.count() > 0) { |
| 2151 *outErrorTypes = ErrorCombination(); |
| 2152 for (int i = 0; i < FLAGS_ignoreErrorTypes.count(); i++) { |
| 2153 ErrorType type; |
| 2154 const char *name = FLAGS_ignoreErrorTypes[i]; |
| 2155 if (!getErrorTypeByName(name, &type)) { |
| 2156 SkDebugf("cannot find ErrorType with name '%s'\n", name); |
| 2157 return false; |
| 2158 } else { |
| 2159 outErrorTypes->add(type); |
| 2160 } |
| 2161 } |
| 2162 } |
| 2163 return true; |
| 2164 } |
| 2165 |
| 2166 /** |
| 2167 * Replace contents of ignoreTestNames with a list of test names, indicating |
| 2168 * which tests' failures should be ignored. |
| 2169 */ |
| 2170 static bool parse_flags_ignore_tests(SkTArray<SkString> &ignoreTestNames) { |
| 2171 ignoreTestNames.reset(); |
| 2172 |
| 2173 // Parse --ignoreFailuresFile |
| 2174 for (int i = 0; i < FLAGS_ignoreFailuresFile.count(); i++) { |
| 2175 SkTArray<SkString> linesFromFile; |
| 2176 if (!read_lines_from_file(FLAGS_ignoreFailuresFile[i], linesFromFile)) { |
| 2177 return false; |
| 2178 } else { |
| 2179 for (int j = 0; j < linesFromFile.count(); j++) { |
| 2180 SkString thisLine = linesFromFile[j]; |
| 2181 if (thisLine.isEmpty() || thisLine.startsWith('#')) { |
| 2182 // skip this line |
| 2183 } else { |
| 2184 ignoreTestNames.push_back(thisLine); |
| 2185 } |
| 2186 } |
| 2187 } |
| 2188 } |
| 2189 |
| 2190 return true; |
| 2191 } |
| 2192 |
| 2193 static bool parse_flags_modulo(int* moduloRemainder, int* moduloDivisor) { |
| 2194 if (FLAGS_modulo.count() == 2) { |
| 2195 *moduloRemainder = atoi(FLAGS_modulo[0]); |
| 2196 *moduloDivisor = atoi(FLAGS_modulo[1]); |
| 2197 if (*moduloRemainder < 0 || *moduloDivisor <= 0 || |
| 2198 *moduloRemainder >= *moduloDivisor) { |
| 2199 SkDebugf("invalid modulo values."); |
| 2200 return false; |
| 2201 } |
| 2202 } |
| 2203 return true; |
| 2204 } |
| 2205 |
| 2206 #if SK_SUPPORT_GPU |
| 2207 static bool parse_flags_gpu_cache(int* sizeBytes, int* sizeCount) { |
| 2208 if (FLAGS_gpuCacheSize.count() > 0) { |
| 2209 if (FLAGS_gpuCacheSize.count() != 2) { |
| 2210 SkDebugf("--gpuCacheSize requires two arguments\n"); |
| 2211 return false; |
| 2212 } |
| 2213 *sizeBytes = atoi(FLAGS_gpuCacheSize[0]); |
| 2214 *sizeCount = atoi(FLAGS_gpuCacheSize[1]); |
| 2215 } else { |
| 2216 *sizeBytes = DEFAULT_CACHE_VALUE; |
| 2217 *sizeCount = DEFAULT_CACHE_VALUE; |
| 2218 } |
| 2219 return true; |
| 2220 } |
| 2221 |
| 2222 static bool parse_flags_gl_standard(GrGLStandard* gpuAPI) { |
| 2223 if (0 == FLAGS_gpuAPI.count()) { |
| 2224 *gpuAPI = kNone_GrGLStandard; |
| 2225 return true; |
| 2226 } |
| 2227 if (1 == FLAGS_gpuAPI.count()) { |
| 2228 if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) { |
| 2229 *gpuAPI = kGL_GrGLStandard; |
| 2230 return true; |
| 2231 } |
| 2232 if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) { |
| 2233 *gpuAPI = kGLES_GrGLStandard; |
| 2234 return true; |
| 2235 } |
| 2236 } |
| 2237 SkDebugf("--gpuAPI invalid api value"); |
| 2238 return false; |
| 2239 } |
| 2240 #endif |
| 2241 |
| 2242 static bool parse_flags_tile_grid_replay_scales(SkTDArray<SkScalar>* outScales)
{ |
| 2243 *outScales->append() = SK_Scalar1; // By default only test at scale 1.0 |
| 2244 if (FLAGS_tileGridReplayScales.count() > 0) { |
| 2245 outScales->reset(); |
| 2246 for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) { |
| 2247 double val = atof(FLAGS_tileGridReplayScales[i]); |
| 2248 if (0 < val) { |
| 2249 *outScales->append() = SkDoubleToScalar(val); |
| 2250 } |
| 2251 } |
| 2252 if (0 == outScales->count()) { |
| 2253 // Should have at least one scale |
| 2254 SkDebugf("--tileGridReplayScales requires at least one scale.\n"); |
| 2255 return false; |
| 2256 } |
| 2257 } |
| 2258 return true; |
| 2259 } |
| 2260 |
| 2261 static bool parse_flags_gmmain_paths(GMMain* gmmain) { |
| 2262 gmmain->fUseFileHierarchy = FLAGS_hierarchy; |
| 2263 gmmain->fWriteChecksumBasedFilenames = FLAGS_writeChecksumBasedFilenames; |
| 2264 |
| 2265 if (FLAGS_mismatchPath.count() == 1) { |
| 2266 gmmain->fMismatchPath = FLAGS_mismatchPath[0]; |
| 2267 } |
| 2268 |
| 2269 if (FLAGS_missingExpectationsPath.count() == 1) { |
| 2270 gmmain->fMissingExpectationsPath = FLAGS_missingExpectationsPath[0]; |
| 2271 } |
| 2272 |
| 2273 if (FLAGS_readPath.count() == 1) { |
| 2274 const char* readPath = FLAGS_readPath[0]; |
| 2275 if (!sk_exists(readPath)) { |
| 2276 SkDebugf("readPath %s does not exist!\n", readPath); |
| 2277 return false; |
| 2278 } |
| 2279 if (sk_isdir(readPath)) { |
| 2280 if (FLAGS_verbose) { |
| 2281 SkDebugf("reading from %s\n", readPath); |
| 2282 } |
| 2283 gmmain->fExpectationsSource.reset(SkNEW_ARGS( |
| 2284 IndividualImageExpectationsSource, (readPath))); |
| 2285 } else { |
| 2286 if (FLAGS_verbose) { |
| 2287 SkDebugf("reading expectations from JSON summary file %s\n", rea
dPath); |
| 2288 } |
| 2289 gmmain->fExpectationsSource.reset(SkNEW_ARGS(JsonExpectationsSource,
(readPath))); |
| 2290 } |
| 2291 } |
| 2292 return true; |
| 2293 } |
| 2294 |
| 2295 static bool parse_flags_jpeg_quality() { |
| 2296 if (FLAGS_pdfJpegQuality < -1 || FLAGS_pdfJpegQuality > 100) { |
| 2297 SkDebugf("%s\n", "pdfJpegQuality must be in [-1 .. 100] range."); |
| 2298 return false; |
| 2299 } |
| 2300 return true; |
| 2301 } |
| 2302 |
| 2303 int tool_main(int argc, char** argv); |
| 2304 int tool_main(int argc, char** argv) { |
| 2305 SetupCrashHandler(); |
| 2306 |
| 2307 SkString usage; |
| 2308 usage.printf("Run the golden master tests.\n"); |
| 2309 SkCommandLineFlags::SetUsage(usage.c_str()); |
| 2310 SkCommandLineFlags::Parse(argc, argv); |
| 2311 |
| 2312 #if SK_ENABLE_INST_COUNT |
| 2313 if (FLAGS_leaks) { |
| 2314 gPrintInstCount = true; |
| 2315 } |
| 2316 #endif |
| 2317 |
| 2318 SkAutoGraphics ag; |
| 2319 |
| 2320 setSystemPreferences(); |
| 2321 GMMain gmmain; |
| 2322 |
| 2323 SkTDArray<size_t> configs; |
| 2324 |
| 2325 int moduloRemainder = -1; |
| 2326 int moduloDivisor = -1; |
| 2327 SkTDArray<const PDFRasterizerData*> pdfRasterizers; |
| 2328 SkTDArray<SkScalar> tileGridReplayScales; |
| 2329 #if SK_SUPPORT_GPU |
| 2330 GrGLStandard gpuAPI = kNone_GrGLStandard; |
| 2331 GrContext::Options grContextOpts; |
| 2332 grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks; |
| 2333 GrContextFactory* grFactory = new GrContextFactory(grContextOpts); |
| 2334 #else |
| 2335 GrGLStandard gpuAPI = 0; |
| 2336 GrContextFactory* grFactory = NULL; |
| 2337 #endif |
| 2338 |
| 2339 if (FLAGS_dryRun) { |
| 2340 SkDebugf( "Doing a dry run; no tests will actually be executed.\n"); |
| 2341 } |
| 2342 |
| 2343 if (!parse_flags_modulo(&moduloRemainder, &moduloDivisor) || |
| 2344 !parse_flags_ignore_error_types(&gmmain.fIgnorableErrorTypes) || |
| 2345 !parse_flags_ignore_tests(gmmain.fIgnorableTestNames) || |
| 2346 #if SK_SUPPORT_GPU |
| 2347 !parse_flags_gpu_cache(&gGpuCacheSizeBytes, &gGpuCacheSizeCount) || |
| 2348 !parse_flags_gl_standard(&gpuAPI) || |
| 2349 #endif |
| 2350 !parse_flags_tile_grid_replay_scales(&tileGridReplayScales) || |
| 2351 !parse_flags_jpeg_quality() || |
| 2352 !parse_flags_configs(&configs, grFactory, gpuAPI) || |
| 2353 !parse_flags_pdf_rasterizers(configs, &pdfRasterizers) || |
| 2354 !parse_flags_gmmain_paths(&gmmain)) { |
| 2355 return -1; |
| 2356 } |
| 2357 |
| 2358 if (FLAGS_verbose) { |
| 2359 if (FLAGS_writePath.count() == 1) { |
| 2360 SkDebugf("writing to %s\n", FLAGS_writePath[0]); |
| 2361 } |
| 2362 if (gmmain.fMismatchPath) { |
| 2363 SkDebugf("writing mismatches to %s\n", gmmain.fMismatchPath); |
| 2364 } |
| 2365 if (gmmain.fMissingExpectationsPath) { |
| 2366 SkDebugf("writing images without expectations to %s\n", |
| 2367 gmmain.fMissingExpectationsPath); |
| 2368 } |
| 2369 if (FLAGS_writePicturePath.count() == 1) { |
| 2370 SkDebugf("writing pictures to %s\n", FLAGS_writePicturePath[0]); |
| 2371 } |
| 2372 if (!GetResourcePath().isEmpty()) { |
| 2373 SkDebugf("reading resources from %s\n", GetResourcePath().c_str()); |
| 2374 } |
| 2375 } |
| 2376 |
| 2377 int gmsRun = 0; |
| 2378 int gmIndex = -1; |
| 2379 SkString moduloStr; |
| 2380 |
| 2381 if (!FLAGS_dryRun) { |
| 2382 // If we will be writing out files, prepare subdirectories. |
| 2383 if (FLAGS_writePath.count() == 1) { |
| 2384 if (!prepare_subdirectories(FLAGS_writePath[0], gmmain.fUseFileHiera
rchy, |
| 2385 configs, pdfRasterizers)) { |
| 2386 return -1; |
| 2387 } |
| 2388 } |
| 2389 if (gmmain.fMismatchPath) { |
| 2390 if (!prepare_subdirectories(gmmain.fMismatchPath, gmmain.fUseFileHie
rarchy, |
| 2391 configs, pdfRasterizers)) { |
| 2392 return -1; |
| 2393 } |
| 2394 } |
| 2395 if (gmmain.fMissingExpectationsPath) { |
| 2396 if (!prepare_subdirectories(gmmain.fMissingExpectationsPath, gmmain.
fUseFileHierarchy, |
| 2397 configs, pdfRasterizers)) { |
| 2398 return -1; |
| 2399 } |
| 2400 } |
| 2401 } |
| 2402 Iter iter; |
| 2403 GM* gm; |
| 2404 while ((gm = iter.next()) != NULL) { |
| 2405 if (FLAGS_forcePerspectiveMatrix) { |
| 2406 SkMatrix perspective; |
| 2407 perspective.setIdentity(); |
| 2408 perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000))); |
| 2409 perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), |
| 2410 SkIntToScalar(25))); |
| 2411 |
| 2412 gm->setStarterMatrix(perspective); |
| 2413 } |
| 2414 SkAutoTDelete<GM> adgm(gm); |
| 2415 ++gmIndex; |
| 2416 if (moduloRemainder >= 0) { |
| 2417 if ((gmIndex % moduloDivisor) != moduloRemainder) { |
| 2418 continue; |
| 2419 } |
| 2420 moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor); |
| 2421 } |
| 2422 |
| 2423 const char* shortName = gm->getName(); |
| 2424 |
| 2425 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, shortName)) { |
| 2426 continue; |
| 2427 } |
| 2428 |
| 2429 gmsRun++; |
| 2430 SkISize size = gm->getISize(); |
| 2431 SkDebugf("%4dM %sdrawing... %s [%d %d]\n", |
| 2432 sk_tools::getMaxResidentSetSizeMB(), moduloStr.c_str(), shortNa
me, |
| 2433 size.width(), size.height()); |
| 2434 if (!FLAGS_dryRun) |
| 2435 run_multiple_configs(gmmain, gm, configs, pdfRasterizers, tileGridRe
playScales, |
| 2436 grFactory, gpuAPI); |
| 2437 } |
| 2438 |
| 2439 if (FLAGS_dryRun) |
| 2440 return 0; |
| 2441 |
| 2442 SkTArray<SkString> modes; |
| 2443 gmmain.GetRenderModesEncountered(modes); |
| 2444 int modeCount = modes.count(); |
| 2445 |
| 2446 // Now that we have run all the tests and thus know the full set of renderMo
des that we |
| 2447 // tried to run, we can call RecordTestResults() to record the cases in whic
h we skipped |
| 2448 // ALL renderModes. |
| 2449 // See http://skbug.com/1994 and https://codereview.chromium.org/129203002/ |
| 2450 int testCount = gmmain.fTestsSkippedOnAllRenderModes.count(); |
| 2451 for (int testNum = 0; testNum < testCount; ++testNum) { |
| 2452 const SkString &shortNamePlusConfig = gmmain.fTestsSkippedOnAllRenderMod
es[testNum]; |
| 2453 for (int modeNum = 0; modeNum < modeCount; ++modeNum) { |
| 2454 gmmain.RecordTestResults(kIntentionallySkipped_ErrorType, shortNameP
lusConfig, |
| 2455 modes[modeNum].c_str()); |
| 2456 } |
| 2457 } |
| 2458 |
| 2459 bool reportError = false; |
| 2460 if (gmmain.NumSignificantErrors() > 0) { |
| 2461 reportError = true; |
| 2462 } |
| 2463 |
| 2464 // We test every GM against every config, and for every raster config also t
est every mode. |
| 2465 int rasterConfigs = 0; |
| 2466 for (int i = 0; i < configs.count(); i++) { |
| 2467 if (gRec[configs[i]].fBackend == kRaster_Backend) { |
| 2468 rasterConfigs++; |
| 2469 } |
| 2470 } |
| 2471 // For raster configs, we run all renderModes; for non-raster configs, just
default renderMode |
| 2472 const int expectedNumberOfTests = rasterConfigs * gmsRun * modeCount |
| 2473 + (configs.count() - rasterConfigs) * gmsRun
; |
| 2474 |
| 2475 // Output summary to stdout. |
| 2476 if (FLAGS_verbose) { |
| 2477 SkDebugf("Ran %d GMs\n", gmsRun); |
| 2478 SkDebugf("... over %2d configs [%s]\n", configs.count(), |
| 2479 list_all_config_names(configs).c_str()); |
| 2480 SkDebugf("... and %2d modes [%s]\n", modeCount, list_all(modes).c_str
()); |
| 2481 SkDebugf("... so there should be a total of %d tests.\n", expectedNumber
OfTests); |
| 2482 } |
| 2483 gmmain.ListErrors(FLAGS_verbose); |
| 2484 |
| 2485 // TODO(epoger): Enable this check for Android, too, once we resolve |
| 2486 // https://code.google.com/p/skia/issues/detail?id=1222 |
| 2487 // ('GM is unexpectedly skipping tests on Android') |
| 2488 #ifndef SK_BUILD_FOR_ANDROID |
| 2489 if (expectedNumberOfTests != gmmain.fTestsRun) { |
| 2490 SkDebugf("expected %d tests, but ran or skipped %d tests\n", |
| 2491 expectedNumberOfTests, gmmain.fTestsRun); |
| 2492 reportError = true; |
| 2493 } |
| 2494 #endif |
| 2495 |
| 2496 if (FLAGS_writeJsonSummaryPath.count() == 1) { |
| 2497 Json::Value root = CreateJsonTree( |
| 2498 gmmain.fJsonExpectedResults, |
| 2499 gmmain.fJsonActualResults_Failed, gmmain.fJsonActualResults_FailureI
gnored, |
| 2500 gmmain.fJsonActualResults_NoComparison, gmmain.fJsonActualResults_Su
cceeded); |
| 2501 std::string jsonStdString = root.toStyledString(); |
| 2502 SkFILEWStream stream(FLAGS_writeJsonSummaryPath[0]); |
| 2503 stream.write(jsonStdString.c_str(), jsonStdString.length()); |
| 2504 } |
| 2505 |
| 2506 #if SK_SUPPORT_GPU |
| 2507 |
| 2508 #if GR_CACHE_STATS |
| 2509 for (int i = 0; i < configs.count(); i++) { |
| 2510 ConfigData config = gRec[configs[i]]; |
| 2511 |
| 2512 if (FLAGS_verbose && (kGPU_Backend == config.fBackend)) { |
| 2513 GrContext* gr = grFactory->get(config.fGLContextType, gpuAPI); |
| 2514 |
| 2515 SkDebugf("config: %s %x\n", config.fName, gr); |
| 2516 gr->printCacheStats(); |
| 2517 } |
| 2518 } |
| 2519 #endif |
| 2520 |
| 2521 #if GR_DUMP_FONT_CACHE |
| 2522 for (int i = 0; i < configs.count(); i++) { |
| 2523 ConfigData config = gRec[configs[i]]; |
| 2524 |
| 2525 if (kGPU_Backend == config.fBackend) { |
| 2526 GrContext* gr = grFactory->get(config.fGLContextType, gpuAPI); |
| 2527 |
| 2528 gr->dumpFontCache(); |
| 2529 } |
| 2530 } |
| 2531 #endif |
| 2532 |
| 2533 delete grFactory; |
| 2534 #endif |
| 2535 |
| 2536 return (reportError) ? -1 : 0; |
| 2537 } |
| 2538 |
| 2539 void GMMain::InstallFilter(SkCanvas* canvas) { |
| 2540 if (FLAGS_forceBWtext) { |
| 2541 canvas->setDrawFilter(SkNEW(BWTextDrawFilter))->unref(); |
| 2542 } |
| 2543 } |
| 2544 |
| 2545 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) |
| 2546 int main(int argc, char * const argv[]) { |
| 2547 return tool_main(argc, (char**) argv); |
| 2548 } |
| 2549 #endif |
OLD | NEW |