1 /*
2  * Copyright 2013 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 #include "GrContext.h"
9 #include "GrContextFactory.h"
10 #include "GrRenderTarget.h"
11 #include "SkGpuDevice.h"
12 #include "gl/GrGLDefines.h"
13 
14 #include "SkBitmap.h"
15 #include "SkCanvas.h"
16 #include "SkColor.h"
17 #include "SkDevice.h"
18 #include "SkGraphics.h"
19 #include "SkImageEncoder.h"
20 #include "SkOSFile.h"
21 #include "SkPicture.h"
22 #include "SkRTConf.h"
23 #include "SkStream.h"
24 #include "SkString.h"
25 #include "SkTArray.h"
26 #include "SkTDArray.h"
27 #include "SkTaskGroup.h"
28 #include "SkTime.h"
29 #include "Test.h"
30 
31 #if !SK_SUPPORT_GPU
32 #error "GPU support required"
33 #endif
34 
35 #ifdef SK_BUILD_FOR_WIN
36     #define PATH_SLASH "\\"
37     #define IN_DIR "D:\\9-30-13\\"
38     #define OUT_DIR "D:\\skpSkGr\\11\\"
39     #define LINE_FEED "\r\n"
40 #else
41     #define PATH_SLASH "/"
42     #define IN_DIR "/usr/local/google/home/caryclark" PATH_SLASH "9-30-13-skp"
43     #define OUT_DIR "/media/01CD75512A7F9EE0/4" PATH_SLASH
44     #define LINE_FEED "\n"
45 #endif
46 
47 #define PATH_STR_SIZE 512
48 
49 static const struct {
50     int directory;
51     const char* filename;
52 } skipOverSkGr[] = {
53     {1, "http___accuweather_com_.skp"},  // Couldn't convert bitmap to texture.http___absoku072_com_
54 };
55 
56 static const size_t skipOverSkGrCount = SK_ARRAY_COUNT(skipOverSkGr);
57 
58 /////////////////////////////////////////
59 
60 class SkpSkGrThreadedRunnable;
61 
62 enum TestStep {
63     kCompareBits,
64     kEncodeFiles,
65 };
66 
67 enum {
68     kMaxLength = 128,
69     kMaxFiles = 128,
70 };
71 
72 struct TestResult {
initTestResult73     void init(int dirNo) {
74         fDirNo = dirNo;
75         sk_bzero(fFilename, sizeof(fFilename));
76         fTestStep = kCompareBits;
77         fScaleOversized = true;
78     }
79 
statusTestResult80     SkString status() {
81         SkString outStr;
82         outStr.printf("%s %d %d%s", fFilename, fPixelError, fTime, LINE_FEED);
83         return outStr;
84     }
85 
TestTestResult86     static void Test(int dirNo, const char* filename, TestStep testStep, bool verbose) {
87         TestResult test;
88         test.init(dirNo);
89         test.fTestStep = testStep;
90         strcpy(test.fFilename, filename);
91         test.testOne();
92         if (verbose) {
93             SkDebugf("%s", test.status().c_str());
94         }
95     }
96 
testTestResult97     void test(int dirNo, const SkString& filename) {
98         init(dirNo);
99         strcpy(fFilename, filename.c_str());
100         testOne();
101     }
102 
103     void testOne();
104 
105     char fFilename[kMaxLength];
106     TestStep fTestStep;
107     int fDirNo;
108     int fPixelError;
109     int fTime;
110     bool fScaleOversized;
111 };
112 
113 struct SkpSkGrThreadState {
initSkpSkGrThreadState114     void init(int dirNo) {
115         fResult.init(dirNo);
116         fFoundCount = 0;
117         fSmallestError = 0;
118         sk_bzero(fFilesFound, sizeof(fFilesFound));
119         sk_bzero(fDirsFound, sizeof(fDirsFound));
120         sk_bzero(fError, sizeof(fError));
121     }
122 
123     char fFilesFound[kMaxFiles][kMaxLength];
124     int fDirsFound[kMaxFiles];
125     int fError[kMaxFiles];
126     int fFoundCount;
127     int fSmallestError;
128     skiatest::Reporter* fReporter;
129     TestResult fResult;
130 };
131 
132 struct SkpSkGrThreadedTestRunner {
SkpSkGrThreadedTestRunnerSkpSkGrThreadedTestRunner133     SkpSkGrThreadedTestRunner(skiatest::Reporter* reporter)
134         : fReporter(reporter) {
135     }
136 
137     ~SkpSkGrThreadedTestRunner();
138     void render();
139     SkTDArray<SkpSkGrThreadedRunnable*> fRunnables;
140     skiatest::Reporter* fReporter;
141 };
142 
143 class SkpSkGrThreadedRunnable {
144 public:
SkpSkGrThreadedRunnable(void (* testFun)(SkpSkGrThreadState *),int dirNo,const char * str,SkpSkGrThreadedTestRunner * runner)145     SkpSkGrThreadedRunnable(void (*testFun)(SkpSkGrThreadState*), int dirNo, const char* str,
146             SkpSkGrThreadedTestRunner* runner) {
147         SkASSERT(strlen(str) < sizeof(fState.fResult.fFilename) - 1);
148         fState.init(dirNo);
149         strcpy(fState.fResult.fFilename, str);
150         fState.fReporter = runner->fReporter;
151         fTestFun = testFun;
152     }
153 
operator ()()154     void operator()() {
155         SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
156         (*fTestFun)(&fState);
157     }
158 
159     SkpSkGrThreadState fState;
160     void (*fTestFun)(SkpSkGrThreadState*);
161 };
162 
~SkpSkGrThreadedTestRunner()163 SkpSkGrThreadedTestRunner::~SkpSkGrThreadedTestRunner() {
164     for (int index = 0; index < fRunnables.count(); index++) {
165         delete fRunnables[index];
166     }
167 }
168 
render()169 void SkpSkGrThreadedTestRunner::render() {
170     SkTaskGroup().batch(fRunnables.count(), [&](int i) {
171         fRunnables[i]();
172     });
173 }
174 
175 ////////////////////////////////////////////////
176 
177 static const char outGrDir[] = OUT_DIR "grTest";
178 static const char outSkDir[] = OUT_DIR "skTest";
179 static const char outSkpDir[] = OUT_DIR "skpTest";
180 static const char outDiffDir[] = OUT_DIR "outTest";
181 static const char outStatusDir[] = OUT_DIR "statusTest";
182 
make_filepath(int dirIndex,const char * dir,const char * name)183 static SkString make_filepath(int dirIndex, const char* dir, const char* name) {
184     SkString path(dir);
185     if (dirIndex) {
186         path.appendf("%d", dirIndex);
187     }
188     path.append(PATH_SLASH);
189     path.append(name);
190     return path;
191 }
192 
make_in_dir_name(int dirIndex)193 static SkString make_in_dir_name(int dirIndex) {
194     SkString dirName(IN_DIR);
195     dirName.appendf("%d", dirIndex);
196     if (!sk_exists(dirName.c_str())) {
197         SkDebugf("could not read dir %s\n", dirName.c_str());
198         return SkString();
199     }
200     return dirName;
201 }
202 
make_out_dirs()203 static bool make_out_dirs() {
204     SkString outDir = make_filepath(0, OUT_DIR, "");
205     if (!sk_exists(outDir.c_str())) {
206         if (!sk_mkdir(outDir.c_str())) {
207             SkDebugf("could not create dir %s\n", outDir.c_str());
208             return false;
209         }
210     }
211     SkString grDir = make_filepath(0, outGrDir, "");
212     if (!sk_exists(grDir.c_str())) {
213         if (!sk_mkdir(grDir.c_str())) {
214             SkDebugf("could not create dir %s\n", grDir.c_str());
215             return false;
216         }
217     }
218     SkString skDir = make_filepath(0, outSkDir, "");
219     if (!sk_exists(skDir.c_str())) {
220         if (!sk_mkdir(skDir.c_str())) {
221             SkDebugf("could not create dir %s\n", skDir.c_str());
222             return false;
223         }
224     }
225     SkString skpDir = make_filepath(0, outSkpDir, "");
226     if (!sk_exists(skpDir.c_str())) {
227         if (!sk_mkdir(skpDir.c_str())) {
228             SkDebugf("could not create dir %s\n", skpDir.c_str());
229             return false;
230         }
231     }
232     SkString diffDir = make_filepath(0, outDiffDir, "");
233     if (!sk_exists(diffDir.c_str())) {
234         if (!sk_mkdir(diffDir.c_str())) {
235             SkDebugf("could not create dir %s\n", diffDir.c_str());
236             return false;
237         }
238     }
239     SkString statusDir = make_filepath(0, outStatusDir, "");
240     if (!sk_exists(statusDir.c_str())) {
241         if (!sk_mkdir(statusDir.c_str())) {
242             SkDebugf("could not create dir %s\n", statusDir.c_str());
243             return false;
244         }
245     }
246     return true;
247 }
248 
make_png_name(const char * filename)249 static SkString make_png_name(const char* filename) {
250     SkString pngName = SkString(filename);
251     pngName.remove(pngName.size() - 3, 3);
252     pngName.append("png");
253     return pngName;
254 }
255 
256 typedef GrContextFactory::GLContextType GLContextType;
257 #ifdef SK_BUILD_FOR_WIN
258 static const GLContextType kAngle = GrContextFactory::kANGLE_GLContextType;
259 #else
260 static const GLContextType kNative = GrContextFactory::kNative_GLContextType;
261 #endif
262 
similarBits(const SkBitmap & gr,const SkBitmap & sk)263 static int similarBits(const SkBitmap& gr, const SkBitmap& sk) {
264     const int kRowCount = 3;
265     const int kThreshold = 3;
266     int width = SkTMin(gr.width(), sk.width());
267     if (width < kRowCount) {
268         return true;
269     }
270     int height = SkTMin(gr.height(), sk.height());
271     if (height < kRowCount) {
272         return true;
273     }
274     int errorTotal = 0;
275     SkTArray<char, true> errorRows;
276     errorRows.push_back_n(width * kRowCount);
277     SkAutoLockPixels autoGr(gr);
278     SkAutoLockPixels autoSk(sk);
279     char* base = &errorRows[0];
280     for (int y = 0; y < height; ++y) {
281         SkPMColor* grRow = gr.getAddr32(0, y);
282         SkPMColor* skRow = sk.getAddr32(0, y);
283         char* cOut = &errorRows[(y % kRowCount) * width];
284         for (int x = 0; x < width; ++x) {
285             SkPMColor grColor = grRow[x];
286             SkPMColor skColor = skRow[x];
287             int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor);
288             int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor);
289             int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor);
290             int error = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db)));
291             if ((cOut[x] = error >= kThreshold) && x >= 2
292                     && base[x - 2] && base[width + x - 2] && base[width * 2 + x - 2]
293                     && base[x - 1] && base[width + x - 1] && base[width * 2 + x - 1]
294                     && base[x - 0] && base[width + x - 0] && base[width * 2 + x - 0]) {
295                 errorTotal += error;
296             }
297         }
298     }
299     return errorTotal;
300 }
301 
addError(SkpSkGrThreadState * data)302 static bool addError(SkpSkGrThreadState* data) {
303     bool foundSmaller = false;
304     int dCount = data->fFoundCount;
305     int pixelError = data->fResult.fPixelError;
306     if (data->fFoundCount < kMaxFiles) {
307         data->fError[dCount] = pixelError;
308         strcpy(data->fFilesFound[dCount], data->fResult.fFilename);
309         data->fDirsFound[dCount] = data->fResult.fDirNo;
310         ++data->fFoundCount;
311     } else if (pixelError > data->fSmallestError) {
312         int smallest = SK_MaxS32;
313         int smallestIndex = 0;
314         for (int index = 0; index < kMaxFiles; ++index) {
315             if (smallest > data->fError[index]) {
316                 smallest = data->fError[index];
317                 smallestIndex = index;
318             }
319         }
320         data->fError[smallestIndex] = pixelError;
321         strcpy(data->fFilesFound[smallestIndex], data->fResult.fFilename);
322         data->fDirsFound[smallestIndex] = data->fResult.fDirNo;
323         data->fSmallestError = SK_MaxS32;
324         for (int index = 0; index < kMaxFiles; ++index) {
325             if (data->fSmallestError > data->fError[index]) {
326                 data->fSmallestError = data->fError[index];
327             }
328         }
329         SkDebugf("*%d*", data->fSmallestError);
330         foundSmaller = true;
331     }
332     return foundSmaller;
333 }
334 
timePict(SkPicture * pic,SkCanvas * canvas)335 static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) {
336     canvas->save();
337     int pWidth = pic->width();
338     int pHeight = pic->height();
339     const int maxDimension = 1000;
340     const int slices = 3;
341     int xInterval = SkTMax(pWidth - maxDimension, 0) / (slices - 1);
342     int yInterval = SkTMax(pHeight - maxDimension, 0) / (slices - 1);
343     SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)),
344             SkIntToScalar(SkTMin(maxDimension, pHeight))};
345     canvas->clipRect(rect);
346     SkMSec start = SkTime::GetMSecs();
347     for (int x = 0; x < slices; ++x) {
348         for (int y = 0; y < slices; ++y) {
349             pic->draw(canvas);
350             canvas->translate(0, SkIntToScalar(yInterval));
351         }
352         canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices));
353     }
354     SkMSec end = SkTime::GetMSecs();
355     canvas->restore();
356     return end - start;
357 }
358 
drawPict(SkPicture * pic,SkCanvas * canvas,int scale)359 static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) {
360     canvas->clear(SK_ColorWHITE);
361     if (scale != 1) {
362         canvas->save();
363         canvas->scale(1.0f / scale, 1.0f / scale);
364     }
365     pic->draw(canvas);
366     if (scale != 1) {
367         canvas->restore();
368     }
369 }
370 
writePict(const SkBitmap & bitmap,const char * outDir,const char * pngName)371 static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) {
372     SkString outFile = make_filepath(0, outDir, pngName);
373     if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap,
374             SkImageEncoder::kPNG_Type, 100)) {
375         SkDebugf("unable to encode gr %s (width=%d height=%d)br \n", pngName,
376                     bitmap.width(), bitmap.height());
377     }
378 }
379 
testOne()380 void TestResult::testOne() {
381     SkPicture* pic = nullptr;
382     {
383         SkString d;
384         d.printf("    {%d, \"%s\"},", fDirNo, fFilename);
385         SkString path = make_filepath(fDirNo, IN_DIR, fFilename);
386         SkFILEStream stream(path.c_str());
387         if (!stream.isValid()) {
388             SkDebugf("invalid stream %s\n", path.c_str());
389             goto finish;
390         }
391         if (fTestStep == kEncodeFiles) {
392             size_t length = stream.getLength();
393             SkTArray<char, true> bytes;
394             bytes.push_back_n(length);
395             stream.read(&bytes[0], length);
396             stream.rewind();
397             SkString wPath = make_filepath(0, outSkpDir, fFilename);
398             SkFILEWStream wStream(wPath.c_str());
399             wStream.write(&bytes[0], length);
400             wStream.flush();
401         }
402         pic = SkPicture::CreateFromStream(&stream);
403         if (!pic) {
404             SkDebugf("unable to decode %s\n", fFilename);
405             goto finish;
406         }
407         int pWidth = pic->width();
408         int pHeight = pic->height();
409         int pLargerWH = SkTMax(pWidth, pHeight);
410         GrContextFactory contextFactory;
411 #ifdef SK_BUILD_FOR_WIN
412         GrContext* context = contextFactory.get(kAngle);
413 #else
414         GrContext* context = contextFactory.get(kNative);
415 #endif
416         if (nullptr == context) {
417             SkDebugf("unable to allocate context for %s\n", fFilename);
418             goto finish;
419         }
420         int maxWH = context->getMaxRenderTargetSize();
421         int scale = 1;
422         while (pLargerWH / scale > maxWH) {
423             scale *= 2;
424         }
425         SkBitmap bitmap;
426         SkIPoint dim;
427         do {
428             dim.fX = (pWidth + scale - 1) / scale;
429             dim.fY = (pHeight + scale - 1) / scale;
430             bool success = bitmap.allocN32Pixels(dim.fX, dim.fY);
431             if (success) {
432                 break;
433             }
434             SkDebugf("-%d-", scale);
435         } while ((scale *= 2) < 256);
436         if (scale >= 256) {
437             SkDebugf("unable to allocate bitmap for %s (w=%d h=%d) (sw=%d sh=%d)\n",
438                     fFilename, pWidth, pHeight, dim.fX, dim.fY);
439             goto finish;
440         }
441         SkCanvas skCanvas(bitmap);
442         drawPict(pic, &skCanvas, fScaleOversized ? scale : 1);
443         GrTextureDesc desc;
444         desc.fConfig = kSkia8888_GrPixelConfig;
445         desc.fFlags = kRenderTarget_GrTextureFlagBit;
446         desc.fWidth = dim.fX;
447         desc.fHeight = dim.fY;
448         desc.fSampleCnt = 0;
449         SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, nullptr, 0));
450         if (!texture) {
451             SkDebugf("unable to allocate texture for %s (w=%d h=%d)\n", fFilename,
452                 dim.fX, dim.fY);
453             goto finish;
454         }
455         SkGpuDevice grDevice(context, texture.get());
456         SkCanvas grCanvas(&grDevice);
457         drawPict(pic, &grCanvas, fScaleOversized ? scale : 1);
458 
459         SkBitmap grBitmap;
460         grBitmap.allocPixels(grCanvas.imageInfo());
461         grCanvas.readPixels(&grBitmap, 0, 0);
462 
463         if (fTestStep == kCompareBits) {
464             fPixelError = similarBits(grBitmap, bitmap);
465             int skTime = timePict(pic, &skCanvas);
466             int grTime = timePict(pic, &grCanvas);
467             fTime = skTime - grTime;
468         } else if (fTestStep == kEncodeFiles) {
469             SkString pngStr = make_png_name(fFilename);
470             const char* pngName = pngStr.c_str();
471             writePict(grBitmap, outGrDir, pngName);
472             writePict(bitmap, outSkDir, pngName);
473         }
474     }
475 finish:
476     delete pic;
477 }
478 
makeStatusString(int dirNo)479 static SkString makeStatusString(int dirNo) {
480     SkString statName;
481     statName.printf("stats%d.txt", dirNo);
482     SkString statusFile = make_filepath(0, outStatusDir, statName.c_str());
483     return statusFile;
484 }
485 
486 class PreParser {
487 public:
PreParser(int dirNo)488     PreParser(int dirNo)
489         : fDirNo(dirNo)
490         , fIndex(0)
491         , fStatusPath(makeStatusString(dirNo)) {
492         if (!sk_exists(fStatusPath.c_str())) {
493             return;
494         }
495         SkFILEStream reader;
496         reader.setPath(fStatusPath.c_str());
497         while (fetch(reader, &fResults.push_back()))
498             ;
499         fResults.pop_back();
500     }
501 
fetch(SkFILEStream & reader,TestResult * result)502     bool fetch(SkFILEStream& reader, TestResult* result) {
503         char c;
504         int i = 0;
505         result->init(fDirNo);
506         result->fPixelError = 0;
507         result->fTime = 0;
508         do {
509             bool readOne = reader.read(&c, 1) != 0;
510             if (!readOne) {
511                 SkASSERT(i == 0);
512                 return false;
513             }
514             if (c == ' ') {
515                 result->fFilename[i++] = '\0';
516                 break;
517             }
518             result->fFilename[i++] = c;
519             SkASSERT(i < kMaxLength);
520         } while (true);
521         do {
522             SkAssertResult(reader.read(&c, 1) != 0);
523             if (c == ' ') {
524                 break;
525             }
526             SkASSERT(c >= '0' && c <= '9');
527             result->fPixelError = result->fPixelError * 10 + (c - '0');
528         } while (true);
529         bool minus = false;
530         do {
531             if (reader.read(&c, 1) == 0) {
532                 break;
533             }
534             if (c == '\r' && reader.read(&c, 1) == 0) {
535                 break;
536             }
537             if (c == '\n') {
538                 break;
539             }
540             if (c == '-') {
541                 minus = true;
542                 continue;
543             }
544             SkASSERT(c >= '0' && c <= '9');
545             result->fTime = result->fTime * 10 + (c - '0');
546         } while (true);
547         if (minus) {
548             result->fTime = -result->fTime;
549         }
550         return true;
551     }
552 
match(const SkString & filename,SkFILEWStream * stream,TestResult * result)553     bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) {
554         if (fIndex < fResults.count()) {
555             *result = fResults[fIndex++];
556             SkASSERT(filename.equals(result->fFilename));
557             SkString outStr(result->status());
558             stream->write(outStr.c_str(), outStr.size());
559             stream->flush();
560             return true;
561         }
562         return false;
563     }
564 
565 private:
566     int fDirNo;
567     int fIndex;
568     SkTArray<TestResult, true> fResults;
569     SkString fStatusPath;
570 };
571 
initTest()572 static bool initTest() {
573 #if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC
574     SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
575     SK_CONF_SET("images.png.suppressDecoderWarnings", true);
576 #endif
577     return make_out_dirs();
578 }
579 
DEF_TEST(SkpSkGr,reporter)580 DEF_TEST(SkpSkGr, reporter) {
581     SkTArray<TestResult, true> errors;
582     if (!initTest()) {
583         return;
584     }
585     SkpSkGrThreadState state;
586     state.init(0);
587     int smallCount = 0;
588     for (int dirNo = 1; dirNo <= 100; ++dirNo) {
589         SkString pictDir = make_in_dir_name(dirNo);
590         SkASSERT(pictDir.size());
591         if (reporter->verbose()) {
592             SkDebugf("dirNo=%d\n", dirNo);
593         }
594         SkOSFile::Iter iter(pictDir.c_str(), "skp");
595         SkString filename;
596         int testCount = 0;
597         PreParser preParser(dirNo);
598         SkFILEWStream statusStream(makeStatusString(dirNo).c_str());
599         while (iter.next(&filename)) {
600             for (size_t index = 0; index < skipOverSkGrCount; ++index) {
601                 if (skipOverSkGr[index].directory == dirNo
602                         && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) {
603                     goto skipOver;
604                 }
605             }
606             if (preParser.match(filename, &statusStream, &state.fResult)) {
607                 addError(&state);
608                 ++testCount;
609                 goto checkEarlyExit;
610             }
611             if (state.fSmallestError > 5000000) {
612                 goto breakOut;
613             }
614             {
615                 TestResult& result = state.fResult;
616                 result.test(dirNo, filename);
617                 SkString outStr(result.status());
618                 statusStream.write(outStr.c_str(), outStr.size());
619                 statusStream.flush();
620                 if (1) {
621                     SkDebugf("%s", outStr.c_str());
622                 }
623                 bool noMatch = addError(&state);
624                 if (noMatch) {
625                     smallCount = 0;
626                 } else if (++smallCount > 10000) {
627                     goto breakOut;
628                 }
629             }
630             ++testCount;
631             if (reporter->verbose()) {
632                 if (testCount % 100 == 0) {
633                     SkDebugf("#%d\n", testCount);
634                 }
635             }
636      skipOver:
637              reporter->bumpTestCount();
638     checkEarlyExit:
639             if (1 && testCount == 20) {
640                 break;
641             }
642         }
643     }
644 breakOut:
645     if (reporter->verbose()) {
646         for (int index = 0; index < state.fFoundCount; ++index) {
647             SkDebugf("%d %s %d\n", state.fDirsFound[index], state.fFilesFound[index],
648                      state.fError[index]);
649         }
650     }
651     for (int index = 0; index < state.fFoundCount; ++index) {
652         TestResult::Test(state.fDirsFound[index], state.fFilesFound[index], kEncodeFiles,
653                 reporter->verbose());
654         if (reporter->verbose()) SkDebugf("+");
655     }
656 }
657 
bumpCount(skiatest::Reporter * reporter,bool skipping)658 static void bumpCount(skiatest::Reporter* reporter, bool skipping) {
659     if (reporter->verbose()) {
660         static int threadTestCount;
661         sk_atomic_inc(&threadTestCount);
662         if (!skipping && threadTestCount % 100 == 0) {
663             SkDebugf("#%d\n", threadTestCount);
664         }
665         if (skipping && threadTestCount % 10000 == 0) {
666             SkDebugf("#%d\n", threadTestCount);
667         }
668     }
669 }
670 
testSkGrMain(SkpSkGrThreadState * data)671 static void testSkGrMain(SkpSkGrThreadState* data) {
672     data->fResult.testOne();
673     bumpCount(data->fReporter, false);
674     data->fReporter->bumpTestCount();
675 }
676 
DEF_TEST(SkpSkGrThreaded,reporter)677 DEF_TEST(SkpSkGrThreaded, reporter) {
678     if (!initTest()) {
679         return;
680     }
681     SkpSkGrThreadedTestRunner testRunner(reporter);
682     for (int dirIndex = 1; dirIndex <= 100; ++dirIndex) {
683         SkString pictDir = make_in_dir_name(dirIndex);
684         if (pictDir.size() == 0) {
685             continue;
686         }
687         SkOSFile::Iter iter(pictDir.c_str(), "skp");
688         SkString filename;
689         while (iter.next(&filename)) {
690             SkString pngName = make_png_name(filename.c_str());
691             SkString oldPng = make_filepath(dirIndex, outSkDir, pngName.c_str());
692             SkString newPng = make_filepath(dirIndex, outGrDir, pngName.c_str());
693             if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) {
694                 bumpCount(reporter, true);
695                 continue;
696             }
697             for (size_t index = 0; index < skipOverSkGrCount; ++index) {
698                 if (skipOverSkGr[index].directory == dirIndex
699                         && strcmp(filename.c_str(), skipOverSkGr[index].filename) == 0) {
700                     bumpCount(reporter, true);
701                     goto skipOver;
702                 }
703             }
704             *testRunner.fRunnables.append() = new SkpSkGrThreadedRunnable(
705                     &testSkGrMain, dirIndex, filename.c_str(), &testRunner);
706     skipOver:
707             ;
708         }
709     }
710     testRunner.render();
711     SkpSkGrThreadState& max = testRunner.fRunnables[0]->fState;
712     for (int dirIndex = 2; dirIndex <= 100; ++dirIndex) {
713         SkpSkGrThreadState& state = testRunner.fRunnables[dirIndex - 1]->fState;
714         for (int index = 0; index < state.fFoundCount; ++index) {
715             int maxIdx = max.fFoundCount;
716             if (maxIdx < kMaxFiles) {
717                 max.fError[maxIdx] = state.fError[index];
718                 strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]);
719                 max.fDirsFound[maxIdx] = state.fDirsFound[index];
720                 ++max.fFoundCount;
721                 continue;
722             }
723             for (maxIdx = 0; maxIdx < max.fFoundCount; ++maxIdx) {
724                 if (max.fError[maxIdx] < state.fError[index]) {
725                     max.fError[maxIdx] = state.fError[index];
726                     strcpy(max.fFilesFound[maxIdx], state.fFilesFound[index]);
727                     max.fDirsFound[maxIdx] = state.fDirsFound[index];
728                     break;
729                 }
730             }
731         }
732     }
733     TestResult encoder;
734     encoder.fTestStep = kEncodeFiles;
735     for (int index = 0; index < max.fFoundCount; ++index) {
736         encoder.fDirNo = max.fDirsFound[index];
737         strcpy(encoder.fFilename, max.fFilesFound[index]);
738         encoder.testOne();
739         SkDebugf("+");
740     }
741 }
742 
DEF_TEST(SkpSkGrOneOff,reporter)743 DEF_TEST(SkpSkGrOneOff, reporter) {
744     if (!initTest()) {
745         return;
746     }
747     int testIndex = 166;
748     int dirIndex = skipOverSkGr[testIndex - 166].directory;
749     SkString pictDir = make_in_dir_name(dirIndex);
750     if (pictDir.size() == 0) {
751         return;
752     }
753     SkString filename(skipOverSkGr[testIndex - 166].filename);
754     TestResult::Test(dirIndex, filename.c_str(), kCompareBits, reporter->verbose());
755     TestResult::Test(dirIndex, filename.c_str(), kEncodeFiles, reporter->verbose());
756 }
757