benchmain.cpp revision ed5eb4ef2aa1d6c705bc3ed466f9caba2a230a2b
1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "BenchTimer.h"
11
12#if SK_SUPPORT_GPU
13#include "GrContext.h"
14#include "GrContextFactory.h"
15#include "gl/GrGLDefines.h"
16#include "GrRenderTarget.h"
17#include "SkGpuDevice.h"
18#else
19class GrContext;
20#endif // SK_SUPPORT_GPU
21
22#include "SkBenchLogger.h"
23#include "SkBenchmark.h"
24#include "SkCanvas.h"
25#include "SkDeferredCanvas.h"
26#include "SkDevice.h"
27#include "SkColorPriv.h"
28#include "SkGraphics.h"
29#include "SkImageEncoder.h"
30#include "SkNWayCanvas.h"
31#include "SkPicture.h"
32#include "SkString.h"
33#include "SkTArray.h"
34#include "TimerData.h"
35
36enum benchModes {
37    kNormal_benchModes,
38    kDeferred_benchModes,
39    kDeferredSilent_benchModes,
40    kRecord_benchModes,
41    kPictureRecord_benchModes
42};
43
44///////////////////////////////////////////////////////////////////////////////
45
46static void erase(SkBitmap& bm) {
47    if (bm.config() == SkBitmap::kA8_Config) {
48        bm.eraseColor(SK_ColorTRANSPARENT);
49    } else {
50        bm.eraseColor(SK_ColorWHITE);
51    }
52}
53
54#if 0
55static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
56    if (bm1.width() != bm2.width() ||
57        bm1.height() != bm2.height() ||
58        bm1.config() != bm2.config()) {
59        return false;
60    }
61
62    size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
63    for (int y = 0; y < bm1.height(); y++) {
64        if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
65            return false;
66        }
67    }
68    return true;
69}
70#endif
71
72class Iter {
73public:
74    Iter(void* param) {
75        fBench = BenchRegistry::Head();
76        fParam = param;
77    }
78
79    SkBenchmark* next() {
80        if (fBench) {
81            BenchRegistry::Factory f = fBench->factory();
82            fBench = fBench->next();
83            return f(fParam);
84        }
85        return NULL;
86    }
87
88private:
89    const BenchRegistry* fBench;
90    void* fParam;
91};
92
93class AutoPrePostDraw {
94public:
95    AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) {
96        fBench->preDraw();
97    }
98    ~AutoPrePostDraw() {
99        fBench->postDraw();
100    }
101private:
102    SkBenchmark* fBench;
103};
104
105static void make_filename(const char name[], SkString* path) {
106    path->set(name);
107    for (int i = 0; name[i]; i++) {
108        switch (name[i]) {
109            case '/':
110            case '\\':
111            case ' ':
112            case ':':
113                path->writable_str()[i] = '-';
114                break;
115            default:
116                break;
117        }
118    }
119}
120
121static void saveFile(const char name[], const char config[], const char dir[],
122                     const SkBitmap& bm) {
123    SkBitmap copy;
124    if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
125        return;
126    }
127
128    if (bm.config() == SkBitmap::kA8_Config) {
129        // turn alpha into gray-scale
130        size_t size = copy.getSize() >> 2;
131        SkPMColor* p = copy.getAddr32(0, 0);
132        for (size_t i = 0; i < size; i++) {
133            int c = (*p >> SK_A32_SHIFT) & 0xFF;
134            c = 255 - c;
135            c |= (c << 24) | (c << 16) | (c << 8);
136            *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
137        }
138    }
139
140    SkString str;
141    make_filename(name, &str);
142    str.appendf("_%s.png", config);
143    str.prepend(dir);
144    ::remove(str.c_str());
145    SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
146                               100);
147}
148
149static void performClip(SkCanvas* canvas, int w, int h) {
150    SkRect r;
151
152    r.set(SkIntToScalar(10), SkIntToScalar(10),
153          SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
154    canvas->clipRect(r, SkRegion::kIntersect_Op);
155
156    r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
157          SkIntToScalar(w-10), SkIntToScalar(h-10));
158    canvas->clipRect(r, SkRegion::kXOR_Op);
159}
160
161static void performRotate(SkCanvas* canvas, int w, int h) {
162    const SkScalar x = SkIntToScalar(w) / 2;
163    const SkScalar y = SkIntToScalar(h) / 2;
164
165    canvas->translate(x, y);
166    canvas->rotate(SkIntToScalar(35));
167    canvas->translate(-x, -y);
168}
169
170static void performScale(SkCanvas* canvas, int w, int h) {
171    const SkScalar x = SkIntToScalar(w) / 2;
172    const SkScalar y = SkIntToScalar(h) / 2;
173
174    canvas->translate(x, y);
175    // just enough so we can't take the sprite case
176    canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
177    canvas->translate(-x, -y);
178}
179
180static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
181    if (argv < stop) {
182        *var = atoi(*argv) != 0;
183        return true;
184    }
185    return false;
186}
187
188enum Backend {
189    kNonRendering_Backend,
190    kRaster_Backend,
191    kGPU_Backend,
192    kPDF_Backend,
193};
194
195static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
196                             Backend backend, int sampleCount, GrContext* context) {
197    SkDevice* device = NULL;
198    SkBitmap bitmap;
199    bitmap.setConfig(config, size.fX, size.fY);
200
201    switch (backend) {
202        case kRaster_Backend:
203            bitmap.allocPixels();
204            erase(bitmap);
205            device = SkNEW_ARGS(SkDevice, (bitmap));
206            break;
207#if SK_SUPPORT_GPU
208        case kGPU_Backend: {
209            GrTextureDesc desc;
210            desc.fConfig = kSkia8888_GrPixelConfig;
211            desc.fFlags = kRenderTarget_GrTextureFlagBit;
212            desc.fWidth = size.fX;
213            desc.fHeight = size.fY;
214            desc.fSampleCnt = sampleCount;
215            SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
216            if (!texture) {
217                return NULL;
218            }
219            device = SkNEW_ARGS(SkGpuDevice, (context, texture.get()));
220            break;
221        }
222#endif
223        case kPDF_Backend:
224        default:
225            SkASSERT(!"unsupported");
226    }
227    return device;
228}
229
230#if SK_SUPPORT_GPU
231GrContextFactory gContextFactory;
232typedef GrContextFactory::GLContextType GLContextType;
233static const GLContextType kDontCareGLCtxType = GrContextFactory::kNative_GLContextType;
234#else
235typedef int GLContextType;
236static const GLContextType kDontCareGLCtxType = 0;
237#endif
238
239static const struct {
240    SkBitmap::Config    fConfig;
241    const char*         fName;
242    int                 fSampleCnt;
243    Backend             fBackend;
244    GLContextType       fContextType;
245    bool                fRunByDefault;
246} gConfigs[] = {
247    { SkBitmap::kNo_Config,         "NONRENDERING", 0, kNonRendering_Backend, kDontCareGLCtxType,                      true     },
248    { SkBitmap::kARGB_8888_Config,  "8888",         0, kRaster_Backend,       kDontCareGLCtxType,                      true     },
249    { SkBitmap::kRGB_565_Config,    "565",          0, kRaster_Backend,       kDontCareGLCtxType,                      true     },
250#if SK_SUPPORT_GPU
251    { SkBitmap::kARGB_8888_Config,  "GPU",          0, kGPU_Backend,          GrContextFactory::kNative_GLContextType, true     },
252    { SkBitmap::kARGB_8888_Config,  "MSAA4",        4, kGPU_Backend,          GrContextFactory::kNative_GLContextType, false    },
253    { SkBitmap::kARGB_8888_Config,  "MSAA16",      16, kGPU_Backend,          GrContextFactory::kNative_GLContextType, false    },
254#if SK_ANGLE
255    { SkBitmap::kARGB_8888_Config,  "ANGLE",        0, kGPU_Backend,          GrContextFactory::kANGLE_GLContextType,  true     },
256#endif // SK_ANGLE
257#ifdef SK_DEBUG
258    { SkBitmap::kARGB_8888_Config,  "Debug",        0, kGPU_Backend,          GrContextFactory::kDebug_GLContextType,  GR_DEBUG },
259#endif // SK_DEBUG
260    { SkBitmap::kARGB_8888_Config,  "NULLGPU",      0, kGPU_Backend,          GrContextFactory::kNull_GLContextType,   true     },
261#endif // SK_SUPPORT_GPU
262};
263
264static int findConfig(const char config[]) {
265    for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
266        if (!strcmp(config, gConfigs[i].fName)) {
267            return i;
268        }
269    }
270    return -1;
271}
272
273static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
274    // FIXME: this duplicates the logic in skia_test.cpp, gmmain.cpp -- consolidate
275    int count = array.count();
276    size_t testLen = strlen(name);
277    bool anyExclude = count == 0;
278    for (int i = 0; i < array.count(); ++i) {
279        const char* matchName = array[i];
280        size_t matchLen = strlen(matchName);
281        bool matchExclude, matchStart, matchEnd;
282        if ((matchExclude = matchName[0] == '~')) {
283            anyExclude = true;
284            matchName++;
285            matchLen--;
286        }
287        if ((matchStart = matchName[0] == '^')) {
288            matchName++;
289            matchLen--;
290        }
291        if ((matchEnd = matchName[matchLen - 1] == '$')) {
292            matchLen--;
293        }
294        if (matchStart ? (!matchEnd || matchLen == testLen)
295                && strncmp(name, matchName, matchLen) == 0
296                : matchEnd ? matchLen <= testLen
297                && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
298                : strstr(name, matchName) != 0) {
299            return matchExclude;
300        }
301    }
302    return !anyExclude;
303}
304
305static void help() {
306    SkString configsStr;
307    static const size_t kConfigCount = SK_ARRAY_COUNT(gConfigs);
308    for (size_t i = 0; i < kConfigCount; ++i) {
309        configsStr.appendf("%s%s", gConfigs[i].fName, ((i == kConfigCount - 1) ? "" : "|"));
310    }
311
312    SkDebugf("Usage: bench [-o outDir] [--repeat nr] [--logPerIter] "
313                          "[--timers [wcgWC]*] [--rotate]\n"
314             "    [--scale] [--clip] [--min] [--forceAA 1|0] [--forceFilter 1|0]\n"
315             "    [--forceDither 1|0] [--forceBlend 1|0]"
316#if SK_SUPPORT_GPU
317             " [--gpuCacheSize <bytes> <count>]"
318#endif
319             "\n"
320             "    [--strokeWidth width] [--match name]\n"
321             "    [--mode normal|deferred|deferredSilent|record|picturerecord]\n"
322             "    [--config ");
323    SkDebugf("%s]\n", configsStr.c_str());
324    SkDebugf("    [-Dfoo bar] [--logFile filename] [-h|--help]");
325    SkDebugf("\n\n");
326    SkDebugf("    -o outDir : Image of each bench will be put in outDir.\n");
327    SkDebugf("    --repeat nr : Each bench repeats for nr times.\n");
328    SkDebugf("    --logPerIter : "
329             "Log each repeat timer instead of mean, default is disabled.\n");
330    SkDebugf("    --timers [wcgWC]* : "
331             "Display wall, cpu, gpu, truncated wall or truncated cpu time for each bench.\n");
332    SkDebugf("    --rotate : Rotate before each bench runs.\n");
333    SkDebugf("    --scale : Scale before each bench runs.\n");
334    SkDebugf("    --clip : Clip before each bench runs.\n");
335    SkDebugf("    --min : Print the minimum times (instead of average).\n");
336    SkDebugf("    --forceAA 1|0 : "
337             "Enable/disable anti-aliased, default is enabled.\n");
338    SkDebugf("    --forceFilter 1|0 : "
339             "Enable/disable bitmap filtering, default is disabled.\n");
340    SkDebugf("    --forceDither 1|0 : "
341             "Enable/disable dithering, default is disabled.\n");
342    SkDebugf("    --forceBlend 1|0 : "
343             "Enable/disable dithering, default is disabled.\n");
344#if SK_SUPPORT_GPU
345    SkDebugf("    --gpuCacheSize <bytes> <count>: "
346             "limits gpu cache to  bytes size or object count.\n");
347    SkDebugf("      -1 for either value means use the default. 0 for either disables the cache.\n");
348#endif
349    SkDebugf("    --strokeWidth width : The width for path stroke.\n");
350    SkDebugf("    --match [~][^]substring[$] [...] of test name to run.\n"
351             "             Multiple matches may be separated by spaces.\n"
352             "             ~ causes a matching test to always be skipped\n"
353             "             ^ requires the start of the test to match\n"
354             "             $ requires the end of the test to match\n"
355             "             ^ and $ requires an exact match\n"
356             "             If a test does not match any list entry,\n"
357             "             it is skipped unless some list entry starts with ~\n");
358    SkDebugf("    --mode normal|deferred|deferredSilent|record|picturerecord :\n"
359             "             Run in the corresponding mode\n"
360             "                 normal, Use a normal canvas to draw to;\n"
361             "                 deferred, Use a deferrred canvas when drawing;\n"
362             "                 deferredSilent, deferred with silent playback;\n"
363             "                 record, Benchmark the time to record to an SkPicture;\n"
364             "                 picturerecord, Benchmark the time to do record from a \n"
365             "                                SkPicture to a SkPicture.\n");
366    SkDebugf("    --logFile filename : destination for writing log output, in addition to stdout.\n");
367    SkDebugf("    --config %s:\n", configsStr.c_str());
368    SkDebugf("             Run bench in corresponding config mode.\n");
369    SkDebugf("    -Dfoo bar : Add extra definition to bench.\n");
370    SkDebugf("    -h|--help : Show this help message.\n");
371}
372
373int tool_main(int argc, char** argv);
374int tool_main(int argc, char** argv) {
375#if SK_ENABLE_INST_COUNT
376    gPrintInstCount = true;
377#endif
378    SkAutoGraphics ag;
379
380    SkTDict<const char*> defineDict(1024);
381    int repeatDraw = 1;
382    bool logPerIter = false;
383    int forceAlpha = 0xFF;
384    bool forceAA = true;
385    bool forceFilter = false;
386    SkTriState::State forceDither = SkTriState::kDefault;
387    bool timerWall = false;
388    bool truncatedTimerWall = false;
389    bool timerCpu = true;
390    bool truncatedTimerCpu = false;
391    bool timerGpu = true;
392    bool doScale = false;
393    bool doRotate = false;
394    bool doClip = false;
395    bool printMin = false;
396    bool hasStrokeWidth = false;
397
398#if SK_SUPPORT_GPU
399    struct {
400        int     fBytes;
401        int     fCount;
402    } gpuCacheSize = { -1, -1 }; // -1s mean use the default
403#endif
404
405    float strokeWidth;
406    SkTDArray<const char*> fMatches;
407    benchModes benchMode = kNormal_benchModes;
408    SkString perIterTimeformat("%.2f");
409    SkString normalTimeFormat("%6.2f");
410
411    SkString outDir;
412    SkBitmap::Config outConfig = SkBitmap::kNo_Config;
413    const char* configName = "";
414    Backend backend = kRaster_Backend;  // for warning
415    int sampleCount = 0;
416    SkTDArray<int> configs;
417    bool userConfig = false;
418
419    SkBenchLogger logger;
420
421    char* const* stop = argv + argc;
422    for (++argv; argv < stop; ++argv) {
423        if (strcmp(*argv, "-o") == 0) {
424            argv++;
425            if (argv < stop && **argv) {
426                outDir.set(*argv);
427                if (outDir.c_str()[outDir.size() - 1] != '/') {
428                    outDir.append("/");
429                }
430            }
431        } else if (strcmp(*argv, "--repeat") == 0) {
432            argv++;
433            if (argv < stop) {
434                repeatDraw = atoi(*argv);
435                if (repeatDraw < 1) {
436                    repeatDraw = 1;
437                }
438            } else {
439                logger.logError("missing arg for --repeat\n");
440                help();
441                return -1;
442            }
443        } else if (strcmp(*argv, "--logPerIter") == 0) {
444            logPerIter = true;
445        } else if (strcmp(*argv, "--timers") == 0) {
446            argv++;
447            if (argv < stop) {
448                timerWall = false;
449                truncatedTimerWall = false;
450                timerCpu = false;
451                truncatedTimerCpu = false;
452                timerGpu = false;
453                for (char* t = *argv; *t; ++t) {
454                    switch (*t) {
455                    case 'w': timerWall = true; break;
456                    case 'c': timerCpu = true; break;
457                    case 'W': truncatedTimerWall = true; break;
458                    case 'C': truncatedTimerCpu = true; break;
459                    case 'g': timerGpu = true; break;
460                    }
461                }
462            } else {
463                logger.logError("missing arg for --timers\n");
464                help();
465                return -1;
466            }
467        } else if (!strcmp(*argv, "--rotate")) {
468            doRotate = true;
469        } else if (!strcmp(*argv, "--scale")) {
470            doScale = true;
471        } else if (!strcmp(*argv, "--clip")) {
472            doClip = true;
473        } else if (!strcmp(*argv, "--min")) {
474            printMin = true;
475        } else if (strcmp(*argv, "--forceAA") == 0) {
476            if (!parse_bool_arg(++argv, stop, &forceAA)) {
477                logger.logError("missing arg for --forceAA\n");
478                help();
479                return -1;
480            }
481        } else if (strcmp(*argv, "--forceFilter") == 0) {
482            if (!parse_bool_arg(++argv, stop, &forceFilter)) {
483                logger.logError("missing arg for --forceFilter\n");
484                help();
485                return -1;
486            }
487        } else if (strcmp(*argv, "--forceDither") == 0) {
488            bool tmp;
489            if (!parse_bool_arg(++argv, stop, &tmp)) {
490                logger.logError("missing arg for --forceDither\n");
491                help();
492                return -1;
493            }
494            forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
495        } else if (strcmp(*argv, "--forceBlend") == 0) {
496            bool wantAlpha = false;
497            if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
498                logger.logError("missing arg for --forceBlend\n");
499                help();
500                return -1;
501            }
502            forceAlpha = wantAlpha ? 0x80 : 0xFF;
503#if SK_SUPPORT_GPU
504        } else if (strcmp(*argv, "--gpuCacheSize") == 0) {
505            if (stop - argv > 2) {
506                gpuCacheSize.fBytes = atoi(*++argv);
507                gpuCacheSize.fCount = atoi(*++argv);
508            } else {
509                SkDebugf("missing arg for --gpuCacheSize\n");
510                help();
511                return -1;
512            }
513#endif
514        } else if (strcmp(*argv, "--mode") == 0) {
515            argv++;
516            if (argv < stop) {
517                if (strcmp(*argv, "normal") == 0) {
518                    benchMode = kNormal_benchModes;
519                } else if (strcmp(*argv, "deferred") == 0) {
520                    benchMode = kDeferred_benchModes;
521                } else if (strcmp(*argv, "deferredSilent") == 0) {
522                    benchMode = kDeferredSilent_benchModes;
523                } else if (strcmp(*argv, "record") == 0) {
524                    benchMode = kRecord_benchModes;
525                } else if (strcmp(*argv, "picturerecord") == 0) {
526                    benchMode = kPictureRecord_benchModes;
527                } else {
528                    logger.logError("bad arg for --mode\n");
529                    help();
530                    return -1;
531                }
532            } else {
533                logger.logError("missing arg for --mode\n");
534                help();
535                return -1;
536            }
537        } else if (strcmp(*argv, "--strokeWidth") == 0) {
538            argv++;
539            if (argv < stop) {
540                const char *strokeWidthStr = *argv;
541                if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
542                  logger.logError("bad arg for --strokeWidth\n");
543                  help();
544                  return -1;
545                }
546                hasStrokeWidth = true;
547            } else {
548                logger.logError("missing arg for --strokeWidth\n");
549                help();
550                return -1;
551            }
552        } else if (strcmp(*argv, "--match") == 0) {
553            argv++;
554            while (argv < stop && (*argv)[0] != '-') {
555                *fMatches.append() = *argv++;
556            }
557            argv--;
558            if (!fMatches.count()) {
559                logger.logError("missing arg for --match\n");
560                help();
561                return -1;
562            }
563        } else if (strcmp(*argv, "--config") == 0) {
564            argv++;
565            if (argv < stop) {
566                int index = findConfig(*argv);
567                if (index >= 0) {
568                    *configs.append() = index;
569                    userConfig = true;
570                } else {
571                    SkString str;
572                    str.printf("unrecognized config %s\n", *argv);
573                    logger.logError(str);
574                    help();
575                    return -1;
576                }
577            } else {
578                logger.logError("missing arg for --config\n");
579                help();
580                return -1;
581            }
582        } else if (strcmp(*argv, "--logFile") == 0) {
583            argv++;
584            if (argv < stop) {
585                if (!logger.SetLogFile(*argv)) {
586                    SkString str;
587                    str.printf("Could not open %s for writing.", *argv);
588                    logger.logError(str);
589                    return -1;
590                }
591            } else {
592                logger.logError("missing arg for --logFile\n");
593                help();
594                return -1;
595            }
596        } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
597            argv++;
598            if (argv < stop) {
599                defineDict.set(argv[-1] + 2, *argv);
600            } else {
601                logger.logError("incomplete '-Dfoo bar' definition\n");
602                help();
603                return -1;
604            }
605        } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
606            help();
607            return 0;
608        } else {
609            SkString str;
610            str.printf("unrecognized arg %s\n", *argv);
611            logger.logError(str);
612            help();
613            return -1;
614        }
615    }
616    if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)
617            && !outDir.isEmpty()) {
618        logger.logError("'--mode record' and '--mode picturerecord' are not"
619                  " compatible with -o.\n");
620        return -1;
621    }
622    if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
623        perIterTimeformat.set("%.4f");
624        normalTimeFormat.set("%6.4f");
625    }
626    if (!userConfig) {
627        // if no config is specified by user, add the default configs
628        for (unsigned int i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
629            if (gConfigs[i].fRunByDefault) {
630                *configs.append() = i;
631            }
632        }
633    }
634    if (kNormal_benchModes != benchMode) {
635        // Non-rendering configs only run in normal mode
636        for (int i = 0; i < configs.count(); ++i) {
637            int configIdx = configs[i];
638            if (kNonRendering_Backend == gConfigs[configIdx].fBackend) {
639                configs.remove(i, 1);
640                --i;
641            }
642        }
643    }
644
645#if SK_SUPPORT_GPU
646    for (int i = 0; i < configs.count(); ++i) {
647        int configIdx = configs[i];
648
649        if (kGPU_Backend == gConfigs[configIdx].fBackend && gConfigs[configIdx].fSampleCnt > 0) {
650            GrContext* context = gContextFactory.get(gConfigs[configIdx].fContextType);
651            if (NULL == context) {
652                SkString error;
653                error.printf("Error creating GrContext for config %s. Config will be skipped.\n",
654                             gConfigs[configIdx].fName);
655                logger.logError(error.c_str());
656                configs.remove(i);
657                --i;
658                continue;
659            }
660            if (gConfigs[configIdx].fSampleCnt > context->getMaxSampleCount()){
661                SkString error;
662                error.printf("Sample count (%d) for config %s is unsupported. "
663                             "Config will be skipped.\n",
664                             gConfigs[configIdx].fSampleCnt, gConfigs[configIdx].fName);
665                logger.logError(error.c_str());
666                configs.remove(i);
667                --i;
668                continue;
669            }
670        }
671    }
672#endif
673
674    // report our current settings
675    {
676        SkString str;
677        const char* deferredMode = benchMode == kDeferred_benchModes ? "yes" :
678            (benchMode == kDeferredSilent_benchModes ? "silent" : "no");
679        str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d "
680                   "deferred=%s logperiter=%d",
681                   forceAlpha, forceAA, forceFilter, deferredMode,
682                   logPerIter);
683        str.appendf(" rotate=%d scale=%d clip=%d min=%d",
684                   doRotate, doScale, doClip, printMin);
685        str.appendf(" record=%d picturerecord=%d",
686                    benchMode == kRecord_benchModes,
687                    benchMode == kPictureRecord_benchModes);
688        const char * ditherName;
689        switch (forceDither) {
690            case SkTriState::kDefault: ditherName = "default"; break;
691            case SkTriState::kTrue: ditherName = "true"; break;
692            case SkTriState::kFalse: ditherName = "false"; break;
693            default: ditherName = "<invalid>"; break;
694        }
695        str.appendf(" dither=%s", ditherName);
696
697        if (hasStrokeWidth) {
698            str.appendf(" strokeWidth=%f", strokeWidth);
699        } else {
700            str.append(" strokeWidth=none");
701        }
702
703#if defined(SK_SCALAR_IS_FLOAT)
704        str.append(" scalar=float");
705#elif defined(SK_SCALAR_IS_FIXED)
706        str.append(" scalar=fixed");
707#endif
708
709#if defined(SK_BUILD_FOR_WIN32)
710        str.append(" system=WIN32");
711#elif defined(SK_BUILD_FOR_MAC)
712        str.append(" system=MAC");
713#elif defined(SK_BUILD_FOR_ANDROID)
714        str.append(" system=ANDROID");
715#elif defined(SK_BUILD_FOR_UNIX)
716        str.append(" system=UNIX");
717#else
718        str.append(" system=other");
719#endif
720
721#if defined(SK_DEBUG)
722        str.append(" DEBUG");
723#endif
724        str.append("\n");
725        logger.logProgress(str);
726    }
727
728    SkTArray<BenchTimer*> timers(SK_ARRAY_COUNT(gConfigs));
729    for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
730#if SK_SUPPORT_GPU
731        SkGLContextHelper* glCtx = NULL;
732        if (kGPU_Backend == gConfigs[i].fBackend) {
733            GrContext* context = gContextFactory.get(gConfigs[i].fContextType);
734            if (NULL != context) {
735                // Set the user specified cache limits if non-default.
736                size_t bytes;
737                int count;
738                context->getTextureCacheLimits(&count, &bytes);
739                if (-1 != gpuCacheSize.fBytes) {
740                    bytes = static_cast<size_t>(gpuCacheSize.fBytes);
741                }
742                if (-1 != gpuCacheSize.fCount) {
743                    count = gpuCacheSize.fCount;
744                }
745                context->setTextureCacheLimits(count, bytes);
746            }
747            glCtx = gContextFactory.getGLContext(gConfigs[i].fContextType);
748        }
749        timers.push_back(SkNEW_ARGS(BenchTimer, (glCtx)));
750#else
751        timers.push_back(SkNEW(BenchTimer));
752#endif
753    }
754
755    Iter iter(&defineDict);
756    SkBenchmark* bench;
757    while ((bench = iter.next()) != NULL) {
758        SkAutoTUnref<SkBenchmark> benchUnref(bench);
759
760        SkIPoint dim = bench->getSize();
761        if (dim.fX <= 0 || dim.fY <= 0) {
762            continue;
763        }
764
765        bench->setForceAlpha(forceAlpha);
766        bench->setForceAA(forceAA);
767        bench->setForceFilter(forceFilter);
768        bench->setDither(forceDither);
769        if (hasStrokeWidth) {
770            bench->setStrokeWidth(strokeWidth);
771        }
772
773        // only run benchmarks if their name contains matchStr
774        if (skip_name(fMatches, bench->getName())) {
775            continue;
776        }
777
778        bool loggedBenchStart = false;
779
780        AutoPrePostDraw appd(bench);
781
782        for (int x = 0; x < configs.count(); ++x) {
783            int configIndex = configs[x];
784
785            bool setupFailed = false;
786
787            if (kNonRendering_Backend == gConfigs[configIndex].fBackend) {
788                if (bench->isRendering()) {
789                    continue;
790                }
791            } else {
792                if (!bench->isRendering()) {
793                    continue;
794                }
795            }
796
797            outConfig = gConfigs[configIndex].fConfig;
798            configName = gConfigs[configIndex].fName;
799            backend = gConfigs[configIndex].fBackend;
800            sampleCount = gConfigs[configIndex].fSampleCnt;
801            GrContext* context = NULL;
802            BenchTimer* timer = timers[configIndex];
803
804#if SK_SUPPORT_GPU
805            SkGLContextHelper* glContext = NULL;
806            if (kGPU_Backend == backend) {
807                context = gContextFactory.get(gConfigs[configIndex].fContextType);
808                if (NULL == context) {
809                    continue;
810                }
811                glContext = gContextFactory.getGLContext(gConfigs[configIndex].fContextType);
812            }
813#endif
814            SkDevice* device = NULL;
815            SkCanvas* canvas = NULL;
816            SkPicture pictureRecordFrom;
817            SkPicture pictureRecordTo;
818
819            if (kNonRendering_Backend != backend) {
820                device = make_device(outConfig, dim, backend, sampleCount, context);
821                if (NULL == device) {
822                    SkString error;
823                    error.printf("Device creation failure for config %s. Will skip.\n", configName);
824                    logger.logError(error.c_str());
825                    setupFailed = true;
826                } else {
827                    switch(benchMode) {
828                        case kDeferredSilent_benchModes:
829                        case kDeferred_benchModes:
830                            canvas =
831#if SK_DEFERRED_CANVAS_USES_FACTORIES
832                                SkDeferredCanvas::Create(device);
833#else
834                                SkNEW_ARGS(SkDeferredCanvas, (device));
835#endif
836                            break;
837                        case kRecord_benchModes:
838                            canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
839                                SkPicture::kUsePathBoundsForClip_RecordingFlag);
840                            canvas->ref();
841                            break;
842                        case kPictureRecord_benchModes: {
843                            // This sets up picture-to-picture recording.
844                            // The C++ drawing calls for the benchmark are recorded into
845                            // pictureRecordFrom. As the benchmark, we will time how
846                            // long it takes to playback pictureRecordFrom into
847                            // pictureRecordTo.
848                            SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY,
849                                SkPicture::kUsePathBoundsForClip_RecordingFlag);
850                            bench->draw(tempCanvas);
851                            pictureRecordFrom.endRecording();
852                            canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
853                                SkPicture::kUsePathBoundsForClip_RecordingFlag);
854                            canvas->ref();
855                            break;
856                        }
857                        case kNormal_benchModes:
858                            canvas = new SkCanvas(device);
859                            break;
860                        default:
861                            SkASSERT(0);
862                    }
863                    device->unref();
864                    canvas->clear(SK_ColorWHITE);
865                }
866            }
867            SkAutoUnref canvasUnref(canvas);
868            if (!setupFailed) {
869                if (NULL != canvas) {
870                    if (doClip) {
871                        performClip(canvas, dim.fX, dim.fY);
872                    }
873                    if (doScale) {
874                        performScale(canvas, dim.fX, dim.fY);
875                    }
876                    if (doRotate) {
877                        performRotate(canvas, dim.fX, dim.fY);
878                    }
879                }
880
881                if (!loggedBenchStart) {
882                    loggedBenchStart = true;
883                    SkString str;
884                    str.printf("running bench [%d %d] %28s", dim.fX, dim.fY, bench->getName());
885                    logger.logProgress(str);
886                }
887
888                // warm up caches if needed
889                if (repeatDraw > 1 && NULL != canvas) {
890#if SK_SUPPORT_GPU
891                    // purge the GPU resources to reduce variance
892                    if (NULL != context) {
893                        context->freeGpuResources();
894                    }
895#endif
896                    SkAutoCanvasRestore acr(canvas, true);
897                    if (benchMode == kPictureRecord_benchModes) {
898                        pictureRecordFrom.draw(canvas);
899                    } else {
900                        bench->draw(canvas);
901                    }
902
903                    if (kDeferredSilent_benchModes == benchMode) {
904                        static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
905                    } else {
906                        canvas->flush();
907                    }
908#if SK_SUPPORT_GPU
909                    if (NULL != context) {
910                        context->flush();
911                        SK_GL(*glContext, Finish());
912                    }
913#endif
914                }
915
916                // record timer values for each repeat, and their sum
917                TimerData timerData(perIterTimeformat, normalTimeFormat);
918                for (int i = 0; i < repeatDraw; i++) {
919                    if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
920                        // This will clear the recorded commands so that they do not
921                        // accumulate.
922                        canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
923                            SkPicture::kUsePathBoundsForClip_RecordingFlag);
924                    }
925
926                    timer->start(bench->getDurationScale());
927                    if (NULL != canvas) {
928                        canvas->save();
929                    }
930                    if (benchMode == kPictureRecord_benchModes) {
931                        pictureRecordFrom.draw(canvas);
932                    } else {
933                        bench->draw(canvas);
934                    }
935
936                    if (kDeferredSilent_benchModes == benchMode) {
937                        static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
938                    } else if (NULL != canvas) {
939                        canvas->flush();
940                    }
941
942                    if (NULL != canvas) {
943                        canvas->restore();
944                    }
945
946                    // stop the truncated timer after the last canvas call but
947                    // don't wait for all the GL calls to complete
948                    timer->truncatedEnd();
949#if SK_SUPPORT_GPU
950                    if (NULL != glContext) {
951                        context->flush();
952                        SK_GL(*glContext, Finish());
953                    }
954#endif
955                    // stop the inclusive and gpu timers once all the GL calls
956                    // have completed
957                    timer->end();
958
959                    timerData.appendTimes(timer, repeatDraw - 1 == i);
960
961                }
962                if (repeatDraw > 1) {
963                    SkString result = timerData.getResult(
964                                        logPerIter, printMin, repeatDraw, configName,
965                                        timerWall, truncatedTimerWall, timerCpu,
966                                        truncatedTimerCpu,
967                                        timerGpu && NULL != context);
968                    logger.logProgress(result);
969                }
970                if (outDir.size() > 0 && kNonRendering_Backend != backend) {
971                    saveFile(bench->getName(), configName, outDir.c_str(),
972                             device->accessBitmap(false));
973                }
974            }
975        }
976        if (loggedBenchStart) {
977            logger.logProgress(SkString("\n"));
978        }
979    }
980#if SK_SUPPORT_GPU
981#if GR_CACHE_STATS
982    for (int i = 0; i <= GrContextFactory::kLastGLContextType; ++i) {
983        GrContextFactory::GLContextType ctxType = (GrContextFactory::GLContextType)i;
984        GrContext* context = gContextFactory.get(ctxType);
985        if (NULL != context) {
986            SkDebugf("Cache Stats for %s context:\n", GrContextFactory::GLContextTypeName(ctxType));
987            context->printCacheStats();
988            SkDebugf("\n");
989        }
990    }
991#endif
992    // Destroy the GrContext before the inst tracking printing at main() exit occurs.
993    gContextFactory.destroyContexts();
994#endif
995    for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
996        SkDELETE(timers[i]);
997    }
998
999    return 0;
1000}
1001
1002#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
1003int main(int argc, char * const argv[]) {
1004    return tool_main(argc, (char**) argv);
1005}
1006#endif
1007