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
24 DEFINE_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 ~");
33 DEFINE_string(skps, "skps", "Directory to read skps from.");
34 DEFINE_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"
38 class WarmupBench : public Benchmark {
39 public:
WarmupBench()40 WarmupBench() {
41 sk_tool_utils::make_big_path(fPath);
42 fPerlinRect = SkRect::MakeLTRB(0., 0., 400., 400.);
43 }
44 private:
onGetName()45 const char* onGetName() override { return "warmupbench"; }
onGetSize()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 }
onDraw(int loops,SkCanvas * canvas)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
VisualBenchmarkStream(const SkSurfaceProps & surfaceProps,bool justSKP)76 VisualBenchmarkStream::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
ReadPicture(const char * path,SkAutoTUnref<SkPicture> * pic)107 bool 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
next()128 Benchmark* 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
innerNext()153 Benchmark* 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