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