blob: 8f445d82c2a49f5ce9d7c279a2be07b14adf292b [file] [log] [blame]
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +00001/*
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"
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +000017#include "SkTArray.h"
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000018#include "TimerData.h"
19
mtklein@google.comc2897432013-09-10 19:23:38 +000020static const int kNumNormalRecordings = 10;
21static const int kNumRTreeRecordings = 10;
22static const int kNumPlaybacks = 1;
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000023static const size_t kNumBaseBenchmarks = 3;
24static const size_t kNumTileSizes = 3;
25static const size_t kNumBbhPlaybackBenchmarks = 3;
26static const size_t kNumBenchmarks = kNumBaseBenchmarks + kNumBbhPlaybackBenchmarks;
27
28enum BenchmarkType {
29 kNormal_BenchmarkType = 0,
30 kRTree_BenchmarkType,
31};
32
33struct Histogram {
34 Histogram() {
35 // Make fCpuTime negative so that we don't mess with stats:
36 fCpuTime = SkIntToScalar(-1);
37 }
38 SkScalar fCpuTime;
39 SkString fPath;
40};
41
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000042
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000043////////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000044// Defined below.
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000045struct BenchmarkControl;
46
47typedef void (*BenchmarkFunction)
48 (const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
49
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000050static void benchmark_playback(
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000051 const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000052static void benchmark_recording(
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000053 const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
54////////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000055
56/**
57 * Acts as a POD containing information needed to run a benchmark.
58 * Provides static methods to poll benchmark info from an index.
59 */
60struct BenchmarkControl {
61 SkISize fTileSize;
62 BenchmarkType fType;
63 BenchmarkFunction fFunction;
64 SkString fName;
65
66 /**
67 * Will construct a BenchmarkControl instance from an index between 0 an kNumBenchmarks.
68 */
69 static BenchmarkControl Make(size_t i) {
70 SkASSERT(kNumBenchmarks > i);
71 BenchmarkControl benchControl;
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000072 benchControl.fTileSize = GetTileSize(i);
73 benchControl.fType = GetBenchmarkType(i);
74 benchControl.fFunction = GetBenchmarkFunc(i);
75 benchControl.fName = GetBenchmarkName(i);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000076 return benchControl;
77 }
78
79 enum BaseBenchmarks {
80 kNormalRecord = 0,
81 kRTreeRecord,
82 kNormalPlayback,
83 };
84
85 static SkISize fTileSizes[kNumTileSizes];
86
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +000087 static SkISize GetTileSize(size_t i) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +000088 // Two of the base benchmarks don't need a tile size. But to maintain simplicity
89 // down the pipeline we have to let a couple of values unused.
90 if (i < kNumBaseBenchmarks) {
91 return SkISize::Make(256, 256);
92 }
93 if (i >= kNumBaseBenchmarks && i < kNumBenchmarks) {
94 return fTileSizes[i - kNumBaseBenchmarks];
95 }
96 SkASSERT(0);
97 return SkISize::Make(0, 0);
98 }
99
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000100 static BenchmarkType GetBenchmarkType(size_t i) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000101 if (i < kNumBaseBenchmarks) {
102 switch (i) {
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000103 case kNormalRecord:
104 return kNormal_BenchmarkType;
105 case kNormalPlayback:
106 return kNormal_BenchmarkType;
107 case kRTreeRecord:
108 return kRTree_BenchmarkType;
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000109 }
110 }
111 if (i < kNumBenchmarks) {
112 return kRTree_BenchmarkType;
113 }
114 SkASSERT(0);
115 return kRTree_BenchmarkType;
116 }
117
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000118 static BenchmarkFunction GetBenchmarkFunc(size_t i) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000119 // Base functions.
120 switch (i) {
121 case kNormalRecord:
122 return benchmark_recording;
123 case kNormalPlayback:
124 return benchmark_playback;
125 case kRTreeRecord:
126 return benchmark_recording;
127 }
128 // RTree playbacks
129 if (i < kNumBenchmarks) {
130 return benchmark_playback;
131 }
132 SkASSERT(0);
133 return NULL;
134 }
135
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000136 static SkString GetBenchmarkName(size_t i) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000137 // Base benchmark names
138 switch (i) {
139 case kNormalRecord:
140 return SkString("normal_recording");
141 case kNormalPlayback:
142 return SkString("normal_playback");
143 case kRTreeRecord:
144 return SkString("rtree_recording");
145 }
146 // RTree benchmark names.
147 if (i < kNumBenchmarks) {
148 SkASSERT(i >= kNumBaseBenchmarks);
149 SkString name;
150 name.printf("rtree_playback_%dx%d",
151 fTileSizes[i - kNumBaseBenchmarks].fWidth,
152 fTileSizes[i - kNumBaseBenchmarks].fHeight);
153 return name;
154
155 } else {
156 SkASSERT(0);
157 }
158 return SkString("");
159 }
160
161};
162
163SkISize BenchmarkControl::fTileSizes[kNumTileSizes] = {
164 SkISize::Make(256, 256),
165 SkISize::Make(512, 512),
166 SkISize::Make(1024, 1024),
167};
168
169static SkPicture* pic_from_path(const char path[]) {
170 SkFILEStream stream(path);
171 if (!stream.isValid()) {
172 SkDebugf("-- Can't open '%s'\n", path);
173 return NULL;
174 }
175 return SkPicture::CreateFromStream(&stream, &sk_tools::LazyDecodeBitmap);
176}
177
178/**
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000179 * Returns true when a tiled renderer with no bounding box hierarchy produces the given bitmap.
180 */
181static bool compare_picture(const SkString& path, const SkBitmap& inBitmap, SkPicture* pic) {
182 SkBitmap* bitmap;
183 sk_tools::TiledPictureRenderer renderer;
184 renderer.setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
185 renderer.init(pic);
186 renderer.setup();
187 renderer.render(&path, &bitmap);
188 SkAutoTDelete<SkBitmap> bmDeleter(bitmap);
189 renderer.end();
190
191 if (bitmap->getSize() != inBitmap.getSize()) {
192 return false;
193 }
194 return !memcmp(bitmap->getPixels(), inBitmap.getPixels(), bitmap->getSize());
195}
196
197/**
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000198 * This function is the sink to which all work ends up going.
199 * Renders the picture into the renderer. It may or may not use an RTree.
200 * The renderer is chosen upstream. If we want to measure recording, we will
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000201 * use a RecordPictureRenderer. If we want to measure rendering, we will use a
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000202 * TiledPictureRenderer.
203 */
204static void do_benchmark_work(sk_tools::PictureRenderer* renderer,
205 int benchmarkType, const SkString& path, SkPicture* pic,
206 const int numRepeats, const char *msg, BenchTimer* timer) {
207 SkString msgPrefix;
208
209 switch (benchmarkType){
210 case kNormal_BenchmarkType:
211 msgPrefix.set("Normal");
212 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
213 break;
214 case kRTree_BenchmarkType:
215 msgPrefix.set("RTree");
216 renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kRTree_BBoxHierarchyType);
217 break;
218 default:
219 SkASSERT(0);
220 break;
221 }
222
223 renderer->init(pic);
224
225 /**
226 * If the renderer is not tiled, assume we are measuring recording.
227 */
228 bool isPlayback = (NULL != renderer->getTiledRenderer());
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000229 // Will be non-null during RTree picture playback. For correctness test.
230 SkBitmap* bitmap = NULL;
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000231
232 SkDebugf("%s %s %s %d times...\n", msgPrefix.c_str(), msg, path.c_str(), numRepeats);
233 for (int i = 0; i < numRepeats; ++i) {
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000234 // Set up the bitmap.
235 SkBitmap** out = NULL;
236 if (i == 0 && kRTree_BenchmarkType == benchmarkType && isPlayback) {
237 out = &bitmap;
238 }
239
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000240 renderer->setup();
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000241 // Render once to fill caches. Fill bitmap during the first iteration.
242 renderer->render(NULL, out);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000243 // Render again to measure
244 timer->start();
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000245 bool result = renderer->render(NULL);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000246 timer->end();
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000247
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000248 // We only care about a false result on playback. RecordPictureRenderer::render will always
249 // return false because we are passing a NULL file name on purpose; which is fine.
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000250 if (isPlayback && !result) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000251 SkDebugf("Error rendering during playback.\n");
252 }
253 }
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000254 if (bitmap) {
255 SkAutoTDelete<SkBitmap> bmDeleter(bitmap);
256 if (!compare_picture(path, *bitmap, pic)) {
257 SkDebugf("Error: RTree produced different bitmap\n");
258 }
259 }
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000260}
261
262/**
263 * Call do_benchmark_work with a tiled renderer using the default tile dimensions.
264 */
265static void benchmark_playback(
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000266 const BenchmarkControl& benchControl,
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000267 const SkString& path, SkPicture* pic, BenchTimer* timer) {
268 sk_tools::TiledPictureRenderer renderer;
269
270 SkString message("tiled_playback");
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000271 message.appendf("_%dx%d", benchControl.fTileSize.fWidth, benchControl.fTileSize.fHeight);
272 do_benchmark_work(&renderer, benchControl.fType,
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000273 path, pic, kNumPlaybacks, message.c_str(), timer);
274}
275
276/**
277 * Call do_benchmark_work with a RecordPictureRenderer.
278 */
279static void benchmark_recording(
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000280 const BenchmarkControl& benchControl,
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000281 const SkString& path, SkPicture* pic, BenchTimer* timer) {
282 sk_tools::RecordPictureRenderer renderer;
283 int numRecordings = 0;
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000284 switch(benchControl.fType) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000285 case kRTree_BenchmarkType:
286 numRecordings = kNumRTreeRecordings;
287 break;
288 case kNormal_BenchmarkType:
289 numRecordings = kNumNormalRecordings;
290 break;
291 }
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000292 do_benchmark_work(&renderer, benchControl.fType,
293 path, pic, numRecordings, "recording", timer);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000294}
295
296/**
297 * Takes argc,argv along with one of the benchmark functions defined above.
298 * Will loop along all skp files and perform measurments.
299 *
300 * Returns a SkScalar representing CPU time taken during benchmark.
301 * As a side effect, it spits the timer result to stdout.
302 * Will return -1.0 on error.
303 */
304static bool benchmark_loop(
305 int argc,
306 char **argv,
307 const BenchmarkControl& benchControl,
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +0000308 SkTArray<Histogram>& histogram) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000309 static const SkString timeFormat("%f");
commit-bot@chromium.org55fd6122013-07-31 20:00:56 +0000310 TimerData timerData(argc - 1);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000311 for (int index = 1; index < argc; ++index) {
312 BenchTimer timer;
313 SkString path(argv[index]);
314 SkAutoTUnref<SkPicture> pic(pic_from_path(path.c_str()));
315 if (NULL == pic) {
316 SkDebugf("Couldn't create picture. Ignoring path: %s\n", path.c_str());
317 continue;
318 }
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000319 benchControl.fFunction(benchControl, path, pic, &timer);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000320
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +0000321 histogram[index - 1].fPath = path;
322 histogram[index - 1].fCpuTime = SkDoubleToScalar(timer.fCpu);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000323 }
324
325 const SkString timerResult = timerData.getResult(
commit-bot@chromium.org55fd6122013-07-31 20:00:56 +0000326 /*doubleFormat = */ timeFormat.c_str(),
327 /*result = */ TimerData::kAvg_Result,
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000328 /*configName = */ benchControl.fName.c_str(),
commit-bot@chromium.org55fd6122013-07-31 20:00:56 +0000329 /*timerFlags = */ TimerData::kCpu_Flag);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000330
331 const char findStr[] = "= ";
332 int pos = timerResult.find(findStr);
333 if (-1 == pos) {
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000334 SkDebugf("Unexpected output from TimerData::getResult(...). Unable to parse.\n");
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000335 return false;
336 }
337
338 SkScalar cpuTime = SkDoubleToScalar(atof(timerResult.c_str() + pos + sizeof(findStr) - 1));
339 if (cpuTime == 0) { // atof returns 0.0 on error.
340 SkDebugf("Unable to read value from timer result.\n");
341 return false;
342 }
343 return true;
344}
345
sglez@google.com1d38ae92013-07-19 20:03:57 +0000346int tool_main(int argc, char** argv);
347int tool_main(int argc, char** argv) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000348 SkAutoGraphics ag;
349 SkString usage;
350 usage.printf("Usage: filename [filename]*\n");
351
352 if (argc < 2) {
353 SkDebugf("%s\n", usage.c_str());
354 return -1;
355 }
356
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +0000357 SkTArray<Histogram> histograms[kNumBenchmarks];
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000358
359 for (size_t i = 0; i < kNumBenchmarks; ++i) {
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +0000360 histograms[i].reset(argc - 1);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000361 bool success = benchmark_loop(
362 argc, argv,
363 BenchmarkControl::Make(i),
commit-bot@chromium.org49a07ad2013-07-22 19:28:40 +0000364 histograms[i]);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000365 if (!success) {
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000366 SkDebugf("benchmark_loop failed at index %d\n", i);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000367 }
368 }
369
370 // Output gnuplot readable histogram data..
371 const char* pbTitle = "bbh_shootout_playback.dat";
372 const char* recTitle = "bbh_shootout_record.dat";
373 SkFILEWStream playbackOut(pbTitle);
374 SkFILEWStream recordOut(recTitle);
375 recordOut.writeText("# ");
376 playbackOut.writeText("# ");
377 for (size_t i = 0; i < kNumBenchmarks; ++i) {
378 SkString out;
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000379 out.printf("%s ", BenchmarkControl::GetBenchmarkName(i).c_str());
380 if (BenchmarkControl::GetBenchmarkFunc(i) == &benchmark_recording) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000381 recordOut.writeText(out.c_str());
382 }
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000383 if (BenchmarkControl::GetBenchmarkFunc(i) == &benchmark_playback) {
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000384 playbackOut.writeText(out.c_str());
385 }
386 }
387 recordOut.writeText("\n");
388 playbackOut.writeText("\n");
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000389 // Write to file, and save recording averages.
390 SkScalar avgRecord = SkIntToScalar(0);
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000391 SkScalar avgPlayback = SkIntToScalar(0);
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000392 SkScalar avgRecordRTree = SkIntToScalar(0);
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000393 SkScalar avgPlaybackRTree = SkIntToScalar(0);
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000394 for (int i = 0; i < argc - 1; ++i) {
395 SkString pbLine;
396 SkString recLine;
397 // ==== Write record info
398 recLine.printf("%d ", i);
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000399 SkScalar cpuTime = histograms[BenchmarkControl::kNormalRecord][i].fCpuTime;
400 recLine.appendf("%f ", cpuTime);
401 avgRecord += cpuTime;
402 cpuTime = histograms[BenchmarkControl::kRTreeRecord][i].fCpuTime;
403 recLine.appendf("%f", cpuTime);
404 avgRecordRTree += cpuTime;
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000405 avgPlaybackRTree += cpuTime;
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000406
407 // ==== Write playback info
408 pbLine.printf("%d ", i);
409 pbLine.appendf("%f ", histograms[2][i].fCpuTime); // Start with normal playback time.
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000410 avgPlayback += histograms[kNumBbhPlaybackBenchmarks - 1][i].fCpuTime;
411 avgPlaybackRTree += histograms[kNumBbhPlaybackBenchmarks][i].fCpuTime;
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000412 // Append all playback benchmark times.
413 for (size_t j = kNumBbhPlaybackBenchmarks; j < kNumBenchmarks; ++j) {
414 pbLine.appendf("%f ", histograms[j][i].fCpuTime);
415 }
416 pbLine.remove(pbLine.size() - 1, 1); // Remove trailing space from line.
417 pbLine.appendf("\n");
418 recLine.appendf("\n");
419 playbackOut.writeText(pbLine.c_str());
420 recordOut.writeText(recLine.c_str());
421 }
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000422 avgRecord /= argc - 1;
423 avgRecordRTree /= argc - 1;
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000424 avgPlayback /= argc - 1;
425 avgPlaybackRTree /= argc - 1;
commit-bot@chromium.org2e915b32013-08-20 12:23:18 +0000426 SkDebugf("Average base recording time: %.3fms\n", avgRecord);
427 SkDebugf("Average recording time with rtree: %.3fms\n", avgRecordRTree);
commit-bot@chromium.org62417542013-08-28 16:02:47 +0000428 SkDebugf("Average base playback time: %.3fms\n", avgPlayback);
429 SkDebugf("Average playback time with rtree: %.3fms\n", avgPlaybackRTree);
430
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000431 SkDebugf("\nWrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle);
432
433 return 0;
434}
435
sglez@google.com5f3f6812013-07-19 20:21:05 +0000436#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
commit-bot@chromium.org6645cde2013-07-19 18:54:04 +0000437int main(int argc, char** argv) {
438 return tool_main(argc, argv);
439}
sglez@google.com5f3f6812013-07-19 20:21:05 +0000440#endif