• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FailRecFailRec114     FailRec() : fIsPixelError(false) {}
FailRecFailRec115     FailRec(const SkString& name) : fName(name), fIsPixelError(false) {}
116 };
117 
118 class Iter {
119 public:
Iter()120     Iter() {
121         this->reset();
122     }
123 
reset()124     void reset() {
125         fReg = GMRegistry::Head();
126     }
127 
next()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 
Count()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 };
filter(SkPaint * p,Type t)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:
GMMain()207     GMMain() {
208         // Set default values of member variables, which tool_main()
209         // may override.
210         fUseFileHierarchy = false;
211         fMismatchPath = NULL;
212     }
213 
make_name(const char shortName[],const char configName[])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     */
force_all_opaque(const SkBitmap & bitmap)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 
force_all_opaque_8888(const SkBitmap & bitmap)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 
write_bitmap(const SkString & path,const SkBitmap & bitmap)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.
RecordError(ErrorBitfield errorType,const SkString & name,const char renderModeDescriptor[])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.
ListErrors()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 
write_document(const SkString & path,const SkDynamicMemoryWStream & document)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      */
setup_bitmap(const ConfigData & gRec,SkISize & size,SkBitmap * bitmap)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      */
complete_bitmap(SkBitmap * bitmap)379     static void complete_bitmap(SkBitmap* bitmap) {
380         force_all_opaque(*bitmap);
381     }
382 
installFilter(SkCanvas * canvas)383     static void installFilter(SkCanvas* canvas) {
384         if (gForceBWtext) {
385             canvas->setDrawFilter(new BWTextDrawFilter)->unref();
386         }
387     }
388 
invokeGM(GM * gm,SkCanvas * canvas,bool isPDF,bool isDeferred)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 
generate_image(GM * gm,const ConfigData & gRec,GrContext * context,GrRenderTarget * rt,SkBitmap * bitmap,bool deferred)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 
generate_image_from_picture(GM * gm,const ConfigData & gRec,SkPicture * pict,SkBitmap * bitmap,SkScalar scale=SK_Scalar1)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 
generate_pdf(GM * gm,SkDynamicMemoryWStream & pdf)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 
generate_xps(GM * gm,SkDynamicMemoryWStream & xps)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 
write_reference_image(const ConfigData & gRec,const char writePath[],const char renderModeDescriptor[],const SkString & name,SkBitmap & bitmap,SkDynamicMemoryWStream * document)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      */
report_bitmap_diffs(const SkBitmap & expectedBitmap,const SkBitmap & actualBitmap,const char * testName)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      */
compare_to_expectations(Expectations expectations,const SkBitmap & actualBitmap,const SkString & baseNameString,const char renderModeDescriptor[],bool addToJsonSummary=false)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      */
add_actual_results_to_json_summary(const char testName[],Checksum actualChecksum,ErrorBitfield result,bool ignoreFailure)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      */
add_expected_results_to_json_summary(const char testName[],Expectations expectations)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      */
compare_test_results_to_stored_expectations(GM * gm,const ConfigData & gRec,const char writePath[],SkBitmap & actualBitmap,SkDynamicMemoryWStream * pdf)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      */
compare_test_results_to_reference_bitmap(GM * gm,const ConfigData & gRec,const char renderModeDescriptor[],SkBitmap & actualBitmap,const SkBitmap * referenceBitmap)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 
generate_new_picture(GM * gm,BbhType bbhType,uint32_t recordFlags,SkScalar scale=SK_Scalar1)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 
stream_to_new_picture(const SkPicture & src)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.
test_drawing(GM * gm,const ConfigData & gRec,const char writePath[],GrContext * context,GrRenderTarget * rt,SkBitmap * bitmap)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 
test_deferred_drawing(GM * gm,const ConfigData & gRec,const SkBitmap & referenceBitmap,GrContext * context,GrRenderTarget * rt)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 
test_pipe_playback(GM * gm,const ConfigData & gRec,const SkBitmap & referenceBitmap)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 
test_tiled_pipe_playback(GM * gm,const ConfigData & gRec,const SkBitmap & referenceBitmap)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 
usage(const char * argv0)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 
findConfig(const char config[])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 
skip_name(const SkTDArray<const char * > array,const char name[])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  */
SetGr(GrContext * grContext)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();
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:
AutoResetGr()1131     AutoResetGr() : fOld(NULL) {}
set(GrContext * context)1132     void set(GrContext* context) {
1133         SkASSERT(NULL == fOld);
1134         fOld = GetGr();
1135         SkSafeRef(fOld);
1136         SetGr(context);
1137     }
~AutoResetGr()1138     ~AutoResetGr() { SetGr(fOld); SkSafeUnref(fOld); }
1139 private:
1140     GrContext* fOld;
1141 };
1142 #else
1143 GrContext* GetGr() { return NULL; }
1144 #endif
1145 }
1146 
appendUnique(SkTDArray<T> * array,const T & value)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);
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)
main(int argc,char * const argv[])1717 int main(int argc, char * const argv[]) {
1718     return tool_main(argc, (char**) argv);
1719 }
1720 #endif
1721