| /* | 
 |  * 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; | 
 | } |