diff options
-rw-r--r-- | bench/Benchmark.h | 1 | ||||
-rw-r--r-- | bench/nanobench.cpp | 232 | ||||
-rw-r--r-- | bench/nanobench.h | 79 | ||||
-rw-r--r-- | bench/nanobenchAndroid.cpp | 160 | ||||
-rw-r--r-- | bench/nanobenchAndroid.h | 49 | ||||
-rw-r--r-- | gyp/bench.gyp | 14 |
6 files changed, 438 insertions, 97 deletions
diff --git a/bench/Benchmark.h b/bench/Benchmark.h index c30167e572..5ba6e91108 100644 --- a/bench/Benchmark.h +++ b/bench/Benchmark.h @@ -57,6 +57,7 @@ public: kRaster_Backend, kGPU_Backend, kPDF_Backend, + kHWUI_Backend, }; // Call to determine whether the benchmark is intended for diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index 5e059328ad..4bc1484da0 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -7,6 +7,8 @@ #include <ctype.h> +#include "nanobench.h" + #include "Benchmark.h" #include "CrashHandler.h" #include "DecodingBench.h" @@ -32,6 +34,10 @@ #include "SkSurface.h" #include "SkTaskGroup.h" +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + #include "nanobenchAndroid.h" +#endif + #if SK_SUPPORT_GPU #include "gl/GrGLDefines.h" #include "GrContextFactory.h" @@ -86,24 +92,100 @@ static SkString humanize(double ms) { } #define HUMANIZE(ms) humanize(ms).c_str() -static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContext* gl) { +bool Target::init(SkImageInfo info, Benchmark* bench) { + if (Benchmark::kRaster_Backend == config.backend) { + this->surface.reset(SkSurface::NewRaster(info)); + if (!this->surface.get()) { + return false; + } + } + return true; +} +bool Target::capturePixels(SkBitmap* bmp) { + if (!this->surface.get()) { + return false; + } + SkCanvas* canvas = this->surface->getCanvas(); + if (!canvas) { + return false; + } + bmp->setInfo(canvas->imageInfo()); + if (!canvas->readPixels(bmp, 0, 0)) { + SkDebugf("Can't read canvas pixels.\n"); + return false; + } + return true; +} + +#if SK_SUPPORT_GPU +struct GPUTarget : public Target { + explicit GPUTarget(const Config& c) : Target(c), gl(NULL) { } + SkGLContext* gl; + + void setup() override { + this->gl->makeCurrent(); + // Make sure we're done with whatever came before. + SK_GL(*this->gl, Finish()); + } + void endTiming() override { + if (this->gl) { + SK_GL(*this->gl, Flush()); + this->gl->swapBuffers(); + } + } + void fence() override { + SK_GL(*this->gl, Finish()); + } + + bool needsFrameTiming() const override { return true; } + bool init(SkImageInfo info, Benchmark* bench) override { + uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; + SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); + this->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(this->config.ctxType), + SkSurface::kNo_Budgeted, info, + this->config.samples, &props)); + this->gl = gGrFactory->getGLContext(this->config.ctxType); + if (!this->surface.get()) { + return false; + } + return true; + } + void fillOptions(ResultsWriter* log) override { + const GrGLubyte* version; + SK_GL_RET(*this->gl, version, GetString(GR_GL_VERSION)); + log->configOption("GL_VERSION", (const char*)(version)); + + SK_GL_RET(*this->gl, version, GetString(GR_GL_RENDERER)); + log->configOption("GL_RENDERER", (const char*) version); + + SK_GL_RET(*this->gl, version, GetString(GR_GL_VENDOR)); + log->configOption("GL_VENDOR", (const char*) version); + + SK_GL_RET(*this->gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION)); + log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); + } +}; + +#endif + +static double time(int loops, Benchmark* bench, SkCanvas* canvas, Target* target) { if (canvas) { canvas->clear(SK_ColorWHITE); } WallTimer timer; timer.start(); + if (target) { + canvas = target->beginTiming(canvas); + } if (bench) { bench->draw(loops, canvas); } if (canvas) { canvas->flush(); } -#if SK_SUPPORT_GPU - if (gl) { - SK_GL(*gl, Flush()); - gl->swapBuffers(); + if (target) { + target->endTiming(); } -#endif timer.end(); return timer.fWall; } @@ -137,19 +219,22 @@ static int clamp_loops(int loops) { return loops; } -static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) { +static bool write_canvas_png(Target* target, const SkString& filename) { + if (filename.isEmpty()) { return false; } - if (kUnknown_SkColorType == canvas->imageInfo().colorType()) { + if (target->surface.get() && target->surface->getCanvas() && + kUnknown_SkColorType == target->surface->getCanvas()->imageInfo().colorType()) { return false; } + SkBitmap bmp; - bmp.setInfo(canvas->imageInfo()); - if (!canvas->readPixels(&bmp, 0, 0)) { - SkDebugf("Can't read canvas pixels.\n"); + + if (!target->capturePixels(&bmp)) { return false; } + SkString dir = SkOSPath::Dirname(filename.c_str()); if (!sk_mkdir(dir.c_str())) { SkDebugf("Can't make dir %s.\n", dir.c_str()); @@ -215,14 +300,7 @@ static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, return loops; } -#if SK_SUPPORT_GPU -static void setup_gl(SkGLContext* gl) { - gl->makeCurrent(); - // Make sure we're done with whatever came before. - SK_GL(*gl, Finish()); -} - -static int gpu_bench(SkGLContext* gl, +static int gpu_bench(Target* target, Benchmark* bench, SkCanvas* canvas, double* samples) { @@ -242,7 +320,7 @@ static int gpu_bench(SkGLContext* gl, // _this_ round, not still timing last round. We force this by looping // more times than any reasonable GPU will allow frames to lag. for (int i = 0; i < FLAGS_gpuFrameLag; i++) { - elapsed = time(loops, bench, canvas, gl); + elapsed = time(loops, bench, canvas, target); } } while (elapsed < FLAGS_gpuMs); @@ -250,8 +328,8 @@ static int gpu_bench(SkGLContext* gl, loops = (int)ceil(loops * FLAGS_gpuMs / elapsed); loops = clamp_loops(loops); - // Might as well make sure we're not still timing our calibration. - SK_GL(*gl, Finish()); + // Make sure we're not still timing our calibration. + target->fence(); } else { loops = detect_forever_loops(loops); } @@ -259,16 +337,16 @@ static int gpu_bench(SkGLContext* gl, // 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 < FLAGS_gpuFrameLag; i++) { - time(loops, bench, canvas, gl); + time(loops, bench, canvas, target); } // Now, actually do the timing! for (int i = 0; i < FLAGS_samples; i++) { - samples[i] = time(loops, bench, canvas, gl) / loops; + samples[i] = time(loops, bench, canvas, target) / loops; } + return loops; } -#endif static SkString to_lower(const char* str) { SkString lower(str); @@ -278,30 +356,6 @@ static SkString to_lower(const char* str) { return lower; } -struct Config { - const char* name; - Benchmark::Backend backend; - SkColorType color; - SkAlphaType alpha; - int samples; -#if SK_SUPPORT_GPU - GrContextFactory::GLContextType ctxType; - bool useDFText; -#else - int bogusInt; - bool bogusBool; -#endif -}; - -struct Target { - explicit Target(const Config& c) : config(c) {} - const Config config; - SkAutoTDelete<SkSurface> surface; -#if SK_SUPPORT_GPU - SkGLContext* gl; -#endif -}; - static bool is_cpu_config_allowed(const char* name) { for (int i = 0; i < FLAGS_config.count(); i++) { if (to_lower(FLAGS_config[i]).equals(name)) { @@ -373,6 +427,14 @@ static void create_configs(SkTDArray<Config>* configs) { #endif } #endif + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + if (is_cpu_config_allowed("hwui")) { + Config config = { "hwui", Benchmark::kHWUI_Backend, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, 0, kBogusGLContextType, false }; + configs->push(config); + } +#endif } // If bench is enabled for config, returns a Target* for it, otherwise NULL. @@ -384,23 +446,25 @@ static Target* is_enabled(Benchmark* bench, const Config& config) { SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY, config.color, config.alpha); - Target* target = new Target(config); + Target* target = NULL; - if (Benchmark::kRaster_Backend == config.backend) { - target->surface.reset(SkSurface::NewRaster(info)); - } + switch (config.backend) { #if SK_SUPPORT_GPU - else if (Benchmark::kGPU_Backend == config.backend) { - uint32_t flags = config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; - SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); - target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType), - SkSurface::kNo_Budgeted, info, - config.samples, &props)); - target->gl = gGrFactory->getGLContext(config.ctxType); - } + case Benchmark::kGPU_Backend: + target = new GPUTarget(config); + break; +#endif +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + case Benchmark::kHWUI_Backend: + target = new HWUITarget(config, bench); + break; #endif + default: + target = new Target(config); + break; + } - if (Benchmark::kNonRendering_Backend != config.backend && !target->surface.get()) { + if (!target->init(info, bench)) { delete target; return NULL; } @@ -418,22 +482,6 @@ static void create_targets(SkTDArray<Target*>* targets, Benchmark* b, } } -#if SK_SUPPORT_GPU -static void fill_gpu_options(ResultsWriter* log, SkGLContext* ctx) { - const GrGLubyte* version; - SK_GL_RET(*ctx, version, GetString(GR_GL_VERSION)); - log->configOption("GL_VERSION", (const char*)(version)); - - SK_GL_RET(*ctx, version, GetString(GR_GL_RENDERER)); - log->configOption("GL_RENDERER", (const char*) version); - - SK_GL_RET(*ctx, version, GetString(GR_GL_VENDOR)); - log->configOption("GL_VENDOR", (const char*) version); - - SK_GL_RET(*ctx, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION)); - log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); -} -#endif class BenchmarkStream { public: @@ -790,32 +838,26 @@ int nanobench_main() { bench->preDraw(); } for (int j = 0; j < targets.count(); j++) { + // During HWUI output this canvas may be NULL. SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL; const char* config = targets[j]->config.name; -#if SK_SUPPORT_GPU - if (Benchmark::kGPU_Backend == targets[j]->config.backend) { - setup_gl(targets[j]->gl); - } -#endif - + targets[j]->setup(); bench->perCanvasPreDraw(canvas); const int loops = -#if SK_SUPPORT_GPU - Benchmark::kGPU_Backend == targets[j]->config.backend - ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) - : -#endif - cpu_bench( overhead, bench.get(), canvas, samples.get()); + targets[j]->needsFrameTiming() + ? gpu_bench(targets[j], bench.get(), canvas, samples.get()) + : cpu_bench(overhead, bench.get(), canvas, samples.get()); bench->perCanvasPostDraw(canvas); - if (canvas && !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { + if (Benchmark::kNonRendering_Backend != targets[j]->config.backend && + !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); pngFilename.append(".png"); - write_canvas_png(canvas, pngFilename); + write_canvas_png(targets[j], pngFilename); } if (kFailedLoops == loops) { @@ -827,11 +869,7 @@ int nanobench_main() { log->config(config); log->configOption("name", bench->getName()); benchStream.fillCurrentOptions(log.get()); -#if SK_SUPPORT_GPU - if (Benchmark::kGPU_Backend == targets[j]->config.backend) { - fill_gpu_options(log.get(), targets[j]->gl); - } -#endif + targets[j]->fillOptions(log.get()); log->metric("min_ms", stats.min); if (runs++ % FLAGS_flushEvery == 0) { log->flush(); diff --git a/bench/nanobench.h b/bench/nanobench.h new file mode 100644 index 0000000000..a38b5f5772 --- /dev/null +++ b/bench/nanobench.h @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef nanobench_DEFINED +#define nanobench_DEFINED + +#include "Benchmark.h" +#include "SkImageInfo.h" +#include "SkSurface.h" +#include "SkTypes.h" + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#endif + +class ResultsWriter; +class SkBitmap; +class SkCanvas; + +struct Config { + const char* name; + Benchmark::Backend backend; + SkColorType color; + SkAlphaType alpha; + int samples; +#if SK_SUPPORT_GPU + GrContextFactory::GLContextType ctxType; + bool useDFText; +#else + int bogusInt; + bool bogusBool; +#endif +}; + +struct Target { + explicit Target(const Config& c) : config(c) { } + virtual ~Target() { } + + const Config config; + SkAutoTDelete<SkSurface> surface; + + /** Called once per target, immediately before any timing or drawing. */ + virtual void setup() { } + + /** Called *after* the clock timer is started, before the benchmark + is drawn. */ + virtual SkCanvas* beginTiming(SkCanvas* canvas) { return canvas; } + + /** Called *after* a benchmark is drawn, but before the clock timer + is stopped. */ + virtual void endTiming() { } + + /** Called between benchmarks (or between calibration and measured + runs) to make sure all pending work in drivers / threads is + complete. */ + virtual void fence() { } + + /** CPU-like targets can just be timed, but GPU-like + targets need to pay attention to frame boundaries + or other similar details. */ + virtual bool needsFrameTiming() const { return false; } + + /** Called once per target, during program initialization. + Returns false if initialization fails. */ + virtual bool init(SkImageInfo info, Benchmark* bench); + + /** Stores any pixels drawn to the screen in the bitmap. + Returns false on error. */ + virtual bool capturePixels(SkBitmap* bmp); + + /** Writes any config-specific data to the log. */ + virtual void fillOptions(ResultsWriter*) { } +}; + +#endif // nanobench_DEFINED diff --git a/bench/nanobenchAndroid.cpp b/bench/nanobenchAndroid.cpp new file mode 100644 index 0000000000..8bda1c8625 --- /dev/null +++ b/bench/nanobenchAndroid.cpp @@ -0,0 +1,160 @@ +/* + * 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 "nanobenchAndroid.h" + +#include "AnimationContext.h" +#include "IContextFactory.h" +#include "SkiaCanvasProxy.h" +#include "android/rect.h" +#include "android/native_window.h" +#include "renderthread/TimeLord.h" + +namespace { + +/** + * Helper class for setting up android::uirenderer::renderthread::RenderProxy. + */ +class ContextFactory : public android::uirenderer::IContextFactory { +public: + android::uirenderer::AnimationContext* createAnimationContext + (android::uirenderer::renderthread::TimeLord& clock) override { + return new android::uirenderer::AnimationContext(clock); + } +}; + +} + +HWUITarget::HWUITarget(const Config& c, Benchmark* bench) : Target(c) { } + +void HWUITarget::setup() { + this->proxy->fence(); +} + +SkCanvas* HWUITarget::beginTiming(SkCanvas* canvas) { + this->renderer->prepare(); + this->renderer->clipRect(0, 0, this->size.width(), this->size.height(), + SkRegion::Op::kReplace_Op); + SkCanvas* targetCanvas = this->renderer->asSkCanvas(); + if (targetCanvas) { + this->fc.reset(targetCanvas); + canvas = &this->fc; + // This might minimally distort timing, but canvas isn't valid outside the timer. + canvas->clear(SK_ColorWHITE); + } + return canvas; +} + +void HWUITarget::endTiming() { + this->renderer->finish(); + this->rootNode->setStagingDisplayList(this->renderer->finishRecording()); + this->proxy->syncAndDrawFrame(); + // Surprisingly, calling this->proxy->fence() here appears to make no difference to + // the timings we record. +} + +void HWUITarget::fence() { + this->proxy->fence(); +} + +bool HWUITarget::needsFrameTiming() const { + return true; +} + +bool HWUITarget::init(SkImageInfo info, Benchmark* bench) { + // extracted from DMSrcSinkAndroid.cpp's HWUISink::draw() + size.set(bench->getSize().x(), bench->getSize().y()); + android::BufferQueue::createBufferQueue(&this->producer, &this->consumer); + this->cpuConsumer = new android::CpuConsumer(this->consumer, 1); + this->cpuConsumer->setName(android::String8("SkiaBenchmarkClient")); + this->cpuConsumer->setDefaultBufferSize(size.width(), size.height()); + this->androidSurface = new android::Surface(this->producer); + native_window_set_buffers_dimensions(this->androidSurface.get(), + size.width(), size.height()); + native_window_set_buffers_format(this->androidSurface.get(), + android::PIXEL_FORMAT_RGBA_8888); + native_window_set_usage(this->androidSurface.get(), GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_NEVER | + GRALLOC_USAGE_HW_RENDER); + this->rootNode.reset(new android::uirenderer::RenderNode()); + this->rootNode->incStrong(nullptr); + this->rootNode->mutateStagingProperties().setLeftTopRightBottom + (0, 0, size.width(), size.height()); + this->rootNode->mutateStagingProperties().setClipToBounds(false); + this->rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC); + ContextFactory factory; + this->proxy.reset + (new android::uirenderer::renderthread::RenderProxy(false, this->rootNode, &factory)); + this->proxy->loadSystemProperties(); + this->proxy->initialize(this->androidSurface.get()); + float lightX = size.width() / 2.0f; + android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f }; + this->proxy->setup(size.width(), size.height(), lightVector, 800.0f, + 255 * 0.075f, 255 * 0.15f); + this->renderer.reset(new android::uirenderer::DisplayListRenderer()); + this->renderer->setViewport(size.width(), size.height()); + + // Since we have no SkSurface for HWUI, other parts of the code base have to + // explicitly work around the fact that it may be invalid / have no SkCanvas. + + return true; +} + +bool HWUITarget::capturePixels(SkBitmap* bmp) { + SkImageInfo destinationConfig = + SkImageInfo::Make(this->size.width(), this->size.height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + bmp->allocPixels(destinationConfig); + sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, + this->size.width() * this->size.height()); + + android::CpuConsumer::LockedBuffer nativeBuffer; + android::status_t retval = this->cpuConsumer->lockNextBuffer(&nativeBuffer); + if (retval == android::BAD_VALUE) { + SkDebugf("write_canvas_png() got no buffer; returning transparent"); + // No buffer ready to read - commonly triggered by dm sending us + // a no-op source, or calling code that doesn't do anything on this + // backend. + bmp->eraseColor(SK_ColorTRANSPARENT); + return false; + } else if (retval) { + SkDebugf("Failed to lock buffer to read pixels: %d.", retval); + return false; + } + + // Move the pixels into the destination SkBitmap + + SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 && + "Native buffer not RGBA!"); + SkImageInfo nativeConfig = + SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height, + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + + // Android stride is in pixels, Skia stride is in bytes + SkBitmap nativeWrapper; + bool success = + nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4); + if (!success) { + SkDebugf("Failed to wrap HWUI buffer in a SkBitmap"); + return false; + } + + SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType && + "Destination buffer not RGBA!"); + success = + nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0); + if (!success) { + SkDebugf("Failed to extract pixels from HWUI buffer"); + return false; + } + + this->cpuConsumer->unlockBuffer(nativeBuffer); + + return true; +} + + diff --git a/bench/nanobenchAndroid.h b/bench/nanobenchAndroid.h new file mode 100644 index 0000000000..3baeb1270f --- /dev/null +++ b/bench/nanobenchAndroid.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef nanobenchAndroid_DEFINED +#define nanobenchAndroid_DEFINED + +#include "DisplayListRenderer.h" +#include "RenderNode.h" +#include "SkAndroidSDKCanvas.h" +#include "gui/BufferQueue.h" +#include "gui/CpuConsumer.h" +#include "gui/IGraphicBufferConsumer.h" +#include "gui/IGraphicBufferProducer.h" +#include "gui/Surface.h" +#include "renderthread/RenderProxy.h" + +#include "nanobench.h" + +struct HWUITarget : public Target { + explicit HWUITarget(const Config& c, Benchmark* bench); + + SkAutoTDelete<android::uirenderer::RenderNode> rootNode; + SkAutoTDelete<android::uirenderer::renderthread::RenderProxy> proxy; + SkAutoTDelete<android::uirenderer::DisplayListRenderer> renderer; + android::sp<android::IGraphicBufferProducer> producer; + android::sp<android::IGraphicBufferConsumer> consumer; + android::sp<android::CpuConsumer> cpuConsumer; + android::sp<android::Surface> androidSurface; + SkISize size; + SkAndroidSDKCanvas fc; + + void setup() override; + SkCanvas* beginTiming(SkCanvas* canvas) override; + void endTiming() override; + void fence() override; + bool needsFrameTiming() const override; + + /// Returns false if initialization fails + bool init(SkImageInfo info, Benchmark* bench) override; + bool capturePixels(SkBitmap* bmp) override; +}; + + + +#endif // nanobenchAndroid_DEFINED diff --git a/gyp/bench.gyp b/gyp/bench.gyp index b4442918a6..1370b5383a 100644 --- a/gyp/bench.gyp +++ b/gyp/bench.gyp @@ -37,6 +37,20 @@ ['skia_android_framework', { 'libraries': [ '-lskia', + '-landroid', + '-lgui', + '-lhwui', + '-lutils', + ], + 'include_dirs': [ + '../../../frameworks/base/libs/hwui/', + '../../../frameworks/native/include/', + ], + 'sources': [ + '../bench/nanobenchAndroid.cpp', + ], + 'dependencies': [ + 'utils.gyp:android_utils', ], }], ], |