Android HWUI backend Nanobench

Uses filtering canvas from utils/android, shared with DM.
Follow-up plans in https://skbug.com/3589, https://skbug.com/3595

R=djsollen@google.com

Review URL: https://codereview.chromium.org/1029423010
diff --git a/bench/Benchmark.h b/bench/Benchmark.h
index c30167e..5ba6e91 100644
--- a/bench/Benchmark.h
+++ b/bench/Benchmark.h
@@ -57,6 +57,7 @@
         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 5e05932..4bc1484 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 @@
 }
 #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 @@
     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 @@
     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 @@
             // _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 @@
         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 @@
     // 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 @@
     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 @@
 #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 @@
     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 @@
     }
 }
 
-#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 @@
             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 @@
             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 0000000..a38b5f5
--- /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 0000000..8bda1c8
--- /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 0000000..3baeb12
--- /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