• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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