Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <dlfcn.h>
7 #include <android/log.h>
8 #include <GLES2/gl2.h>
9 #include <nsTArray.h>
10 #include "AndroidGraphicBuffer.h"
11 #include "AndroidBridge.h"
12 #include "mozilla/Preferences.h"
14 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidGraphicBuffer" , ## args)
16 #define EGL_NATIVE_BUFFER_ANDROID 0x3140
17 #define EGL_IMAGE_PRESERVED_KHR 0x30D2
19 typedef void *EGLContext;
20 typedef void *EGLDisplay;
21 typedef uint32_t EGLenum;
22 typedef int32_t EGLint;
23 typedef uint32_t EGLBoolean;
25 #define EGL_TRUE 1
26 #define EGL_FALSE 0
27 #define EGL_NONE 0x3038
28 #define EGL_NO_CONTEXT (EGLContext)0
29 #define EGL_DEFAULT_DISPLAY (void*)0
31 #define ANDROID_LIBUI_PATH "libui.so"
32 #define ANDROID_GLES_PATH "libGLESv2.so"
33 #define ANDROID_EGL_PATH "libEGL.so"
35 // Really I have no idea, but this should be big enough
36 #define GRAPHIC_BUFFER_SIZE 1024
38 enum {
39 /* buffer is never read in software */
40 GRALLOC_USAGE_SW_READ_NEVER = 0x00000000,
41 /* buffer is rarely read in software */
42 GRALLOC_USAGE_SW_READ_RARELY = 0x00000002,
43 /* buffer is often read in software */
44 GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003,
45 /* mask for the software read values */
46 GRALLOC_USAGE_SW_READ_MASK = 0x0000000F,
48 /* buffer is never written in software */
49 GRALLOC_USAGE_SW_WRITE_NEVER = 0x00000000,
50 /* buffer is never written in software */
51 GRALLOC_USAGE_SW_WRITE_RARELY = 0x00000020,
52 /* buffer is never written in software */
53 GRALLOC_USAGE_SW_WRITE_OFTEN = 0x00000030,
54 /* mask for the software write values */
55 GRALLOC_USAGE_SW_WRITE_MASK = 0x000000F0,
57 /* buffer will be used as an OpenGL ES texture */
58 GRALLOC_USAGE_HW_TEXTURE = 0x00000100,
59 /* buffer will be used as an OpenGL ES render target */
60 GRALLOC_USAGE_HW_RENDER = 0x00000200,
61 /* buffer will be used by the 2D hardware blitter */
62 GRALLOC_USAGE_HW_2D = 0x00000400,
63 /* buffer will be used with the framebuffer device */
64 GRALLOC_USAGE_HW_FB = 0x00001000,
65 /* mask for the software usage bit-mask */
66 GRALLOC_USAGE_HW_MASK = 0x00001F00,
67 };
69 enum {
70 HAL_PIXEL_FORMAT_RGBA_8888 = 1,
71 HAL_PIXEL_FORMAT_RGBX_8888 = 2,
72 HAL_PIXEL_FORMAT_RGB_888 = 3,
73 HAL_PIXEL_FORMAT_RGB_565 = 4,
74 HAL_PIXEL_FORMAT_BGRA_8888 = 5,
75 HAL_PIXEL_FORMAT_RGBA_5551 = 6,
76 HAL_PIXEL_FORMAT_RGBA_4444 = 7,
77 };
79 typedef struct ARect {
80 int32_t left;
81 int32_t top;
82 int32_t right;
83 int32_t bottom;
84 } ARect;
86 static bool gTryRealloc = true;
88 static class GLFunctions
89 {
90 public:
91 MOZ_CONSTEXPR GLFunctions() : fGetDisplay(nullptr),
92 fEGLGetError(nullptr),
93 fCreateImageKHR(nullptr),
94 fDestroyImageKHR(nullptr),
95 fImageTargetTexture2DOES(nullptr),
96 fBindTexture(nullptr),
97 fGLGetError(nullptr),
98 fGraphicBufferCtor(nullptr),
99 fGraphicBufferDtor(nullptr),
100 fGraphicBufferLock(nullptr),
101 fGraphicBufferLockRect(nullptr),
102 fGraphicBufferUnlock(nullptr),
103 fGraphicBufferGetNativeBuffer(nullptr),
104 fGraphicBufferReallocate(nullptr),
105 mInitialized(false)
106 {
107 }
109 typedef EGLDisplay (* pfnGetDisplay)(void *display_id);
110 pfnGetDisplay fGetDisplay;
111 typedef EGLint (* pfnEGLGetError)(void);
112 pfnEGLGetError fEGLGetError;
114 typedef EGLImageKHR (* pfnCreateImageKHR)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
115 pfnCreateImageKHR fCreateImageKHR;
116 typedef EGLBoolean (* pfnDestroyImageKHR)(EGLDisplay dpy, EGLImageKHR image);
117 pfnDestroyImageKHR fDestroyImageKHR;
119 typedef void (* pfnImageTargetTexture2DOES)(GLenum target, EGLImageKHR image);
120 pfnImageTargetTexture2DOES fImageTargetTexture2DOES;
122 typedef void (* pfnBindTexture)(GLenum target, GLuint texture);
123 pfnBindTexture fBindTexture;
125 typedef GLenum (* pfnGLGetError)();
126 pfnGLGetError fGLGetError;
128 typedef void (*pfnGraphicBufferCtor)(void*, uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
129 pfnGraphicBufferCtor fGraphicBufferCtor;
131 typedef void (*pfnGraphicBufferDtor)(void*);
132 pfnGraphicBufferDtor fGraphicBufferDtor;
134 typedef int (*pfnGraphicBufferLock)(void*, uint32_t usage, unsigned char **addr);
135 pfnGraphicBufferLock fGraphicBufferLock;
137 typedef int (*pfnGraphicBufferLockRect)(void*, uint32_t usage, const ARect&, unsigned char **addr);
138 pfnGraphicBufferLockRect fGraphicBufferLockRect;
140 typedef int (*pfnGraphicBufferUnlock)(void*);
141 pfnGraphicBufferUnlock fGraphicBufferUnlock;
143 typedef void* (*pfnGraphicBufferGetNativeBuffer)(void*);
144 pfnGraphicBufferGetNativeBuffer fGraphicBufferGetNativeBuffer;
146 typedef int (*pfnGraphicBufferReallocate)(void*, uint32_t w, uint32_t h, uint32_t format);
147 pfnGraphicBufferReallocate fGraphicBufferReallocate;
149 bool EnsureInitialized()
150 {
151 if (mInitialized) {
152 return true;
153 }
155 void *handle = dlopen(ANDROID_EGL_PATH, RTLD_LAZY);
156 if (!handle) {
157 LOG("Couldn't load EGL library");
158 return false;
159 }
161 fGetDisplay = (pfnGetDisplay)dlsym(handle, "eglGetDisplay");
162 fEGLGetError = (pfnEGLGetError)dlsym(handle, "eglGetError");
163 fCreateImageKHR = (pfnCreateImageKHR)dlsym(handle, "eglCreateImageKHR");
164 fDestroyImageKHR = (pfnDestroyImageKHR)dlsym(handle, "eglDestroyImageKHR");
166 if (!fGetDisplay || !fEGLGetError || !fCreateImageKHR || !fDestroyImageKHR) {
167 LOG("Failed to find some EGL functions");
168 return false;
169 }
171 handle = dlopen(ANDROID_GLES_PATH, RTLD_LAZY);
172 if (!handle) {
173 LOG("Couldn't load GL library");
174 return false;
175 }
177 fImageTargetTexture2DOES = (pfnImageTargetTexture2DOES)dlsym(handle, "glEGLImageTargetTexture2DOES");
178 fBindTexture = (pfnBindTexture)dlsym(handle, "glBindTexture");
179 fGLGetError = (pfnGLGetError)dlsym(handle, "glGetError");
181 if (!fImageTargetTexture2DOES || !fBindTexture || !fGLGetError) {
182 LOG("Failed to find some GL functions");
183 return false;
184 }
186 handle = dlopen(ANDROID_LIBUI_PATH, RTLD_LAZY);
187 if (!handle) {
188 LOG("Couldn't load libui.so");
189 return false;
190 }
192 fGraphicBufferCtor = (pfnGraphicBufferCtor)dlsym(handle, "_ZN7android13GraphicBufferC1Ejjij");
193 fGraphicBufferDtor = (pfnGraphicBufferDtor)dlsym(handle, "_ZN7android13GraphicBufferD1Ev");
194 fGraphicBufferLock = (pfnGraphicBufferLock)dlsym(handle, "_ZN7android13GraphicBuffer4lockEjPPv");
195 fGraphicBufferLockRect = (pfnGraphicBufferLockRect)dlsym(handle, "_ZN7android13GraphicBuffer4lockEjRKNS_4RectEPPv");
196 fGraphicBufferUnlock = (pfnGraphicBufferUnlock)dlsym(handle, "_ZN7android13GraphicBuffer6unlockEv");
197 fGraphicBufferGetNativeBuffer = (pfnGraphicBufferGetNativeBuffer)dlsym(handle, "_ZNK7android13GraphicBuffer15getNativeBufferEv");
198 fGraphicBufferReallocate = (pfnGraphicBufferReallocate)dlsym(handle, "_ZN7android13GraphicBuffer10reallocateEjjij");
200 if (!fGraphicBufferCtor || !fGraphicBufferDtor || !fGraphicBufferLock ||
201 !fGraphicBufferUnlock || !fGraphicBufferGetNativeBuffer) {
202 LOG("Failed to lookup some GraphicBuffer functions");
203 return false;
204 }
206 mInitialized = true;
207 return true;
208 }
210 private:
211 bool mInitialized;
213 } sGLFunctions;
215 namespace mozilla {
217 static void clearGLError()
218 {
219 while (glGetError() != GL_NO_ERROR);
220 }
222 static bool ensureNoGLError(const char* name)
223 {
224 bool result = true;
225 GLuint error;
227 while ((error = glGetError()) != GL_NO_ERROR) {
228 LOG("GL error [%s]: %40x\n", name, error);
229 result = false;
230 }
232 return result;
233 }
235 AndroidGraphicBuffer::AndroidGraphicBuffer(uint32_t width, uint32_t height, uint32_t usage,
236 gfxImageFormat format) :
237 mWidth(width)
238 , mHeight(height)
239 , mUsage(usage)
240 , mFormat(format)
241 , mHandle(0)
242 , mEGLImage(0)
243 {
244 }
246 AndroidGraphicBuffer::~AndroidGraphicBuffer()
247 {
248 DestroyBuffer();
249 }
251 void
252 AndroidGraphicBuffer::DestroyBuffer()
253 {
254 /**
255 * XXX: eglDestroyImageKHR crashes sometimes due to refcount badness (I think)
256 *
257 * If you look at egl.cpp (https://github.com/android/platform_frameworks_base/blob/master/opengl/libagl/egl.cpp#L2002)
258 * you can see that eglCreateImageKHR just refs the native buffer, and eglDestroyImageKHR
259 * just unrefs it. Somehow the ref count gets messed up and things are already destroyed
260 * by the time eglDestroyImageKHR gets called. For now, at least, just not calling
261 * eglDestroyImageKHR should be fine since we do free the GraphicBuffer below.
262 *
263 * Bug 712716
264 */
265 #if 0
266 if (mEGLImage) {
267 if (sGLFunctions.EnsureInitialized()) {
268 sGLFunctions.fDestroyImageKHR(sGLFunctions.fGetDisplay(EGL_DEFAULT_DISPLAY), mEGLImage);
269 mEGLImage = nullptr;
270 }
271 }
272 #endif
273 mEGLImage = nullptr;
275 if (mHandle) {
276 if (sGLFunctions.EnsureInitialized()) {
277 sGLFunctions.fGraphicBufferDtor(mHandle);
278 }
279 free(mHandle);
280 mHandle = nullptr;
281 }
283 }
285 bool
286 AndroidGraphicBuffer::EnsureBufferCreated()
287 {
288 if (!mHandle) {
289 mHandle = malloc(GRAPHIC_BUFFER_SIZE);
290 sGLFunctions.fGraphicBufferCtor(mHandle, mWidth, mHeight, GetAndroidFormat(mFormat), GetAndroidUsage(mUsage));
291 }
293 return true;
294 }
296 bool
297 AndroidGraphicBuffer::EnsureInitialized()
298 {
299 if (!sGLFunctions.EnsureInitialized()) {
300 return false;
301 }
303 EnsureBufferCreated();
304 return true;
305 }
307 int
308 AndroidGraphicBuffer::Lock(uint32_t aUsage, unsigned char **bits)
309 {
310 if (!EnsureInitialized())
311 return true;
313 return sGLFunctions.fGraphicBufferLock(mHandle, GetAndroidUsage(aUsage), bits);
314 }
316 int
317 AndroidGraphicBuffer::Lock(uint32_t aUsage, const nsIntRect& aRect, unsigned char **bits)
318 {
319 if (!EnsureInitialized())
320 return false;
322 ARect rect;
323 rect.left = aRect.x;
324 rect.top = aRect.y;
325 rect.right = aRect.x + aRect.width;
326 rect.bottom = aRect.y + aRect.height;
328 return sGLFunctions.fGraphicBufferLockRect(mHandle, GetAndroidUsage(aUsage), rect, bits);
329 }
331 int
332 AndroidGraphicBuffer::Unlock()
333 {
334 if (!EnsureInitialized())
335 return false;
337 return sGLFunctions.fGraphicBufferUnlock(mHandle);
338 }
340 bool
341 AndroidGraphicBuffer::Reallocate(uint32_t aWidth, uint32_t aHeight, gfxImageFormat aFormat)
342 {
343 if (!EnsureInitialized())
344 return false;
346 mWidth = aWidth;
347 mHeight = aHeight;
348 mFormat = aFormat;
350 // Sometimes GraphicBuffer::reallocate just doesn't work. In those cases we'll just allocate a brand
351 // new buffer. If reallocate fails once, never try it again.
352 if (!gTryRealloc || sGLFunctions.fGraphicBufferReallocate(mHandle, aWidth, aHeight, GetAndroidFormat(aFormat)) != 0) {
353 DestroyBuffer();
354 EnsureBufferCreated();
356 gTryRealloc = false;
357 }
359 return true;
360 }
362 uint32_t
363 AndroidGraphicBuffer::GetAndroidUsage(uint32_t aUsage)
364 {
365 uint32_t flags = 0;
367 if (aUsage & UsageSoftwareRead) {
368 flags |= GRALLOC_USAGE_SW_READ_OFTEN;
369 }
371 if (aUsage & UsageSoftwareWrite) {
372 flags |= GRALLOC_USAGE_SW_WRITE_OFTEN;
373 }
375 if (aUsage & UsageTexture) {
376 flags |= GRALLOC_USAGE_HW_TEXTURE;
377 }
379 if (aUsage & UsageTarget) {
380 flags |= GRALLOC_USAGE_HW_RENDER;
381 }
383 if (aUsage & Usage2D) {
384 flags |= GRALLOC_USAGE_HW_2D;
385 }
387 return flags;
388 }
390 uint32_t
391 AndroidGraphicBuffer::GetAndroidFormat(gfxImageFormat aFormat)
392 {
393 switch (aFormat) {
394 case gfxImageFormat::RGB24:
395 return HAL_PIXEL_FORMAT_RGBX_8888;
396 case gfxImageFormat::RGB16_565:
397 return HAL_PIXEL_FORMAT_RGB_565;
398 default:
399 return 0;
400 }
401 }
403 bool
404 AndroidGraphicBuffer::EnsureEGLImage()
405 {
406 if (mEGLImage)
407 return true;
410 if (!EnsureInitialized())
411 return false;
413 EGLint eglImgAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE };
414 void* nativeBuffer = sGLFunctions.fGraphicBufferGetNativeBuffer(mHandle);
416 mEGLImage = sGLFunctions.fCreateImageKHR(sGLFunctions.fGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)nativeBuffer, eglImgAttrs);
417 return mEGLImage != nullptr;
418 }
420 bool
421 AndroidGraphicBuffer::Bind()
422 {
423 if (!EnsureInitialized())
424 return false;
426 if (!EnsureEGLImage()) {
427 LOG("No valid EGLImage!");
428 return false;
429 }
431 clearGLError();
432 sGLFunctions.fImageTargetTexture2DOES(GL_TEXTURE_2D, mEGLImage);
433 return ensureNoGLError("glEGLImageTargetTexture2DOES");
434 }
436 // Build whitelist to check for board type.
437 static void InitWhiteList(nsTArray<nsString>& list)
438 {
439 nsString ele;
440 ele.AssignASCII("droid2"); // Motorola Droid 2
441 list.AppendElement(ele);
442 ele.AssignASCII("GT-I9100"); // Samsung Galaxy SII
443 list.AppendElement(ele);
444 ele.AssignASCII("herring"); // Samsung Nexus S
445 list.AppendElement(ele);
446 ele.AssignASCII("omap4sdp"); // Amazon Kindle Fire
447 list.AppendElement(ele);
448 ele.AssignASCII("SGH-I897"); // Samsung Galaxy S
449 list.AppendElement(ele);
450 ele.AssignASCII("sgh-i997"); // Samsung Infuse 4G
451 list.AppendElement(ele);
452 ele.AssignASCII("sgh-t839"); // Samsung Sidekick 4G
453 list.AppendElement(ele);
454 ele.AssignASCII("shadow"); // Motorola Droid X
455 list.AppendElement(ele);
456 ele.AssignASCII("spyder"); // Motorola Razr
457 list.AppendElement(ele);
458 ele.AssignASCII("targa"); // Motorola Droid Bionic
459 list.AppendElement(ele);
460 ele.AssignASCII("tuna"); // Galaxy Nexus
461 list.AppendElement(ele);
462 ele.AssignASCII("venus2"); // Motorla Droid Pro
463 list.AppendElement(ele);
464 }
466 bool
467 AndroidGraphicBuffer::IsBlacklisted()
468 {
469 nsAutoString board;
470 if (!AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "BOARD", board))
471 return true;
473 NS_ConvertUTF16toUTF8 boardUtf8(board);
475 if (Preferences::GetBool("direct-texture.force.enabled", false)) {
476 LOG("allowing board '%s' due to prefs override", boardUtf8.get());
477 return false;
478 }
480 if (Preferences::GetBool("direct-texture.force.disabled", false)) {
481 LOG("disallowing board '%s' due to prefs override", boardUtf8.get());
482 return true;
483 }
485 static nsTArray<nsString> sListAllowed;
486 if (sListAllowed.Length() == 0) {
487 InitWhiteList(sListAllowed);
488 }
490 int i = -1;
491 if ((i = sListAllowed.BinaryIndexOf(board)) >= 0) {
492 nsString name = sListAllowed.ElementAt(i);
493 LOG("allowing board '%s' based on '%s'\n", boardUtf8.get(), NS_ConvertUTF16toUTF8(name).get());
494 return false;
495 }
497 LOG("disallowing board: %s\n", boardUtf8.get());
498 return true;
499 }
501 } /* mozilla */