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>
This commit is contained in:
Brian Salomon 2017-10-11 15:34:27 -04:00 committed by Skia Commit-Bot
parent ba1c7901a7
commit 0b4d8aa108
12 changed files with 190 additions and 47 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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",

View File

@ -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;

View File

@ -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
};

View File

@ -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;

View File

@ -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();
}

View File

@ -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,

View File

@ -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);