| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| */ |
| |
| #include <VisualBench/VisualBenchmarkStream.h> |
| #include <VisualBench/WrappedBenchmark.h> |
| #include "GMBench.h" |
| #include "SkOSFile.h" |
| #include "SkPath.h" |
| #include "SkPictureRecorder.h" |
| #include "SkStream.h" |
| #include "sk_tool_utils.h" |
| #include "VisualFlags.h" |
| #include "VisualSKPBench.h" |
| |
| #if SK_SUPPORT_GPU |
| #include "GrContext.h" |
| #endif |
| |
| DEFINE_string2(match, m, nullptr, |
| "[~][^]substring[$] [...] of bench name to run.\n" |
| "Multiple matches may be separated by spaces.\n" |
| "~ causes a matching bench to always be skipped\n" |
| "^ requires the start of the bench to match\n" |
| "$ requires the end of the bench to match\n" |
| "^ and $ requires an exact match\n" |
| "If a bench does not match any list entry,\n" |
| "it is skipped unless some list entry starts with ~"); |
| DEFINE_string(skps, "skps", "Directory to read skps from."); |
| DEFINE_bool(warmup, true, "Include a warmup bench? (Excluding the warmup may compromise results)"); |
| |
| // We draw a big nonAA path to warmup the gpu / cpu |
| #include "SkPerlinNoiseShader.h" |
| class WarmupBench : public Benchmark { |
| public: |
| WarmupBench() { |
| sk_tool_utils::make_big_path(fPath); |
| fPerlinRect = SkRect::MakeLTRB(0., 0., 400., 400.); |
| } |
| private: |
| const char* onGetName() override { return "warmupbench"; } |
| SkIPoint onGetSize() override { |
| int w = SkScalarCeilToInt(SkTMax(fPath.getBounds().right(), fPerlinRect.right())); |
| int h = SkScalarCeilToInt(SkTMax(fPath.getBounds().bottom(), fPerlinRect.bottom())); |
| return SkIPoint::Make(w, h); |
| } |
| void onDraw(int loops, SkCanvas* canvas) override { |
| // We draw a big path to warm up the cpu, and then use perlin noise shader to warm up the |
| // gpu |
| SkPaint paint; |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(2); |
| |
| SkPaint perlinPaint; |
| perlinPaint.setShader(SkPerlinNoiseShader::CreateTurbulence(0.1f, 0.1f, 1, 0, |
| nullptr))->unref(); |
| for (int i = 0; i < loops; i++) { |
| canvas->drawPath(fPath, paint); |
| canvas->drawRect(fPerlinRect, perlinPaint); |
| #if SK_SUPPORT_GPU |
| // Ensure the GrContext doesn't batch across draw loops. |
| if (GrContext* context = canvas->getGrContext()) { |
| context->flush(); |
| } |
| #endif |
| } |
| } |
| SkPath fPath; |
| SkRect fPerlinRect; |
| }; |
| |
| VisualBenchmarkStream::VisualBenchmarkStream(const SkSurfaceProps& surfaceProps, bool justSKP) |
| : fSurfaceProps(surfaceProps) |
| , fBenches(BenchRegistry::Head()) |
| , fGMs(skiagm::GMRegistry::Head()) |
| , fSourceType(nullptr) |
| , fBenchType(nullptr) |
| , fCurrentSKP(0) |
| , fIsWarmedUp(false) { |
| for (int i = 0; i < FLAGS_skps.count(); i++) { |
| if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { |
| fSKPs.push_back() = FLAGS_skps[i]; |
| } else { |
| SkOSFile::Iter it(FLAGS_skps[i], ".skp"); |
| SkString path; |
| while (it.next(&path)) { |
| fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); |
| } |
| } |
| } |
| |
| if (justSKP) { |
| fGMs = nullptr; |
| fBenches = nullptr; |
| } |
| |
| // seed with an initial benchmark |
| // NOTE the initial benchmark will not have preTimingHooks called, but that is okay because |
| // it is the warmupbench |
| this->next(); |
| } |
| |
| sk_sp<SkPicture> VisualBenchmarkStream::ReadPicture(const char path[]) { |
| // Not strictly necessary, as it will be checked again later, |
| // but helps to avoid a lot of pointless work if we're going to skip it. |
| if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) { |
| return nullptr; |
| } |
| |
| SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path)); |
| if (stream.get() == nullptr) { |
| SkDebugf("Could not read %s.\n", path); |
| return nullptr; |
| } |
| |
| auto pic = SkPicture::MakeFromStream(stream.get()); |
| if (!pic) { |
| SkDebugf("Could not read %s as an SkPicture.\n", path); |
| } |
| return pic; |
| } |
| |
| Benchmark* VisualBenchmarkStream::next() { |
| Benchmark* bench; |
| if (FLAGS_warmup && !fIsWarmedUp) { |
| fIsWarmedUp = true; |
| bench = new WarmupBench; |
| } else { |
| // skips non matching benches |
| while ((bench = this->innerNext()) && |
| (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName()) || |
| !bench->isSuitableFor(Benchmark::kGPU_Backend))) { |
| bench->unref(); |
| } |
| } |
| |
| // TODO move this all to --config |
| if (bench && FLAGS_cpu) { |
| bench = new CpuWrappedBenchmark(fSurfaceProps, bench); |
| } else if (bench && FLAGS_offscreen) { |
| bench = new GpuWrappedBenchmark(fSurfaceProps, bench, FLAGS_msaa); |
| } |
| |
| fBenchmark.reset(bench); |
| return fBenchmark; |
| } |
| |
| Benchmark* VisualBenchmarkStream::innerNext() { |
| while (fBenches) { |
| Benchmark* bench = fBenches->factory()(nullptr); |
| fBenches = fBenches->next(); |
| if (bench->isVisual()) { |
| fSourceType = "bench"; |
| fBenchType = "micro"; |
| return bench; |
| } |
| bench->unref(); |
| } |
| |
| while (fGMs) { |
| SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(nullptr)); |
| fGMs = fGMs->next(); |
| if (gm->runAsBench()) { |
| fSourceType = "gm"; |
| fBenchType = "micro"; |
| return new GMBench(gm.release()); |
| } |
| } |
| |
| // Render skps |
| while (fCurrentSKP < fSKPs.count()) { |
| const SkString& path = fSKPs[fCurrentSKP++]; |
| sk_sp<SkPicture> pic = ReadPicture(path.c_str()); |
| if (!pic) { |
| continue; |
| } |
| |
| SkString name = SkOSPath::Basename(path.c_str()); |
| fSourceType = "skp"; |
| fBenchType = "playback"; |
| return new VisualSKPBench(name.c_str(), pic.get()); |
| } |
| |
| return nullptr; |
| } |