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, 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 "rtree", // kRTree_BBoxHierarchyType 40 "tilegrid", // kTileGrid_BBoxHierarchyType 41 }; 42 43 static 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 */ 62 static 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 83 int tool_main(int argc, char** argv); 84 int 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) 200 int main(int argc, char** argv) { 201 return tool_main(argc, argv); 202 } 203 #endif 204