From 59eecb526adc5bd7041e7b6147bfcc40dd2c200e Mon Sep 17 00:00:00 2001 From: sergeyv Date: Thu, 17 Nov 2016 17:54:57 -0800 Subject: [PATCH] Support readback from hardware bitmaps Test: hwuimacro readbackFromHBitmap --onscreen. bug:30999911 Change-Id: I369c069c40cb0f9adae5a94501815f29c2d7df0f --- libs/hwui/OpenGLReadback.cpp | 22 +++++-- libs/hwui/OpenGLReadback.h | 5 ++ libs/hwui/Readback.h | 2 + libs/hwui/hwui/Bitmap.cpp | 7 ++- libs/hwui/renderthread/RenderProxy.cpp | 14 +++++ libs/hwui/renderthread/RenderProxy.h | 4 ++ .../scenes/ReadbackFromHardwareBitmap.cpp | 63 +++++++++++++++++++ 7 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp index da6d994f436c8..408159b8f33a8 100644 --- a/libs/hwui/OpenGLReadback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -34,8 +34,6 @@ namespace uirenderer { CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { ATRACE_CALL(); - mRenderThread.eglManager().initialize(); - // Setup the source sp sourceBuffer; sp sourceFence; @@ -61,13 +59,19 @@ CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect return CopyResult::Timeout; } + return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap); +} + +CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, + Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) { + mRenderThread.eglManager().initialize(); // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES // to be able to properly sample from the buffer. // Create the EGLImage object that maps the GraphicBuffer EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer(); + EGLClientBuffer clientBuffer = (EGLClientBuffer) graphicBuffer->getNativeBuffer(); EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, @@ -78,8 +82,8 @@ CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect return CopyResult::UnknownError; } - CopyResult copyResult = copyImageInto(sourceImage, texTransform, sourceBuffer->getWidth(), - sourceBuffer->getHeight(), srcRect, bitmap); + CopyResult copyResult = copyImageInto(sourceImage, texTransform, graphicBuffer->getWidth(), + graphicBuffer->getHeight(), srcRect, bitmap); // All we're flushing & finishing is the deletion of the texture since // copyImageInto already did a major flush & finish as an implicit @@ -89,6 +93,14 @@ CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect return copyResult; } +CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) { + Rect srcRect; + Matrix4 transform; + transform.loadScale(1, -1, 1); + transform.translate(0, -1); + return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h index 7ec2a96a9ac69..f4ebabcdebe56 100644 --- a/libs/hwui/OpenGLReadback.h +++ b/libs/hwui/OpenGLReadback.h @@ -28,6 +28,8 @@ class OpenGLReadback : public Readback { public: virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) override; + virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, + SkBitmap* bitmap) override; protected: explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {} @@ -35,6 +37,9 @@ protected: virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0; +private: + CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform, + const Rect& srcRect, SkBitmap* bitmap); }; class OpenGLReadbackImpl : public OpenGLReadback { diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index 7fbc4bf48c4e4..b76395301a218 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -23,6 +23,7 @@ #include namespace android { +class GraphicBuffer; namespace uirenderer { // Keep in sync with PixelCopy.java codes @@ -42,6 +43,7 @@ public: */ virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0; + virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0; protected: explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {} diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index c028f1158e707..d6b6548c47e22 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -459,14 +459,15 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) { } void Bitmap::getSkBitmap(SkBitmap* outBitmap) { + outBitmap->setHasHardwareMipMap(mHasHardwareMipMap); if (isHardware()) { - //TODO: use readback to get pixels - LOG_ALWAYS_FATAL("Not implemented"); + ALOGW("Warning: attempt to read pixels from hardware bitmap, which is very slow operation"); + outBitmap->allocPixels(info()); + uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap); return; } outBitmap->setInfo(info(), rowBytes()); outBitmap->setPixelRef(this); - outBitmap->setHasHardwareMipMap(mHasHardwareMipMap); } void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) { diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 2c48242793258..022e871315a92 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -27,6 +27,8 @@ #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include + namespace android { namespace uirenderer { namespace renderthread { @@ -663,6 +665,18 @@ sk_sp RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) { return hardwareBitmap; } +CREATE_BRIDGE3(copyGraphicBufferInto, RenderThread* thread, GraphicBuffer* buffer, SkBitmap* bitmap) { + return (void*) args->thread->readback().copyGraphicBufferInto(args->buffer, args->bitmap); +} + +int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) { + SETUP_TASK(copyGraphicBufferInto); + args->thread = &RenderThread::getInstance(); + args->bitmap = bitmap; + args->buffer = buffer; + return static_cast(reinterpret_cast(staticPostAndWait(task))); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index e559142c1acec..44a5a147b6da0 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -35,6 +35,8 @@ #include "DrawFrameTask.h" namespace android { +class GraphicBuffer; + namespace uirenderer { class DeferredLayerUpdater; @@ -131,6 +133,8 @@ public: ANDROID_API static void prepareToDraw(Bitmap& bitmap); static sk_sp allocateHardwareBitmap(SkBitmap& bitmap); + + static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap); private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp new file mode 100644 index 0000000000000..bc6fc6452e901 --- /dev/null +++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestSceneBase.h" + +class ReadbackFromHardware; + +static TestScene::Registrar _SaveLayer(TestScene::Info{ + "readbackFromHBitmap", + "Allocates hardware bitmap and readback data from it.", + TestScene::simpleCreateScene +}); + +class ReadbackFromHardware : public TestScene { +public: + static sk_sp createHardwareBitmap() { + SkBitmap skBitmap; + SkImageInfo info = SkImageInfo::Make(400, 400, kN32_SkColorType, kPremul_SkAlphaType); + skBitmap.allocPixels(info); + skBitmap.eraseColor(Color::Red_500); + SkCanvas canvas(skBitmap); + SkPaint paint; + paint.setColor(Color::Blue_500); + canvas.drawRect(SkRect::MakeXYWH(30, 30, 30, 150), paint); + canvas.drawRect(SkRect::MakeXYWH(30, 30, 100, 30), paint); + canvas.drawRect(SkRect::MakeXYWH(30, 100, 70, 30), paint); + return Bitmap::allocateHardwareBitmap(skBitmap); + } + + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background + + sk_sp hardwareBitmap(createHardwareBitmap()); + + SkBitmap readback; + hardwareBitmap->getSkBitmap(&readback); + + SkBitmap canvasBitmap; + sk_sp heapBitmap(TestUtils::createBitmap(hardwareBitmap->width(), + hardwareBitmap->height(), &canvasBitmap)); + + SkCanvas skCanvas(canvasBitmap); + skCanvas.drawBitmap(readback, 0, 0); + canvas.drawBitmap(*heapBitmap, 0, 0, nullptr); + + canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr); + } + + void doFrame(int frameNr) override { } +};