Routine to upload hardware bitmaps

Change-Id: Id8283a0975325e6830d55fd1e33c5f292a1e9be0
Test: refactoring cl.
bug:30999911
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index c4a65f6..0a60a8e 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -82,6 +82,12 @@
 void Texture::deleteTexture() {
     mCaches.textureState().deleteTexture(mId);
     mId = 0;
+    mTarget = GL_NONE;
+    if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
+        EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
+        eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
+        mEglImageHandle = EGL_NO_IMAGE_KHR;
+    }
 }
 
 bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
@@ -129,6 +135,17 @@
     GL_CHECKPOINT(MODERATE);
 }
 
+void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
+    EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
+    if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
+        mEglImageHandle = EGL_NO_IMAGE_KHR;
+    }
+    mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            buffer->getNativeBuffer(), 0);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
+}
+
 static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
         GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
 
@@ -173,7 +190,7 @@
     }
 }
 
-static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
+void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
         bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
     switch (colorType) {
     case kAlpha_8_SkColorType:
@@ -214,6 +231,24 @@
     }
 }
 
+SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
+    SkBitmap rgbaBitmap;
+    rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
+            bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
+    rgbaBitmap.eraseColor(0);
+    SkCanvas canvas(rgbaBitmap);
+    canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+    return rgbaBitmap;
+}
+
+bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
+    bool needSRGB = info.colorSpace() == sRGB;
+    return info.colorType() == kARGB_4444_SkColorType
+        || info.colorType() == kIndex_8_SkColorType
+        || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
+}
+
+
 void Texture::upload(Bitmap& bitmap) {
     if (!bitmap.readyToDraw()) {
         ALOGE("Cannot generate texture from bitmap");
@@ -243,33 +278,23 @@
     GLint internalFormat, format, type;
     colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
 
-    if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format, GL_TEXTURE_2D)) {
-        needsAlloc = true;
-    }
+    GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+    needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
 
     blend = !bitmap.isOpaque();
-    mCaches.textureState().bindTexture(mId);
+    mCaches.textureState().bindTexture(mTarget, mId);
 
     // TODO: Handle sRGB gray bitmaps
     bool hasSRGB = mCaches.extensions().hasSRGB();
-    if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
-            || bitmap.colorType() == kIndex_8_SkColorType
-            || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) {
-
-        SkBitmap rgbaBitmap;
-        rgbaBitmap.allocPixels(SkImageInfo::MakeN32(
-                mWidth, mHeight, bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
-        rgbaBitmap.eraseColor(0);
-
-        SkCanvas canvas(rgbaBitmap);
+    if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
         SkBitmap skBitmap;
         bitmap.getSkBitmap(&skBitmap);
-        canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
-
+        SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
         uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
                 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
                 rgbaBitmap.height(), rgbaBitmap.getPixels());
-
+    } else if (bitmap.isHardware()) {
+        uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
     } else {
         uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
                 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index d73789a..c75e88f 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -21,8 +21,14 @@
 #include "hwui/Bitmap.h"
 
 #include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <SkBitmap.h>
 
 namespace android {
+
+class GraphicBuffer;
+
 namespace uirenderer {
 
 class Caches;
@@ -34,6 +40,11 @@
  */
 class Texture : public GpuMemoryTracker {
 public:
+    static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB);
+    static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB);
+    static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
+            bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
+
     explicit Texture(Caches& caches)
         : GpuMemoryTracker(GpuObjectType::Texture)
         , mCaches(caches)
@@ -147,7 +158,6 @@
      * the current frame. This is reset at the start of a new frame.
      */
     void* isInUse = nullptr;
-
 private:
     // TODO: Temporarily grant private access to Layer, remove once
     // Layer can be de-tangled from being a dual-purpose render target
@@ -157,6 +167,7 @@
     // Returns true if the size changed, false if it was the same
     bool updateSize(uint32_t width, uint32_t height, GLint internalFormat,
             GLint format, GLenum target);
+    void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
     void resetCachedParams();
 
     GLuint mId = 0;
@@ -165,6 +176,7 @@
     GLint mFormat = 0;
     GLint mInternalFormat = 0;
     GLenum mTarget = GL_NONE;
+    EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
 
     /* See GLES spec section 3.8.14
      * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 31fbe68..be0b22e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,11 +16,27 @@
 #include "Bitmap.h"
 
 #include "Caches.h"
+#include "renderthread/RenderThread.h"
+#include "renderthread/RenderProxy.h"
 
 #include <cutils/log.h>
 #include <sys/mman.h>
 #include <cutils/ashmem.h>
 
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+#include <binder/IServiceManager.h>
+#include <ui/PixelFormat.h>
+
+#include <SkCanvas.h>
+
 namespace android {
 
 static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
@@ -76,6 +92,167 @@
     return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
 }
 
+#define FENCE_TIMEOUT 2000000000
+
+// TODO: handle SRGB sanely
+static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
+    switch (internalFormat) {
+    case GL_ALPHA:
+        return PIXEL_FORMAT_TRANSPARENT;
+    case GL_LUMINANCE:
+        return PIXEL_FORMAT_RGBA_8888;
+    case GL_SRGB8_ALPHA8:
+        return PIXEL_FORMAT_RGBA_8888;
+    case GL_RGBA:
+        return PIXEL_FORMAT_RGBA_8888;
+    default:
+        LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
+        return PIXEL_FORMAT_UNKNOWN;
+    }
+}
+
+class AutoEglFence {
+public:
+    AutoEglFence(EGLDisplay display)
+            : mDisplay(display) {
+        fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
+    }
+
+    ~AutoEglFence() {
+        if (fence != EGL_NO_SYNC_KHR) {
+            eglDestroySyncKHR(mDisplay, fence);
+        }
+    }
+
+    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+private:
+    EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoEglImage {
+public:
+    AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer)
+            : mDisplay(display) {
+        EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+        image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+                EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
+    }
+
+    ~AutoEglImage() {
+        if (image != EGL_NO_IMAGE_KHR) {
+            eglDestroyImageKHR(mDisplay, image);
+        }
+    }
+
+    EGLImageKHR image = EGL_NO_IMAGE_KHR;
+private:
+    EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
+        GraphicBuffer& buffer, GLint format, GLint type) {
+    SkAutoLockPixels alp(bitmap);
+    EGLDisplay display = eglGetCurrentDisplay();
+    LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+                "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+                uirenderer::renderthread::EglManager::eglErrorString());
+    // These objects are initialized below but the default "null"
+    // values are used to cleanup properly at any point in the
+    // initialization sequenc
+    GLuint texture = 0;
+    // We use an EGLImage to access the content of the GraphicBuffer
+    // The EGL image is later bound to a 2D texture
+    EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer.getNativeBuffer();
+    AutoEglImage autoImage(display, clientBuffer);
+    if (autoImage.image == EGL_NO_IMAGE_KHR) {
+        ALOGW("Could not create EGL image, err =%s",
+                uirenderer::renderthread::EglManager::eglErrorString());
+        return false;
+    }
+    glGenTextures(1, &texture);
+    caches.textureState().bindTexture(texture);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+
+    GL_CHECKPOINT(MODERATE);
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+            format, type, bitmap.getPixels());
+
+    GL_CHECKPOINT(MODERATE);
+
+    // The fence is used to wait for the texture upload to finish
+    // properly. We cannot rely on glFlush() and glFinish() as
+    // some drivers completely ignore these API calls
+    AutoEglFence autoFence(display);
+    if (autoFence.fence == EGL_NO_SYNC_KHR) {
+        LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
+        return false;
+    }
+    // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
+    // pipeline flush (similar to what a glFlush() would do.)
+    EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
+            EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
+    if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
+        LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
+        return false;
+    }
+    return true;
+}
+
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread,
+        SkBitmap& skBitmap) {
+    renderThread.eglManager().initialize();
+    uirenderer::Caches& caches = uirenderer::Caches::getInstance();
+
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+    if (alloc == NULL) {
+        ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
+        return nullptr;
+    }
+
+    const SkImageInfo& info = skBitmap.info();
+    if (info.colorType() == kUnknown_SkColorType) {
+        ALOGW("unable to create hardware bitmap of configuration");
+        return nullptr;
+    }
+
+    sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+    bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
+    bool hasSRGB = caches.extensions().hasSRGB();
+    GLint format, type, internalFormat;
+    uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
+            needSRGB, &internalFormat, &format, &type);
+
+    PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
+    status_t error;
+    sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
+            GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
+            | GraphicBuffer::USAGE_SW_READ_NEVER , &error);
+
+    if (!buffer.get()) {
+        ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+        return nullptr;
+    }
+
+    SkBitmap bitmap;
+    if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
+            hasSRGB, sRGB.get()))) {
+        bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
+    } else {
+        bitmap = skBitmap;
+    }
+
+    if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
+        return nullptr;
+    }
+    return sk_sp<Bitmap>(new Bitmap(std::move(buffer), info));
+}
+
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
+    return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
+}
+
 sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
    return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
 }
@@ -183,6 +360,16 @@
     reconfigure(info, rowBytes, ctable);
 }
 
+Bitmap::Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info)
+        : SkPixelRef(info)
+        , mPixelStorageType(PixelStorageType::Hardware) {
+    auto rawBuffer = buffer.get();
+    mPixelStorage.hardware.buffer = rawBuffer;
+    if (rawBuffer) {
+        rawBuffer->incStrong(rawBuffer);
+    }
+    mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride();
+}
 Bitmap::~Bitmap() {
     switch (mPixelStorageType) {
     case PixelStorageType::External:
@@ -196,6 +383,12 @@
     case PixelStorageType::Heap:
         free(mPixelStorage.heap.address);
         break;
+    case PixelStorageType::Hardware:
+        auto buffer = mPixelStorage.hardware.buffer;
+        buffer->decStrong(buffer);
+        mPixelStorage.hardware.buffer = nullptr;
+        break;
+
     }
 
     if (android::uirenderer::Caches::hasInstance()) {
@@ -219,6 +412,9 @@
         return mPixelStorage.ashmem.address;
     case PixelStorageType::Heap:
         return mPixelStorage.heap.address;
+    case PixelStorageType::Hardware:
+        LOG_ALWAYS_FATAL_IF("Can't get address for hardware bitmap");
+        return nullptr;
     }
 }
 
@@ -264,6 +460,11 @@
 }
 
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
+    if (isHardware()) {
+        //TODO: use readback to get pixels
+        LOG_ALWAYS_FATAL("Not implemented");
+        return;
+    }
     outBitmap->setInfo(info(), rowBytes());
     outBitmap->setPixelRef(this);
     outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
@@ -274,4 +475,11 @@
     bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
 }
 
+GraphicBuffer* Bitmap::graphicBuffer() {
+    if (isHardware()) {
+        return mPixelStorage.hardware.buffer;
+    }
+    return nullptr;
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index c175690..3940381 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -20,6 +20,7 @@
 #include <SkImageInfo.h>
 #include <SkPixelRef.h>
 #include <cutils/compiler.h>
+#include <ui/GraphicBuffer.h>
 
 namespace android {
 
@@ -27,8 +28,17 @@
     External,
     Heap,
     Ashmem,
+    Hardware,
 };
 
+namespace uirenderer {
+namespace renderthread {
+    class RenderThread;
+}
+}
+
+class PixelStorage;
+
 typedef void (*FreeFunc)(void* addr, void* context);
 
 class ANDROID_API Bitmap : public SkPixelRef {
@@ -36,17 +46,24 @@
     static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
     static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
 
+    static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
+
     static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
     static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
         size_t rowBytes, SkColorTable* ctable);
 
     static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
+
+    static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&,
+            SkBitmap& bitmap);
+
     Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
             SkColorTable* ctable);
     Bitmap(void* address, void* context, FreeFunc freeFunc,
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
     Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
             size_t rowBytes, SkColorTable* ctable);
+    Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info);
 
     int width() const { return info().width(); }
     int height() const { return info().height(); }
@@ -81,13 +98,18 @@
     bool readyToDraw() const {
         return this->colorType() != kIndex_8_SkColorType || mColorTable;
     }
+
+    bool isHardware() const {
+        return mPixelStorageType == PixelStorageType::Hardware;
+    }
+
+    GraphicBuffer* graphicBuffer();
 protected:
     virtual bool onNewLockPixels(LockRec* rec) override;
     virtual void onUnlockPixels() override { };
     virtual size_t getAllocatedSizeInBytes() const override;
 private:
     virtual ~Bitmap();
-    void doFreePixels();
     void* getStorage() const;
 
     PixelStorageType mPixelStorageType;
@@ -111,6 +133,9 @@
             void* address;
             size_t size;
         } heap;
+        struct {
+            GraphicBuffer* buffer;
+        } hardware;
     } mPixelStorage;
 };
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index beda045..cf0af6f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,6 +16,7 @@
 
 #include "EglManager.h"
 
+#include "Texture.h"
 #include "Caches.h"
 #include "DeviceInfo.h"
 #include "Properties.h"
@@ -58,7 +59,7 @@
         return "Unknown error";
     }
 }
-static const char* egl_error_str() {
+const char* EglManager::eglErrorString() {
     return egl_error_str(eglGetError());
 }
 
@@ -101,11 +102,11 @@
 
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
-            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
 
     EGLint major, minor;
     LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
-            "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+            "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
 
     ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
 
@@ -173,7 +174,7 @@
             loadConfig();
         } else {
             // Failed to get a valid config
-            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
         }
     }
 }
@@ -185,7 +186,7 @@
     };
     mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
     LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
-        "Failed to create context, error = %s", egl_error_str());
+        "Failed to create context, error = %s", eglErrorString());
 }
 
 void EglManager::createPBufferSurface() {
@@ -212,12 +213,12 @@
     EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
     LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
             "Failed to create EGLSurface for window %p, eglErr = %s",
-            (void*) window, egl_error_str());
+            (void*) window, eglErrorString());
 
     if (mSwapBehavior != SwapBehavior::Preserved) {
         LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
                             "Failed to set swap behavior to destroyed for window %p, eglErr = %s",
-                            (void*) window, egl_error_str());
+                            (void*) window, eglErrorString());
     }
 
     return surface;
@@ -228,7 +229,7 @@
         makeCurrent(EGL_NO_SURFACE);
     }
     if (!eglDestroySurface(mEglDisplay, surface)) {
-        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
     }
 }
 
@@ -262,7 +263,7 @@
                     (void*)surface, egl_error_str(*errOut));
         } else {
             LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
-                    (void*)surface, egl_error_str());
+                    (void*)surface, eglErrorString());
         }
     }
     mCurrentSurface = surface;
@@ -303,7 +304,7 @@
         frame.map(dirty, rects);
         if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
             LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
-                    (void*)frame.mSurface, egl_error_str());
+                    (void*)frame.mSurface, eglErrorString());
         }
     }
 #endif
@@ -357,14 +358,14 @@
             preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
     if (!preserved) {
         ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
-                (void*) surface, egl_error_str());
+                (void*) surface, eglErrorString());
         // Maybe it's already set?
         EGLint swapBehavior;
         if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
             preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
         } else {
             ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
-                                (void*) surface, egl_error_str());
+                                (void*) surface, eglErrorString());
         }
     }
 
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index ba4a3e1..7349dcc 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -55,6 +55,7 @@
 // and EGLConfig, which are re-used by CanvasContext
 class EglManager {
 public:
+    static const char* eglErrorString();
     // Returns true on success, false on failure
     void initialize();
 
@@ -83,7 +84,6 @@
 
 private:
     friend class RenderThread;
-
     explicit EglManager(RenderThread& thread);
     // EglContext is never destroyed, method is purposely not implemented
     ~EglManager();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 42da293..f747a51 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -653,6 +653,19 @@
     }
 }
 
+CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
+    sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
+    return hardwareBitmap.release();
+}
+
+sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
+    SETUP_TASK(allocateHardwareBitmap);
+    args->bitmap = &bitmap;
+    args->thread = &RenderThread::getInstance();
+    sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task)));
+    return hardwareBitmap;
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ae9330d..e559142 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -130,6 +130,7 @@
             int left, int top, int right, int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);
 
+    static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
new file mode 100644
index 0000000..6dadd3e3
--- /dev/null
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "TestScene.h"
+
+#include <SkBitmap.h>
+#include <string>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+class BitmapAllocationTestUtils {
+public:
+    static sk_sp<Bitmap> allocateHeapBitmap(int width, int height,
+            SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
+         sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType);
+         SkBitmap skBitmap;
+         bitmap->getSkBitmap(&skBitmap);
+         setup(skBitmap);
+         return bitmap;
+    }
+
+    static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
+            SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
+        SkBitmap skBitmap;
+        sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
+        setup(skBitmap);
+        return Bitmap::allocateHardwareBitmap(skBitmap);
+    }
+
+    typedef sk_sp<Bitmap> (*BitmapAllocator) (int, int, SkColorType,
+            std::function<void(SkBitmap& bitmap)> setup);
+
+    template <class T, BitmapAllocator allocator>
+    static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) {
+        return new T(allocator);
+    }
+
+    template <class BaseScene>
+    static bool registerBitmapAllocationScene(std::string name, std::string  description) {
+        TestScene::registerScene({
+            name + "GlTex",
+            description + " (GlTex version).",
+            createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>
+        });
+
+        TestScene::registerScene({
+            name + "EglImage",
+            description + " (EglImage version).",
+            createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>
+        });
+        return true;
+    }
+};
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android
\ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
new file mode 100644
index 0000000..be58d09
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 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"
+#include "tests/common/BitmapAllocationTestUtils.h"
+#include "utils/Color.h"
+
+#include <SkBitmap.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+class BitmapFillrate;
+
+static bool _BitmapFillrate(
+        BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapFillrate>(
+                "bitmapFillrate", "Draws multiple large half transparent bitmaps."));
+
+class BitmapFillrate : public TestScene {
+public:
+    BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+        : TestScene()
+        , mAllocator(allocator) { }
+
+    void createContent(int width, int height, Canvas& canvas) override {
+        canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+        createNode(canvas, 0x909C27B0, 0, 0, width, height);
+        createNode(canvas, 0xA0CDDC39, width / 3, height / 3, width, height);
+        createNode(canvas, 0x90009688, width / 3, 0, width, height);
+        createNode(canvas, 0xA0FF5722, 0, height / 3, width, height);
+        createNode(canvas, 0x9000796B, width / 6, height/6, width, height);
+        createNode(canvas, 0xA0FFC107, width / 6, 0, width, height);
+    }
+
+    void doFrame(int frameNr) override {
+        for (size_t ci = 0; ci < mNodes.size(); ci++) {
+            mNodes[ci]->mutateStagingProperties().setTranslationX(frameNr % 200);
+            mNodes[ci]->mutateStagingProperties().setTranslationY(frameNr % 200);
+            mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        }
+    }
+private:
+    void createNode(Canvas& canvas, SkColor color, int left, int top,
+            int width, int height) {
+        int itemWidth = 2 * width / 3;
+        int itemHeight = 2 * height / 3;
+        auto card = TestUtils::createNode(left, top, left + itemWidth , top + itemHeight,
+                [this, itemWidth, itemHeight, color](RenderProperties& props, Canvas& canvas) {
+            sk_sp<Bitmap> bitmap = mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType,
+                    [color](SkBitmap& skBitmap) {
+                 skBitmap.eraseColor(color);
+            });
+            canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+        });
+        canvas.drawRenderNode(card.get());
+        mNodes.push_back(card);
+    }
+
+    BitmapAllocationTestUtils::BitmapAllocator mAllocator;
+    std::vector< sp<RenderNode> > mNodes;
+};
\ No newline at end of file
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index b49c1eb..94818b2 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -27,7 +27,7 @@
 #if DEBUG_OPENGL
 #define GL_CHECKPOINT(LEVEL) \
     do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
-    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
+    LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\
             "GL errors! %s:%d", __FILE__, __LINE__);\
     } } while (0)
 #else