1/* 2 * Copyright 2015 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 9#include <VisualBench/VisualBenchmarkStream.h> 10#include <VisualBench/WrappedBenchmark.h> 11#include "GMBench.h" 12#include "SkOSFile.h" 13#include "SkPath.h" 14#include "SkPictureRecorder.h" 15#include "SkStream.h" 16#include "sk_tool_utils.h" 17#include "VisualFlags.h" 18#include "VisualSKPBench.h" 19 20#if SK_SUPPORT_GPU 21#include "GrContext.h" 22#endif 23 24DEFINE_string2(match, m, nullptr, 25 "[~][^]substring[$] [...] of bench name to run.\n" 26 "Multiple matches may be separated by spaces.\n" 27 "~ causes a matching bench to always be skipped\n" 28 "^ requires the start of the bench to match\n" 29 "$ requires the end of the bench to match\n" 30 "^ and $ requires an exact match\n" 31 "If a bench does not match any list entry,\n" 32 "it is skipped unless some list entry starts with ~"); 33DEFINE_string(skps, "skps", "Directory to read skps from."); 34DEFINE_bool(warmup, true, "Include a warmup bench? (Excluding the warmup may compromise results)"); 35 36// We draw a big nonAA path to warmup the gpu / cpu 37#include "SkPerlinNoiseShader.h" 38class WarmupBench : public Benchmark { 39public: 40 WarmupBench() { 41 sk_tool_utils::make_big_path(fPath); 42 fPerlinRect = SkRect::MakeLTRB(0., 0., 400., 400.); 43 } 44private: 45 const char* onGetName() override { return "warmupbench"; } 46 SkIPoint onGetSize() override { 47 int w = SkScalarCeilToInt(SkTMax(fPath.getBounds().right(), fPerlinRect.right())); 48 int h = SkScalarCeilToInt(SkTMax(fPath.getBounds().bottom(), fPerlinRect.bottom())); 49 return SkIPoint::Make(w, h); 50 } 51 void onDraw(int loops, SkCanvas* canvas) override { 52 // We draw a big path to warm up the cpu, and then use perlin noise shader to warm up the 53 // gpu 54 SkPaint paint; 55 paint.setStyle(SkPaint::kStroke_Style); 56 paint.setStrokeWidth(2); 57 58 SkPaint perlinPaint; 59 perlinPaint.setShader(SkPerlinNoiseShader::CreateTurbulence(0.1f, 0.1f, 1, 0, 60 nullptr))->unref(); 61 for (int i = 0; i < loops; i++) { 62 canvas->drawPath(fPath, paint); 63 canvas->drawRect(fPerlinRect, perlinPaint); 64#if SK_SUPPORT_GPU 65 // Ensure the GrContext doesn't batch across draw loops. 66 if (GrContext* context = canvas->getGrContext()) { 67 context->flush(); 68 } 69#endif 70 } 71 } 72 SkPath fPath; 73 SkRect fPerlinRect; 74}; 75 76VisualBenchmarkStream::VisualBenchmarkStream(const SkSurfaceProps& surfaceProps, bool justSKP) 77 : fSurfaceProps(surfaceProps) 78 , fBenches(BenchRegistry::Head()) 79 , fGMs(skiagm::GMRegistry::Head()) 80 , fSourceType(nullptr) 81 , fBenchType(nullptr) 82 , fCurrentSKP(0) 83 , fIsWarmedUp(false) { 84 for (int i = 0; i < FLAGS_skps.count(); i++) { 85 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { 86 fSKPs.push_back() = FLAGS_skps[i]; 87 } else { 88 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); 89 SkString path; 90 while (it.next(&path)) { 91 fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); 92 } 93 } 94 } 95 96 if (justSKP) { 97 fGMs = nullptr; 98 fBenches = nullptr; 99 } 100 101 // seed with an initial benchmark 102 // NOTE the initial benchmark will not have preTimingHooks called, but that is okay because 103 // it is the warmupbench 104 this->next(); 105} 106 107bool VisualBenchmarkStream::ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) { 108 // Not strictly necessary, as it will be checked again later, 109 // but helps to avoid a lot of pointless work if we're going to skip it. 110 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) { 111 return false; 112 } 113 114 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path)); 115 if (stream.get() == nullptr) { 116 SkDebugf("Could not read %s.\n", path); 117 return false; 118 } 119 120 pic->reset(SkPicture::CreateFromStream(stream.get())); 121 if (pic->get() == nullptr) { 122 SkDebugf("Could not read %s as an SkPicture.\n", path); 123 return false; 124 } 125 return true; 126} 127 128Benchmark* VisualBenchmarkStream::next() { 129 Benchmark* bench; 130 if (FLAGS_warmup && !fIsWarmedUp) { 131 fIsWarmedUp = true; 132 bench = new WarmupBench; 133 } else { 134 // skips non matching benches 135 while ((bench = this->innerNext()) && 136 (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName()) || 137 !bench->isSuitableFor(Benchmark::kGPU_Backend))) { 138 bench->unref(); 139 } 140 } 141 142 // TODO move this all to --config 143 if (bench && FLAGS_cpu) { 144 bench = new CpuWrappedBenchmark(fSurfaceProps, bench); 145 } else if (bench && FLAGS_offscreen) { 146 bench = new GpuWrappedBenchmark(fSurfaceProps, bench, FLAGS_msaa); 147 } 148 149 fBenchmark.reset(bench); 150 return fBenchmark; 151} 152 153Benchmark* VisualBenchmarkStream::innerNext() { 154 while (fBenches) { 155 Benchmark* bench = fBenches->factory()(nullptr); 156 fBenches = fBenches->next(); 157 if (bench->isVisual()) { 158 fSourceType = "bench"; 159 fBenchType = "micro"; 160 return bench; 161 } 162 bench->unref(); 163 } 164 165 while (fGMs) { 166 SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(nullptr)); 167 fGMs = fGMs->next(); 168 if (gm->runAsBench()) { 169 fSourceType = "gm"; 170 fBenchType = "micro"; 171 return new GMBench(gm.detach()); 172 } 173 } 174 175 // Render skps 176 while (fCurrentSKP < fSKPs.count()) { 177 const SkString& path = fSKPs[fCurrentSKP++]; 178 SkAutoTUnref<SkPicture> pic; 179 if (!ReadPicture(path.c_str(), &pic)) { 180 continue; 181 } 182 183 SkString name = SkOSPath::Basename(path.c_str()); 184 fSourceType = "skp"; 185 fBenchType = "playback"; 186 return new VisualSKPBench(name.c_str(), pic.get()); 187 } 188 189 return nullptr; 190} 191