diff options
author | Brian Salomon <bsalomon@google.com> | 2017-10-11 15:34:27 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-10-11 20:32:49 +0000 |
commit | 0b4d8aa108f2fd14f1ee4de1affacab166d7a357 (patch) | |
tree | 1a63c56bd675ebf9400980f25ebc61bb88bfaa56 | |
parent | ba1c7901a7448a388fd835e487b6b9d223236826 (diff) |
Add benchmark for comparing multitexturing to non-multitexturing image draws.
Allows benchmarks to override GrContextOptions.
Removes the ability to use the same GrContext for all benchmarks in a config.
Change-Id: I5ab9f6e81055451ac912a66537843d1a49f3b479
Reviewed-on: https://skia-review.googlesource.com/34080
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r-- | bench/Benchmark.h | 5 | ||||
-rw-r--r-- | bench/GMBench.h | 4 | ||||
-rw-r--r-- | bench/MultitextureImageBench.cpp | 125 | ||||
-rw-r--r-- | bench/nanobench.cpp | 75 | ||||
-rw-r--r-- | bench/nanobench.h | 3 | ||||
-rw-r--r-- | gn/bench.gni | 1 | ||||
-rw-r--r-- | include/gpu/GrCaps.h | 3 | ||||
-rw-r--r-- | include/gpu/GrContextOptions.h | 6 | ||||
-rw-r--r-- | include/gpu/GrShaderCaps.h | 3 | ||||
-rw-r--r-- | src/gpu/GrShaderCaps.cpp | 7 | ||||
-rw-r--r-- | src/gpu/ops/GrTextureOp.cpp | 4 | ||||
-rw-r--r-- | tools/flags/SkCommonFlags.h | 1 |
12 files changed, 190 insertions, 47 deletions
diff --git a/bench/Benchmark.h b/bench/Benchmark.h index 8fc75f8b8e..ddc93870cd 100644 --- a/bench/Benchmark.h +++ b/bench/Benchmark.h @@ -27,7 +27,7 @@ * DEF_BENCH(return new MyBenchmark(...)) */ - +struct GrContextOptions; class SkCanvas; class SkPaint; @@ -63,6 +63,9 @@ public: return backend != kNonRendering_Backend; } + // Allows a benchmark to override options used to construct the GrContext. + virtual void modifyGrContextOptions(GrContextOptions*) {} + virtual int calculateLoops(int defaultLoops) const { return defaultLoops; } diff --git a/bench/GMBench.h b/bench/GMBench.h index c9ec80c643..5a917ad2a7 100644 --- a/bench/GMBench.h +++ b/bench/GMBench.h @@ -20,6 +20,10 @@ public: GMBench(skiagm::GM* gm); ~GMBench() override; + void modifyGrContextOptions(GrContextOptions* options) override { + return fGM->modifyGrContextOptions(options); + } + protected: const char* onGetName() override; bool isSuitableFor(Backend backend) override; diff --git a/bench/MultitextureImageBench.cpp b/bench/MultitextureImageBench.cpp new file mode 100644 index 0000000000..d001cac774 --- /dev/null +++ b/bench/MultitextureImageBench.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Benchmark.h" +#if SK_SUPPORT_GPU + +#include "GrContextOptions.h" +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkRandom.h" +#include "SkSurface.h" + +class MultitextureImages : public Benchmark { +public: + MultitextureImages(int imageSize, int dstRectSize, bool disableMultitexturing) + : fImageSize(imageSize) + , fDstRectSize(dstRectSize) + , fDisableMultitexturing(disableMultitexturing) { + fName.appendf("multitexture_images_%dx%d_image_%dx%d_rect", imageSize, imageSize, + dstRectSize, dstRectSize); + if (disableMultitexturing) { + fName.append("_disable_multitexturing"); + } + } + + bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } + +protected: + const char* onGetName() override { return fName.c_str(); } + + void onPerCanvasPreDraw(SkCanvas* canvas) override { + auto ii = SkImageInfo::Make(fImageSize, fImageSize, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, nullptr); + SkRandom random; + for (int i = 0; i < kNumImages; ++i) { + auto surf = canvas->makeSurface(ii); + SkColor color = random.nextU(); + surf->getCanvas()->clear(color); + SkPaint paint; + paint.setColor(~color); + paint.setBlendMode(SkBlendMode::kSrc); + surf->getCanvas()->drawRect(SkRect::MakeLTRB(3, 3, fImageSize - 3, fImageSize - 3), + paint); + fImages[i] = surf->makeImageSnapshot(); + } + } + + void onPerCanvasPostDraw(SkCanvas*) override { + for (int i = 0; i < kNumImages; ++i) { + fImages[i].reset(); + } + } + + void onDraw(int loops, SkCanvas* canvas) override { + SkRect rect = SkRect::MakeWH(fDstRectSize, fDstRectSize); + SkPaint paint; + paint.setAlpha(0x40); + paint.setFilterQuality(kLow_SkFilterQuality); + for (int i = 0; i < loops; i++) { + for (int j = 0; j < kNumImages; ++j) { + SkVector translate = this->translation(i * kNumImages + j); + canvas->drawImageRect(fImages[j].get(), rect.makeOffset(translate.fX, translate.fY), + &paint); + } + // Prevent any batching except without multitexturing since we're trying to measure + // drawing distinct images and just repeating images here to increase the workload for + // timing reasons. + canvas->flush(); + } + } + + void modifyGrContextOptions(GrContextOptions* options) override { + options->fDisableImageMultitexturing = fDisableMultitexturing; + } + +private: + SkIPoint onGetSize() override { + // The rows and columns are spaced by kTranslate, but the images may overlap if they are + // larger than kTranslate and extend beyond the last row/column. + return SkIPoint::Make(kTranslate * (kNumColumns - 1) + fDstRectSize, + kTranslate * (kNumRows - 1) + fDstRectSize); + } + + SkVector translation(int i) const { + SkVector offset; + offset.fX = i % kNumColumns * kTranslate; + offset.fY = (i / kNumColumns) % kNumRows * kTranslate; + return offset; + } + + static const int kTranslate = 200; + static const int kNumColumns = 5; + static const int kNumRows = 5; + static const int kNumImages = 8; + + sk_sp<SkImage> fImages[kNumImages]; + SkString fName; + int fImageSize; + int fDstRectSize; + bool fDisableMultitexturing; + + typedef Benchmark INHERITED; +}; + +DEF_BENCH(return new MultitextureImages(128, 32, false)); +DEF_BENCH(return new MultitextureImages(128, 32, true)); +DEF_BENCH(return new MultitextureImages(128, 128, false)); +DEF_BENCH(return new MultitextureImages(128, 128, true)); +DEF_BENCH(return new MultitextureImages(128, 256, false)); +DEF_BENCH(return new MultitextureImages(128, 256, true)); + +DEF_BENCH(return new MultitextureImages(512, 32, false)); +DEF_BENCH(return new MultitextureImages(512, 32, true)); +DEF_BENCH(return new MultitextureImages(512, 128, false)); +DEF_BENCH(return new MultitextureImages(512, 128, true)); +DEF_BENCH(return new MultitextureImages(512, 256, false)); +DEF_BENCH(return new MultitextureImages(512, 256, true)); +DEF_BENCH(return new MultitextureImages(512, 512, false)); +DEF_BENCH(return new MultitextureImages(512, 512, true)); + +#endif diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index 5a976e6640..203ed67073 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -65,9 +65,10 @@ extern bool gSkForceRasterPipelineBlitter; #include "GrContextFactory.h" #include "gl/GrGLUtil.h" #include "SkGr.h" + using sk_gpu_test::ContextInfo; using sk_gpu_test::GrContextFactory; using sk_gpu_test::TestContext; - std::unique_ptr<GrContextFactory> gGrFactory; + GrContextOptions grContextOpts; #endif struct GrContextOptions; @@ -124,7 +125,6 @@ DEFINE_bool(lite, false, "Use SkLiteRecorder in recording benchmarks?"); DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?"); DEFINE_bool(loopSKP, true, "Loop SKPs like we do for micro benches?"); DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run."); -DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each test."); DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?"); DEFINE_bool(gpuStatsDump, false, "Dump GPU states after each benchmark to json"); DEFINE_bool(keepAlive, false, "Print a message every so often so that we don't time out"); @@ -174,44 +174,45 @@ bool Target::capturePixels(SkBitmap* bmp) { #if SK_SUPPORT_GPU struct GPUTarget : public Target { - explicit GPUTarget(const Config& c) : Target(c), context(nullptr) { } - TestContext* context; + explicit GPUTarget(const Config& c) : Target(c) {} + ContextInfo contextInfo; + std::unique_ptr<GrContextFactory> factory; void setup() override { - this->context->makeCurrent(); + this->contextInfo.testContext()->makeCurrent(); // Make sure we're done with whatever came before. - this->context->finish(); + this->contextInfo.testContext()->finish(); } void endTiming() override { - if (this->context) { - this->context->waitOnSyncOrSwap(); + if (this->contextInfo.testContext()) { + this->contextInfo.testContext()->waitOnSyncOrSwap(); } } - void fence() override { - this->context->finish(); - } + void fence() override { this->contextInfo.testContext()->finish(); } bool needsFrameTiming(int* maxFrameLag) const override { - if (!this->context->getMaxGpuFrameLag(maxFrameLag)) { + if (!this->contextInfo.testContext()->getMaxGpuFrameLag(maxFrameLag)) { // Frame lag is unknown. *maxFrameLag = FLAGS_gpuFrameLag; } return true; } bool init(SkImageInfo info, Benchmark* bench) override { + GrContextOptions options = grContextOpts; + bench->modifyGrContextOptions(&options); + this->factory.reset(new GrContextFactory(options)); uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag : 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); - this->surface = SkSurface::MakeRenderTarget(gGrFactory->get(this->config.ctxType, - this->config.ctxOverrides), - SkBudgeted::kNo, info, - this->config.samples, &props); - this->context = gGrFactory->getContextInfo(this->config.ctxType, - this->config.ctxOverrides).testContext(); + this->surface = SkSurface::MakeRenderTarget( + this->factory->get(this->config.ctxType, this->config.ctxOverrides), + SkBudgeted::kNo, info, this->config.samples, &props); + this->contextInfo = + this->factory->getContextInfo(this->config.ctxType, this->config.ctxOverrides); if (!this->surface.get()) { return false; } - if (!this->context->fenceSyncSupport()) { + if (!this->contextInfo.testContext()->fenceSyncSupport()) { SkDebugf("WARNING: GL context for config \"%s\" does not support fence sync. " "Timings might not be accurate.\n", this->config.name.c_str()); } @@ -219,9 +220,9 @@ struct GPUTarget : public Target { } void fillOptions(ResultsWriter* log) override { const GrGLubyte* version; - if (this->context->backend() == kOpenGL_GrBackend) { - const GrGLInterface* gl = - reinterpret_cast<const GrGLInterface*>(this->context->backendContext()); + if (this->contextInfo.backend() == kOpenGL_GrBackend) { + const GrGLInterface* gl = reinterpret_cast<const GrGLInterface*>( + this->contextInfo.testContext()->backendContext()); GR_GL_CALL_RET(gl, version, GetString(GR_GL_VERSION)); log->configOption("GL_VERSION", (const char*)(version)); @@ -235,6 +236,11 @@ struct GPUTarget : public Target { log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); } } + + void dumpStats() override { + this->contextInfo.grContext()->printCacheStats(); + this->contextInfo.grContext()->printGpuStats(); + } }; #endif @@ -394,7 +400,6 @@ static int setup_gpu_bench(Target* target, Benchmark* bench, int maxGpuFrameLag) } else { loops = detect_forever_loops(loops); } - // Pretty much the same deal as the calibration: do some warmup to make // sure we're timing steady-state pipelined frames. for (int i = 0; i < maxGpuFrameLag; i++) { @@ -427,7 +432,8 @@ static void create_config(const SkCommandLineConfig* config, SkTArray<Config>* c const auto colorType = gpuConfig->getColorType(); auto colorSpace = gpuConfig->getColorSpace(); - if (const GrContext* ctx = gGrFactory->get(ctxType, ctxOverrides)) { + GrContextFactory factory(grContextOpts); + if (const GrContext* ctx = factory.get(ctxType, ctxOverrides)) { GrPixelConfig grPixConfig = SkImageInfo2GrPixelConfig(colorType, colorSpace, *ctx->caps()); int supportedSampleCount = ctx->caps()->getSampleCount(sampleCount, grPixConfig); @@ -578,14 +584,6 @@ static bool valid_brd_bench(sk_sp<SkData> encoded, SkColorType colorType, uint32 static void cleanup_run(Target* target) { delete target; -#if SK_SUPPORT_GPU - if (FLAGS_abandonGpuContext) { - gGrFactory->abandonContexts(); - } - if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) { - gGrFactory->destroyContexts(); - } -#endif } static void collect_files(const SkCommandLineFlags::StringArray& paths, const char* ext, @@ -1141,10 +1139,8 @@ int main(int argc, char** argv) { SkTaskGroup::Enabler enabled(FLAGS_threads); #if SK_SUPPORT_GPU - GrContextOptions grContextOpts; grContextOpts.fGpuPathRenderers = CollectGpuPathRenderersFromFlags(); grContextOpts.fExecutor = GpuExecutorForTools(); - gGrFactory.reset(new GrContextFactory(grContextOpts)); #endif if (FLAGS_veryVerbose) { @@ -1401,10 +1397,7 @@ int main(int argc, char** argv) { #if SK_SUPPORT_GPU if (FLAGS_gpuStats && Benchmark::kGPU_Backend == configs[i].backend) { - GrContext* context = gGrFactory->get(configs[i].ctxType, - configs[i].ctxOverrides); - context->printCacheStats(); - context->printGpuStats(); + target->dumpStats(); } #endif @@ -1425,11 +1418,5 @@ int main(int argc, char** argv) { log->config("meta"); log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB()); -#if SK_SUPPORT_GPU - // Make sure we clean up the global GrContextFactory here, otherwise we might race with the - // SkEventTracer destructor - gGrFactory.reset(nullptr); -#endif - return 0; } diff --git a/bench/nanobench.h b/bench/nanobench.h index ffd6893c4a..45882cc043 100644 --- a/bench/nanobench.h +++ b/bench/nanobench.h @@ -79,6 +79,9 @@ struct Target { /** Writes any config-specific data to the log. */ virtual void fillOptions(ResultsWriter*) { } + /** Writes gathered stats using SkDebugf. */ + virtual void dumpStats() {} + SkCanvas* getCanvas() const { if (!surface.get()) { return nullptr; diff --git a/gn/bench.gni b/gn/bench.gni index 4bd46287db..36569e5613 100644 --- a/gn/bench.gni +++ b/gn/bench.gni @@ -78,6 +78,7 @@ bench_sources = [ "$_bench/MergeBench.cpp", "$_bench/MipMapBench.cpp", "$_bench/MorphologyBench.cpp", + "$_bench/MultitextureImageBench.cpp", "$_bench/MutexBench.cpp", "$_bench/pack_int_uint16_t_Bench.cpp", "$_bench/PatchBench.cpp", diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h index ac4de03ed8..82a4be2c25 100644 --- a/include/gpu/GrCaps.h +++ b/include/gpu/GrCaps.h @@ -225,6 +225,9 @@ protected: // Vulkan doesn't support this (yet) and some drivers have issues, too bool fCrossContextTextureSupport : 1; + // Disables using multiple texture units to batch multiple SkImages at once. + bool fDisableImageMultitexturingSupport : 1; + InstancedSupport fInstancedSupport; BlendEquationSupport fBlendEquationSupport; diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h index 85c8da6e52..61dc8b0457 100644 --- a/include/gpu/GrContextOptions.h +++ b/include/gpu/GrContextOptions.h @@ -118,6 +118,12 @@ struct GrContextOptions { * Include or exclude specific GPU path renderers. */ GpuPathRenderers fGpuPathRenderers = GpuPathRenderers::kDefault; + + /** + * Disables using multiple texture units to batch multiple images into a single draw on + * supported GPUs. + */ + bool fDisableImageMultitexturing = false; #endif }; diff --git a/include/gpu/GrShaderCaps.h b/include/gpu/GrShaderCaps.h index b10ad60b5a..b093f37f28 100644 --- a/include/gpu/GrShaderCaps.h +++ b/include/gpu/GrShaderCaps.h @@ -252,6 +252,8 @@ public: int maxCombinedImageStorages() const { return fMaxCombinedImageStorages; } + bool disableImageMultitexturingSupport() const { return fDisableImageMultitexturing; } + /** * Given a texture's config, this determines what swizzle must be appended to accesses to the * texture in generated shader code. Swizzling may be implemented in texture parameters or a @@ -306,6 +308,7 @@ private: bool fExternalTextureSupport : 1; bool fTexelFetchSupport : 1; bool fVertexIDSupport : 1; + bool fDisableImageMultitexturing : 1; // Used for specific driver bug work arounds bool fCanUseMinAndAbsTogether : 1; diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp index 0ee9339b45..7e44188074 100644 --- a/src/gpu/GrShaderCaps.cpp +++ b/src/gpu/GrShaderCaps.cpp @@ -96,6 +96,12 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) { fMaxFragmentImageStorages = 0; fMaxCombinedImageStorages = 0; fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction; + +#if GR_TEST_UTILS + fDisableImageMultitexturing = options.fDisableImageMultitexturing; +#else + fDisableImageMultitexturing = false; +#endif } void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { @@ -175,6 +181,7 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { writer->appendS32("Max Combined Image Storages", fMaxFragmentImageStorages); writer->appendString("Advanced blend equation interaction", kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]); + writer->appendBool("Disable image multitexturing", fDisableImageMultitexturing); writer->endObject(); } diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp index ab82a9cc87..3606a01096 100644 --- a/src/gpu/ops/GrTextureOp.cpp +++ b/src/gpu/ops/GrTextureOp.cpp @@ -54,7 +54,9 @@ public: static constexpr int kMaxTextures = 8; #endif - static int SupportsMultitexture(const GrShaderCaps& caps) { return caps.integerSupport(); } + static int SupportsMultitexture(const GrShaderCaps& caps) { + return caps.integerSupport() && !caps.disableImageMultitexturingSupport(); + } static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt, sk_sp<GrColorSpaceXform> csxf, diff --git a/tools/flags/SkCommonFlags.h b/tools/flags/SkCommonFlags.h index ef1897b7bc..3d4b33e640 100644 --- a/tools/flags/SkCommonFlags.h +++ b/tools/flags/SkCommonFlags.h @@ -20,7 +20,6 @@ DECLARE_string(colorImages); DECLARE_bool(simpleCodec); DECLARE_string(match); DECLARE_bool(quiet); -DECLARE_bool(resetGpuContext); DECLARE_bool(preAbandonGpuContext); DECLARE_bool(abandonGpuContext); DECLARE_bool(releaseAndAbandonGpuContext); |