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