/* * 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(); } bool VisualBenchmarkStream::ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) { // 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 false; } SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path)); if (stream.get() == nullptr) { SkDebugf("Could not read %s.\n", path); return false; } pic->reset(SkPicture::CreateFromStream(stream.get())); if (pic->get() == nullptr) { SkDebugf("Could not read %s as an SkPicture.\n", path); return false; } return true; } 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.detach()); } } // Render skps while (fCurrentSKP < fSKPs.count()) { const SkString& path = fSKPs[fCurrentSKP++]; SkAutoTUnref<SkPicture> pic; if (!ReadPicture(path.c_str(), &pic)) { continue; } SkString name = SkOSPath::Basename(path.c_str()); fSourceType = "skp"; fBenchType = "playback"; return new VisualSKPBench(name.c_str(), pic.get()); } return nullptr; }