1 /* 2 * Copyright 2014 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 "SkCommandLineFlags.h" 9 #include "SkForceLinking.h" 10 #include "SkGraphics.h" 11 #include "SkOSFile.h" 12 #include "SkPicture.h" 13 #include "SkPictureRecorder.h" 14 #include "SkStream.h" 15 #include "SkString.h" 16 17 #include "../include/record/SkRecording.h" 18 19 #include "BenchTimer.h" 20 #include "Stats.h" 21 22 typedef WallTimer Timer; 23 24 __SK_FORCE_IMAGE_DECODER_LINKING; 25 26 DEFINE_string2(skps, r, "skps", "Directory containing SKPs to playback."); 27 DEFINE_int32(samples, 10, "Gather this many samples of each picture playback."); 28 DEFINE_bool(skr, false, "Play via SkRecord instead of SkPicture."); 29 DEFINE_int32(tile, 1000000000, "Simulated tile size."); 30 DEFINE_string(match, "", "The usual filters on file names of SKPs to bench."); 31 DEFINE_string(timescale, "ms", "Print times in ms, us, or ns"); 32 DEFINE_int32(verbose, 0, "0: print min sample; " 33 "1: print min, mean, max and noise indication " 34 "2: print all samples"); 35 36 static double timescale() { 37 if (FLAGS_timescale.contains("us")) return 1000; 38 if (FLAGS_timescale.contains("ns")) return 1000000; 39 return 1; 40 } 41 42 static SkPicture* rerecord_with_tilegrid(SkPicture& src) { 43 SkTileGridFactory::TileGridInfo info; 44 info.fTileInterval.set(FLAGS_tile, FLAGS_tile); 45 info.fMargin.setEmpty(); 46 info.fOffset.setZero(); 47 SkTileGridFactory factory(info); 48 49 SkPictureRecorder recorder; 50 src.draw(recorder.beginRecording(src.width(), src.height(), &factory)); 51 return recorder.endRecording(); 52 } 53 54 static EXPERIMENTAL::SkPlayback* rerecord_with_skr(SkPicture& src) { 55 EXPERIMENTAL::SkRecording recording(src.width(), src.height()); 56 src.draw(recording.canvas()); 57 return recording.releasePlayback(); 58 } 59 60 static void draw(const EXPERIMENTAL::SkPlayback& skr, const SkPicture& skp, SkCanvas* canvas) { 61 if (FLAGS_skr) { 62 skr.draw(canvas); 63 } else { 64 skp.draw(canvas); 65 } 66 } 67 68 static void bench(SkPMColor* scratch, SkPicture& src, const char* name) { 69 SkAutoTUnref<SkPicture> picture(rerecord_with_tilegrid(src)); 70 SkAutoTDelete<EXPERIMENTAL::SkPlayback> record(rerecord_with_skr(src)); 71 72 SkAutoTDelete<SkCanvas> canvas(SkCanvas::NewRasterDirectN32(src.width(), 73 src.height(), 74 scratch, 75 src.width() * sizeof(SkPMColor))); 76 canvas->clipRect(SkRect::MakeWH(SkIntToScalar(FLAGS_tile), SkIntToScalar(FLAGS_tile))); 77 78 // Draw once to warm any caches. The first sample otherwise can be very noisy. 79 draw(*record, *picture, canvas.get()); 80 81 Timer timer; 82 SkAutoTMalloc<double> samples(FLAGS_samples); 83 for (int i = 0; i < FLAGS_samples; i++) { 84 // We assume timer overhead (typically, ~30ns) is insignificant 85 // compared to draw runtime (at least ~100us, usually several ms). 86 timer.start(timescale()); 87 draw(*record, *picture, canvas.get()); 88 timer.end(); 89 samples[i] = timer.fWall; 90 } 91 92 Stats stats(samples.get(), FLAGS_samples); 93 if (FLAGS_verbose == 0) { 94 printf("%g\t%s\n", stats.min, name); 95 } else if (FLAGS_verbose == 1) { 96 // Get a rough idea of how noisy the measurements were. 97 const double noisePercent = 100 * sqrt(stats.var) / stats.mean; 98 printf("%g\t%g\t%g\t%.0f%%\t%s\n", stats.min, stats.mean, stats.max, noisePercent, name); 99 } else if (FLAGS_verbose == 2) { 100 printf("%s", name); 101 for (int i = 0; i < FLAGS_samples; i++) { 102 printf("\t%g", samples[i]); 103 } 104 printf("\n"); 105 } 106 } 107 108 int tool_main(int argc, char** argv); 109 int tool_main(int argc, char** argv) { 110 SkCommandLineFlags::Parse(argc, argv); 111 SkAutoGraphics autoGraphics; 112 113 // We share a single scratch bitmap among benches to reduce the profile noise from allocation. 114 static const int kMaxArea = 209825221; // tabl_mozilla is this big. 115 SkAutoTMalloc<SkPMColor> scratch(kMaxArea); 116 117 SkOSFile::Iter it(FLAGS_skps[0], ".skp"); 118 SkString filename; 119 bool failed = false; 120 while (it.next(&filename)) { 121 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) { 122 continue; 123 } 124 125 const SkString path = SkOSPath::SkPathJoin(FLAGS_skps[0], filename.c_str()); 126 127 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path.c_str())); 128 if (!stream) { 129 SkDebugf("Could not read %s.\n", path.c_str()); 130 failed = true; 131 continue; 132 } 133 SkAutoTUnref<SkPicture> src(SkPicture::CreateFromStream(stream)); 134 if (!src) { 135 SkDebugf("Could not read %s as an SkPicture.\n", path.c_str()); 136 failed = true; 137 continue; 138 } 139 140 if (src->width() * src->height() > kMaxArea) { 141 SkDebugf("%s (%dx%d) is larger than hardcoded scratch bitmap (%dpx).\n", 142 path.c_str(), src->width(), src->height(), kMaxArea); 143 failed = true; 144 continue; 145 } 146 147 bench(scratch.get(), *src, filename.c_str()); 148 } 149 return failed ? 1 : 0; 150 } 151 152 #if !defined SK_BUILD_FOR_IOS 153 int main(int argc, char * const argv[]) { 154 return tool_main(argc, (char**) argv); 155 } 156 #endif 157