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 "Timer.h"
9#include "Benchmark.h"
10#include "LazyDecodeBitmap.h"
11#include "PictureBenchmark.h"
12#include "PictureRenderer.h"
13#include "SkCommandLineFlags.h"
14#include "SkForceLinking.h"
15#include "SkGraphics.h"
16#include "SkStream.h"
17#include "SkString.h"
18#include "SkTArray.h"
19
20typedef sk_tools::PictureRenderer::BBoxHierarchyType BBoxType;
21static const int kBBoxTypeCount = sk_tools::PictureRenderer::kLast_BBoxHierarchyType + 1;
22
23
24DEFINE_string2(skps, r, "", "The list of SKPs to benchmark.");
25DEFINE_string(bb_types, "", "The set of bbox types to test. If empty, all are tested. "
26                       "Should be one or more of none, rtree, tilegrid.");
27DEFINE_int32(record, 100, "Number of times to record each SKP.");
28DEFINE_int32(playback, 1, "Number of times to playback each SKP.");
29DEFINE_int32(tilesize, 256, "The size of a tile.");
30
31struct Measurement {
32    SkString fName;
33    double fRecordAverage[kBBoxTypeCount];
34    double fPlaybackAverage[kBBoxTypeCount];
35};
36
37const char* kBBoxHierarchyTypeNames[kBBoxTypeCount] = {
38    "none", // kNone_BBoxHierarchyType
39    "rtree", // kRTree_BBoxHierarchyType
40    "tilegrid", // kTileGrid_BBoxHierarchyType
41};
42
43static SkPicture* pic_from_path(const char path[]) {
44    SkFILEStream stream(path);
45    if (!stream.isValid()) {
46        SkDebugf("-- Can't open '%s'\n", path);
47        return NULL;
48    }
49    return SkPicture::CreateFromStream(&stream, &sk_tools::LazyDecodeBitmap);
50}
51
52/**
53 * This function is the sink to which all work ends up going.
54 * @param renderer The renderer to use to perform the work.
55 *                 To measure rendering, use a TiledPictureRenderer.
56 *                 To measure recording, use a RecordPictureRenderer.
57 * @param bBoxType The bounding box hierarchy type to use.
58 * @param pic The picture to draw to the renderer.
59 * @param numRepeats The number of times to repeat the draw.
60 * @param timer The timer used to benchmark the work.
61 */
62static void do_benchmark_work(sk_tools::PictureRenderer* renderer,
63        BBoxType bBoxType,
64        SkPicture* pic,
65        const int numRepeats,
66        Timer* timer) {
67    renderer->setBBoxHierarchyType(bBoxType);
68    renderer->setGridSize(FLAGS_tilesize, FLAGS_tilesize);
69    renderer->init(pic, NULL, NULL, NULL, false);
70
71    SkDebugf("%s %d times...\n", renderer->getConfigName().c_str(), numRepeats);
72    for (int i = 0; i < numRepeats; ++i) {
73        renderer->setup();
74        // Render once to fill caches
75        renderer->render();
76        // Render again to measure
77        timer->start();
78        renderer->render();
79        timer->end();
80    }
81}
82
83int tool_main(int argc, char** argv);
84int tool_main(int argc, char** argv) {
85    SkCommandLineFlags::Parse(argc, argv);
86    SkAutoGraphics ag;
87    bool includeBBoxType[kBBoxTypeCount];
88    for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
89        includeBBoxType[bBoxType] = (FLAGS_bb_types.count() == 0) ||
90            FLAGS_bb_types.contains(kBBoxHierarchyTypeNames[bBoxType]);
91    }
92    // go through all the pictures
93    SkTArray<Measurement> measurements;
94    for (int index = 0; index < FLAGS_skps.count(); ++index) {
95        const char* path = FLAGS_skps[index];
96        SkPicture* picture = pic_from_path(path);
97        if (NULL == picture) {
98            SkDebugf("Couldn't create picture. Ignoring path: %s\n", path);
99            continue;
100        }
101        SkDebugf("Benchmarking path: %s\n", path);
102        Measurement& measurement = measurements.push_back();
103        measurement.fName = path;
104        for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
105            if (!includeBBoxType[bBoxType]) { continue; }
106            if (FLAGS_playback > 0) {
107#if SK_SUPPORT_GPU
108                GrContext::Options grContextOpts;
109                sk_tools::TiledPictureRenderer playbackRenderer(grContextOpts);
110#else
111                sk_tools::TiledPictureRenderer playbackRenderer;
112#endif
113                Timer playbackTimer;
114                do_benchmark_work(&playbackRenderer, (BBoxType)bBoxType,
115                                  picture, FLAGS_playback, &playbackTimer);
116                measurement.fPlaybackAverage[bBoxType] = playbackTimer.fCpu;
117            }
118            if (FLAGS_record > 0) {
119#if SK_SUPPORT_GPU
120                GrContext::Options grContextOpts;
121                sk_tools::RecordPictureRenderer recordRenderer(grContextOpts);
122#else
123                sk_tools::RecordPictureRenderer recordRenderer;
124#endif
125                Timer recordTimer;
126                do_benchmark_work(&recordRenderer, (BBoxType)bBoxType,
127                                  picture, FLAGS_record, &recordTimer);
128                measurement.fRecordAverage[bBoxType] = recordTimer.fCpu;
129            }
130        }
131    }
132
133    Measurement globalMeasurement;
134    for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
135        if (!includeBBoxType[bBoxType]) { continue; }
136        globalMeasurement.fPlaybackAverage[bBoxType] = 0;
137        globalMeasurement.fRecordAverage[bBoxType] = 0;
138        for (int index = 0; index < measurements.count(); ++index) {
139            const Measurement& measurement = measurements[index];
140            globalMeasurement.fPlaybackAverage[bBoxType] +=
141                measurement.fPlaybackAverage[bBoxType];
142            globalMeasurement.fRecordAverage[bBoxType] +=
143                measurement.fRecordAverage[bBoxType];
144        }
145        globalMeasurement.fPlaybackAverage[bBoxType] /= measurements.count();
146        globalMeasurement.fRecordAverage[bBoxType] /= measurements.count();
147    }
148
149    // Output gnuplot readable histogram data..
150    const char* pbTitle = "bbh_shootout_playback.dat";
151    const char* recTitle = "bbh_shootout_record.dat";
152    SkFILEWStream playbackOut(pbTitle);
153    SkFILEWStream recordOut(recTitle);
154    recordOut.writeText("# ");
155    playbackOut.writeText("# ");
156    SkDebugf("---\n");
157    for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
158        if (!includeBBoxType[bBoxType]) { continue; }
159        SkString out;
160        out.printf("%s ", kBBoxHierarchyTypeNames[bBoxType]);
161        recordOut.writeText(out.c_str());
162        playbackOut.writeText(out.c_str());
163
164        if (FLAGS_record > 0) {
165            SkDebugf("Average %s recording time: %.3fms\n",
166                kBBoxHierarchyTypeNames[bBoxType],
167                globalMeasurement.fRecordAverage[bBoxType]);
168        }
169        if (FLAGS_playback > 0) {
170            SkDebugf("Average %s playback time: %.3fms\n",
171                kBBoxHierarchyTypeNames[bBoxType],
172                globalMeasurement.fPlaybackAverage[bBoxType]);
173        }
174    }
175    recordOut.writeText("\n");
176    playbackOut.writeText("\n");
177    // Write to file, and save recording averages.
178    for (int index = 0; index < measurements.count(); ++index) {
179        const Measurement& measurement = measurements[index];
180        SkString pbLine;
181        SkString recLine;
182
183        pbLine.printf("%d", index);
184        recLine.printf("%d", index);
185        for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
186            if (!includeBBoxType[bBoxType]) { continue; }
187            pbLine.appendf(" %f", measurement.fPlaybackAverage[bBoxType]);
188            recLine.appendf(" %f", measurement.fRecordAverage[bBoxType]);
189        }
190        pbLine.appendf("\n");
191        recLine.appendf("\n");
192        playbackOut.writeText(pbLine.c_str());
193        recordOut.writeText(recLine.c_str());
194    }
195    SkDebugf("\nWrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle);
196    return 0;
197}
198
199#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
200int main(int argc, char** argv) {
201    return tool_main(argc, argv);
202}
203#endif
204