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 20 typedef sk_tools::PictureRenderer::BBoxHierarchyType BBoxType; 21 static const int kBBoxTypeCount = sk_tools::PictureRenderer::kLast_BBoxHierarchyType + 1; 22 23 24 DEFINE_string2(skps, r, "", "The list of SKPs to benchmark."); 25 DEFINE_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."); 27 DEFINE_int32(record, 100, "Number of times to record each SKP."); 28 DEFINE_int32(playback, 1, "Number of times to playback each SKP."); 29 DEFINE_int32(tilesize, 256, "The size of a tile."); 30 31 struct Measurement { 32 SkString fName; 33 double fRecordAverage[kBBoxTypeCount]; 34 double fPlaybackAverage[kBBoxTypeCount]; 35 }; 36 37 const char* kBBoxHierarchyTypeNames[kBBoxTypeCount] = { 38 "none", // kNone_BBoxHierarchyType 39 "quadtree", // kQuadTree_BBoxHierarchyType 40 "rtree", // kRTree_BBoxHierarchyType 41 "tilegrid", // kTileGrid_BBoxHierarchyType 42 }; 43 44 static 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 */ 63 static void do_benchmark_work(sk_tools::PictureRenderer* renderer, 64 BBoxType bBoxType, 65 SkPicture* pic, 66 const int numRepeats, 67 Timer* timer) { 68 renderer->setBBoxHierarchyType(bBoxType); 69 renderer->setGridSize(FLAGS_tilesize, FLAGS_tilesize); 70 renderer->init(pic, NULL, NULL, NULL, false); 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(); 77 // Render again to measure 78 timer->start(); 79 renderer->render(); 80 timer->end(); 81 } 82 } 83 84 int tool_main(int argc, char** argv); 85 int 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 Timer 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 Timer 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) 191 int main(int argc, char** argv) { 192 return tool_main(argc, argv); 193 } 194 #endif 195