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