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:
parent
ba1c7901a7
commit
0b4d8aa108
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
125
bench/MultitextureImageBench.cpp
Normal file
125
bench/MultitextureImageBench.cpp
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user