From eebc39ad5a1dcbde1b10a9b2ab54679549522b52 Mon Sep 17 00:00:00 2001 From: tomhudson Date: Mon, 23 Feb 2015 12:18:05 -0800 Subject: Add HWUI Sink to DM on Android Framework builds Allows "hwui" as a --config argument to dm, drawing through the Android Framework's HWUI backend. R=djsollen@google.com,mtklein@google.com BUG=skia: Review URL: https://codereview.chromium.org/943393002 --- dm/DM.cpp | 5 ++ dm/DMSrcSinkAndroid.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++++++ dm/DMSrcSinkAndroid.h | 38 ++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 dm/DMSrcSinkAndroid.cpp create mode 100644 dm/DMSrcSinkAndroid.h (limited to 'dm') diff --git a/dm/DM.cpp b/dm/DM.cpp index fbfd9188cf..1c6d3222c2 100644 --- a/dm/DM.cpp +++ b/dm/DM.cpp @@ -1,6 +1,7 @@ #include "CrashHandler.h" #include "DMJsonWriter.h" #include "DMSrcSink.h" +#include "DMSrcSinkAndroid.h" #include "OverwriteLine.h" #include "ProcStats.h" #include "SkBBHFactory.h" @@ -224,6 +225,10 @@ static Sink* create_sink(const char* tag) { #endif } +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + SINK("hwui", HWUISink); +#endif + if (FLAGS_cpu) { SINK("565", RasterSink, kRGB_565_SkColorType); SINK("8888", RasterSink, kN32_SkColorType); diff --git a/dm/DMSrcSinkAndroid.cpp b/dm/DMSrcSinkAndroid.cpp new file mode 100644 index 0000000000..1ad9329669 --- /dev/null +++ b/dm/DMSrcSinkAndroid.cpp @@ -0,0 +1,158 @@ +/* + * 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 "DMSrcSink.h" +#include "DMSrcSinkAndroid.h" + +#include "AnimationContext.h" +#include "DisplayListRenderer.h" +#include "IContextFactory.h" +#include "RenderNode.h" +#include "android/rect.h" +#include "android/native_window.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 "renderthread/TimeLord.h" + +namespace DM { + +/* These functions are only compiled in the Android Framework. */ + +class ContextFactory : public android::uirenderer::IContextFactory { +public: + android::uirenderer::AnimationContext* createAnimationContext + (android::uirenderer::renderthread::TimeLord& clock) SK_OVERRIDE { + return new android::uirenderer::AnimationContext(clock); + } +}; + +Error HWUISink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const { + // Do all setup in this function because we don't know the size + // for the RenderNode and RenderProxy during the constructor. + // In practice this doesn't seem too expensive. + const SkISize size = src.size(); + + // Based on android::SurfaceTexture_init() + android::sp producer; + android::sp consumer; + android::BufferQueue::createBufferQueue(&producer, &consumer); + + // Consumer setup + + android::sp cpuConsumer = + new android::CpuConsumer(consumer, 1); + cpuConsumer->setName(android::String8("SkiaTestClient")); + cpuConsumer->setDefaultBufferSize(size.width(), size.height()); + + // Producer setup + + android::sp surface = new android::Surface(producer); + native_window_set_buffers_dimensions(surface.get(), size.width(), size.height()); + native_window_set_buffers_format(surface.get(), android::PIXEL_FORMAT_RGBA_8888); + native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_NEVER | + GRALLOC_USAGE_HW_RENDER); + + // RenderNode setup based on hwui/tests/main.cpp:TreeContentAnimation + SkAutoTDelete rootNode + (new android::uirenderer::RenderNode()); + rootNode->incStrong(nullptr); + + // Values set here won't be applied until the framework has called + // RenderNode::pushStagingPropertiesChanges() during RenderProxy::syncAndDrawFrame(). + rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, size.width(), size.height()); + rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::X | + android::uirenderer::RenderNode::Y); + rootNode->mutateStagingProperties().setClipToBounds(false); + rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC); + + // RenderProxy setup based on hwui/tests/main.cpp:TreeContentAnimation + ContextFactory factory; + SkAutoTDelete proxy + (new android::uirenderer::renderthread::RenderProxy(false, rootNode, &factory)); + proxy->loadSystemProperties(); + + proxy->initialize(surface.get()); + + float lightX = size.width() / 2.0f; + android::uirenderer::Vector3 lightVector { lightX, dp(-200.0f), dp(800.0f) }; + proxy->setup(size.width(), size.height(), lightVector, dp(800.0f), 255 * 0.075f, 255 * 0.15f, + kDensity); + + // Do the draw + + SkAutoTDelete renderer + (new android::uirenderer::DisplayListRenderer()); + renderer->setViewport(size.width(), size.height()); + renderer->prepare(); + renderer->clipRect(0, 0, size.width(), size.height(), SkRegion::Op::kReplace_Op); + + Error err = src.draw(renderer->asSkCanvas()); + if (!err.isEmpty()) { + return err; + } + + renderer->finish(); + rootNode->setStagingDisplayList(renderer->finishRecording()); + + proxy->syncAndDrawFrame(); + proxy->fence(); + + // Capture pixels + + SkImageInfo destinationConfig = + SkImageInfo::Make(size.width(), size.height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + dst->allocPixels(destinationConfig); + sk_memset32((uint32_t*) dst->getPixels(), SK_ColorRED, size.width() * size.height()); + + android::CpuConsumer::LockedBuffer nativeBuffer; + android::status_t retval = cpuConsumer->lockNextBuffer(&nativeBuffer); + if (retval == android::BAD_VALUE) { + SkDebugf("HWUISink::draw() 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. + dst->eraseColor(SK_ColorTRANSPARENT); + return ""; + } else if (retval) { + return SkStringPrintf("Failed to lock buffer to read pixels: %d.", retval); + } + + // 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) { + return "Failed to wrap HWUI buffer in a SkBitmap"; + } + + SK_ALWAYSBREAK(dst->colorType() == kRGBA_8888_SkColorType && + "Destination buffer not RGBA!"); + success = + nativeWrapper.readPixels(destinationConfig, dst->getPixels(), dst->rowBytes(), 0, 0); + if (!success) { + return "Failed to extract pixels from HWUI buffer"; + } + + cpuConsumer->unlockBuffer(nativeBuffer); + return ""; +} + +} // namespace DM diff --git a/dm/DMSrcSinkAndroid.h b/dm/DMSrcSinkAndroid.h new file mode 100644 index 0000000000..1c5fe71f18 --- /dev/null +++ b/dm/DMSrcSinkAndroid.h @@ -0,0 +1,38 @@ +/* + * 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 DMSrcSinkAndroid_DEFINED +#define DMSrcSinkAndroid_DEFINED + +#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK + +#include "DMSrcSink.h" + +namespace DM { + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +class HWUISink : public Sink { +public: + HWUISink() { } + + Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE; + int enclave() const SK_OVERRIDE { return kGPU_Enclave; } + const char* fileExtension() const SK_OVERRIDE { return "png"; } + +private: + const float kDensity = 1.0f; + inline float dp(int x) const { return x * kDensity; } +}; + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +} // namespace DM + +#endif // SK_BUILD_FOR_ANDROID_FRAMEWORK + +#endif // DMSrcSinkAndroid_DEFINED -- cgit v1.2.3