/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * James Willcox * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #include #include "AndroidGraphicBuffer.h" #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidGraphicBuffer" , ## args) #define EGL_NATIVE_BUFFER_ANDROID 0x3140 #define EGL_IMAGE_PRESERVED_KHR 0x30D2 typedef void* EGLContext; typedef void* EGLImageKHR; typedef void* EGLClientBuffer; typedef void* EGLDisplay; typedef PRUint32 EGLenum; typedef PRInt32 EGLint; typedef PRUint32 EGLBoolean; typedef gfxASurface::gfxImageFormat gfxImageFormat; #define EGL_TRUE 1 #define EGL_FALSE 0 #define EGL_NONE 0x3038 #define EGL_NO_CONTEXT (EGLContext)0 #define EGL_DEFAULT_DISPLAY (void*)0 #define ANDROID_LIBUI_PATH "libui.so" #define ANDROID_GLES_PATH "libGLESv2.so" #define ANDROID_EGL_PATH "libEGL.so" // Really I have no idea, but this should be big enough #define GRAPHIC_BUFFER_SIZE 1024 enum { /* buffer is never read in software */ GRALLOC_USAGE_SW_READ_NEVER = 0x00000000, /* buffer is rarely read in software */ GRALLOC_USAGE_SW_READ_RARELY = 0x00000002, /* buffer is often read in software */ GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003, /* mask for the software read values */ GRALLOC_USAGE_SW_READ_MASK = 0x0000000F, /* buffer is never written in software */ GRALLOC_USAGE_SW_WRITE_NEVER = 0x00000000, /* buffer is never written in software */ GRALLOC_USAGE_SW_WRITE_RARELY = 0x00000020, /* buffer is never written in software */ GRALLOC_USAGE_SW_WRITE_OFTEN = 0x00000030, /* mask for the software write values */ GRALLOC_USAGE_SW_WRITE_MASK = 0x000000F0, /* buffer will be used as an OpenGL ES texture */ GRALLOC_USAGE_HW_TEXTURE = 0x00000100, /* buffer will be used as an OpenGL ES render target */ GRALLOC_USAGE_HW_RENDER = 0x00000200, /* buffer will be used by the 2D hardware blitter */ GRALLOC_USAGE_HW_2D = 0x00000400, /* buffer will be used with the framebuffer device */ GRALLOC_USAGE_HW_FB = 0x00001000, /* mask for the software usage bit-mask */ GRALLOC_USAGE_HW_MASK = 0x00001F00, }; enum { HAL_PIXEL_FORMAT_RGBA_8888 = 1, HAL_PIXEL_FORMAT_RGBX_8888 = 2, HAL_PIXEL_FORMAT_RGB_888 = 3, HAL_PIXEL_FORMAT_RGB_565 = 4, HAL_PIXEL_FORMAT_BGRA_8888 = 5, HAL_PIXEL_FORMAT_RGBA_5551 = 6, HAL_PIXEL_FORMAT_RGBA_4444 = 7, }; typedef struct AndroidRect { int32_t left; int32_t top; int32_t right; int32_t bottom; } AndroidRect; static bool gTryRealloc = true; static class GLFunctions { public: GLFunctions() : mInitialized(false) { } typedef EGLDisplay (* pfnGetDisplay)(void *display_id); pfnGetDisplay fGetDisplay; typedef EGLint (* pfnEGLGetError)(void); pfnEGLGetError fEGLGetError; typedef EGLImageKHR (* pfnCreateImageKHR)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); pfnCreateImageKHR fCreateImageKHR; typedef EGLBoolean (* pfnDestroyImageKHR)(EGLDisplay dpy, EGLImageKHR image); pfnDestroyImageKHR fDestroyImageKHR; typedef void (* pfnImageTargetTexture2DOES)(GLenum target, EGLImageKHR image); pfnImageTargetTexture2DOES fImageTargetTexture2DOES; typedef void (* pfnBindTexture)(GLenum target, GLuint texture); pfnBindTexture fBindTexture; typedef GLenum (* pfnGLGetError)(); pfnGLGetError fGLGetError; typedef void (*pfnGraphicBufferCtor)(void*, PRUint32 w, PRUint32 h, PRUint32 format, PRUint32 usage); pfnGraphicBufferCtor fGraphicBufferCtor; typedef void (*pfnGraphicBufferDtor)(void*); pfnGraphicBufferDtor fGraphicBufferDtor; typedef int (*pfnGraphicBufferLock)(void*, PRUint32 usage, unsigned char **addr); pfnGraphicBufferLock fGraphicBufferLock; typedef int (*pfnGraphicBufferLockRect)(void*, PRUint32 usage, const AndroidRect&, unsigned char **addr); pfnGraphicBufferLockRect fGraphicBufferLockRect; typedef int (*pfnGraphicBufferUnlock)(void*); pfnGraphicBufferUnlock fGraphicBufferUnlock; typedef void* (*pfnGraphicBufferGetNativeBuffer)(void*); pfnGraphicBufferGetNativeBuffer fGraphicBufferGetNativeBuffer; typedef int (*pfnGraphicBufferReallocate)(void*, PRUint32 w, PRUint32 h, PRUint32 format); pfnGraphicBufferReallocate fGraphicBufferReallocate; bool EnsureInitialized() { if (mInitialized) { return true; } void *handle = dlopen(ANDROID_EGL_PATH, RTLD_LAZY); if (!handle) { LOG("Couldn't load EGL library"); return false; } fGetDisplay = (pfnGetDisplay)dlsym(handle, "eglGetDisplay"); fEGLGetError = (pfnEGLGetError)dlsym(handle, "eglGetError"); fCreateImageKHR = (pfnCreateImageKHR)dlsym(handle, "eglCreateImageKHR"); fDestroyImageKHR = (pfnDestroyImageKHR)dlsym(handle, "eglDestroyImageKHR"); if (!fGetDisplay || !fEGLGetError || !fCreateImageKHR || !fDestroyImageKHR) { LOG("Failed to find some EGL functions"); return false; } handle = dlopen(ANDROID_GLES_PATH, RTLD_LAZY); if (!handle) { LOG("Couldn't load GL library"); return false; } fImageTargetTexture2DOES = (pfnImageTargetTexture2DOES)dlsym(handle, "glEGLImageTargetTexture2DOES"); fBindTexture = (pfnBindTexture)dlsym(handle, "glBindTexture"); fGLGetError = (pfnGLGetError)dlsym(handle, "glGetError"); if (!fImageTargetTexture2DOES || !fBindTexture || !fGLGetError) { LOG("Failed to find some GL functions"); return false; } handle = dlopen(ANDROID_LIBUI_PATH, RTLD_LAZY); if (!handle) { LOG("Couldn't load libui.so"); return false; } fGraphicBufferCtor = (pfnGraphicBufferCtor)dlsym(handle, "_ZN7android13GraphicBufferC1Ejjij"); fGraphicBufferDtor = (pfnGraphicBufferDtor)dlsym(handle, "_ZN7android13GraphicBufferD1Ev"); fGraphicBufferLock = (pfnGraphicBufferLock)dlsym(handle, "_ZN7android13GraphicBuffer4lockEjPPv"); fGraphicBufferLockRect = (pfnGraphicBufferLockRect)dlsym(handle, "_ZN7android13GraphicBuffer4lockEjRKNS_4RectEPPv"); fGraphicBufferUnlock = (pfnGraphicBufferUnlock)dlsym(handle, "_ZN7android13GraphicBuffer6unlockEv"); fGraphicBufferGetNativeBuffer = (pfnGraphicBufferGetNativeBuffer)dlsym(handle, "_ZNK7android13GraphicBuffer15getNativeBufferEv"); fGraphicBufferReallocate = (pfnGraphicBufferReallocate)dlsym(handle, "_ZN7android13GraphicBuffer10reallocateEjjij"); if (!fGraphicBufferCtor || !fGraphicBufferDtor || !fGraphicBufferLock || !fGraphicBufferUnlock || !fGraphicBufferGetNativeBuffer) { LOG("Failed to lookup some GraphicBuffer functions"); return false; } mInitialized = true; return true; } private: bool mInitialized; } sGLFunctions; namespace mozilla { static void clearGLError() { while (glGetError() != GL_NO_ERROR); } static bool ensureNoGLError(const char* name) { bool result = true; GLuint error; while ((error = glGetError()) != GL_NO_ERROR) { LOG("GL error [%s]: %40x\n", name, error); result = false; } return result; } AndroidGraphicBuffer::AndroidGraphicBuffer(PRUint32 width, PRUint32 height, PRUint32 usage, gfxImageFormat format) : mWidth(width) , mHeight(height) , mUsage(usage) , mFormat(format) , mHandle(0) , mEGLImage(0) { } AndroidGraphicBuffer::~AndroidGraphicBuffer() { DestroyBuffer(); } void AndroidGraphicBuffer::DestroyBuffer() { /** * XXX: eglDestroyImageKHR crashes sometimes due to refcount badness (I think) * * If you look at egl.cpp (https://github.com/android/platform_frameworks_base/blob/master/opengl/libagl/egl.cpp#L2002) * you can see that eglCreateImageKHR just refs the native buffer, and eglDestroyImageKHR * just unrefs it. Somehow the ref count gets messed up and things are already destroyed * by the time eglDestroyImageKHR gets called. For now, at least, just not calling * eglDestroyImageKHR should be fine since we do free the GraphicBuffer below. * * Bug 712716 */ #if 0 if (mEGLImage) { if (sGLFunctions.EnsureInitialized()) { sGLFunctions.fDestroyImageKHR(sGLFunctions.fGetDisplay(EGL_DEFAULT_DISPLAY), mEGLImage); mEGLImage = NULL; } } #endif mEGLImage = NULL; if (mHandle) { if (sGLFunctions.EnsureInitialized()) { sGLFunctions.fGraphicBufferDtor(mHandle); } free(mHandle); mHandle = NULL; } } bool AndroidGraphicBuffer::EnsureBufferCreated() { if (!mHandle) { mHandle = malloc(GRAPHIC_BUFFER_SIZE); sGLFunctions.fGraphicBufferCtor(mHandle, mWidth, mHeight, GetAndroidFormat(mFormat), GetAndroidUsage(mUsage)); } return true; } bool AndroidGraphicBuffer::EnsureInitialized() { if (!sGLFunctions.EnsureInitialized()) { return false; } EnsureBufferCreated(); return true; } bool AndroidGraphicBuffer::Lock(PRUint32 aUsage, unsigned char **bits) { if (!EnsureInitialized()) return true; return sGLFunctions.fGraphicBufferLock(mHandle, GetAndroidUsage(aUsage), bits) == 0; } bool AndroidGraphicBuffer::Lock(PRUint32 aUsage, const nsIntRect& aRect, unsigned char **bits) { if (!EnsureInitialized()) return false; AndroidRect rect; rect.left = aRect.x; rect.top = aRect.y; rect.right = aRect.x + aRect.width; rect.bottom = aRect.y + aRect.height; return sGLFunctions.fGraphicBufferLockRect(mHandle, GetAndroidUsage(aUsage), rect, bits) == 0; } bool AndroidGraphicBuffer::Unlock() { if (!EnsureInitialized()) return false; return sGLFunctions.fGraphicBufferUnlock(mHandle) == 0; } bool AndroidGraphicBuffer::Reallocate(PRUint32 aWidth, PRUint32 aHeight, gfxImageFormat aFormat) { if (!EnsureInitialized()) return false; mWidth = aWidth; mHeight = aHeight; mFormat = aFormat; // Sometimes GraphicBuffer::reallocate just doesn't work. In those cases we'll just allocate a brand // new buffer. If reallocate fails once, never try it again. if (!gTryRealloc || sGLFunctions.fGraphicBufferReallocate(mHandle, aWidth, aHeight, GetAndroidFormat(aFormat)) != 0) { DestroyBuffer(); EnsureBufferCreated(); gTryRealloc = false; } return true; } PRUint32 AndroidGraphicBuffer::GetAndroidUsage(PRUint32 aUsage) { PRUint32 flags = 0; if (aUsage & UsageSoftwareRead) { flags |= GRALLOC_USAGE_SW_READ_OFTEN; } if (aUsage & UsageSoftwareWrite) { flags |= GRALLOC_USAGE_SW_WRITE_OFTEN; } if (aUsage & UsageTexture) { flags |= GRALLOC_USAGE_HW_TEXTURE; } if (aUsage & UsageTarget) { flags |= GRALLOC_USAGE_HW_RENDER; } if (aUsage & Usage2D) { flags |= GRALLOC_USAGE_HW_2D; } return flags; } PRUint32 AndroidGraphicBuffer::GetAndroidFormat(gfxImageFormat aFormat) { switch (aFormat) { case gfxImageFormat::ImageFormatRGB24: return HAL_PIXEL_FORMAT_RGBX_8888; case gfxImageFormat::ImageFormatRGB16_565: return HAL_PIXEL_FORMAT_RGB_565; default: return 0; } } bool AndroidGraphicBuffer::EnsureEGLImage() { if (mEGLImage) return true; if (!EnsureInitialized()) return false; EGLint eglImgAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE }; void* nativeBuffer = sGLFunctions.fGraphicBufferGetNativeBuffer(mHandle); mEGLImage = sGLFunctions.fCreateImageKHR(sGLFunctions.fGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)nativeBuffer, eglImgAttrs); return mEGLImage != NULL; } bool AndroidGraphicBuffer::Bind() { if (!EnsureInitialized()) return false; if (!EnsureEGLImage()) { LOG("No valid EGLImage!"); return false; } clearGLError(); sGLFunctions.fImageTargetTexture2DOES(GL_TEXTURE_2D, mEGLImage); return ensureNoGLError("glEGLImageTargetTexture2DOES"); } } /* mozilla */