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