|
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/. */ |
|
5 |
|
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" |
|
13 |
|
14 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidGraphicBuffer" , ## args) |
|
15 |
|
16 #define EGL_NATIVE_BUFFER_ANDROID 0x3140 |
|
17 #define EGL_IMAGE_PRESERVED_KHR 0x30D2 |
|
18 |
|
19 typedef void *EGLContext; |
|
20 typedef void *EGLDisplay; |
|
21 typedef uint32_t EGLenum; |
|
22 typedef int32_t EGLint; |
|
23 typedef uint32_t EGLBoolean; |
|
24 |
|
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 |
|
30 |
|
31 #define ANDROID_LIBUI_PATH "libui.so" |
|
32 #define ANDROID_GLES_PATH "libGLESv2.so" |
|
33 #define ANDROID_EGL_PATH "libEGL.so" |
|
34 |
|
35 // Really I have no idea, but this should be big enough |
|
36 #define GRAPHIC_BUFFER_SIZE 1024 |
|
37 |
|
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, |
|
47 |
|
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, |
|
56 |
|
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 }; |
|
68 |
|
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 }; |
|
78 |
|
79 typedef struct ARect { |
|
80 int32_t left; |
|
81 int32_t top; |
|
82 int32_t right; |
|
83 int32_t bottom; |
|
84 } ARect; |
|
85 |
|
86 static bool gTryRealloc = true; |
|
87 |
|
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 } |
|
108 |
|
109 typedef EGLDisplay (* pfnGetDisplay)(void *display_id); |
|
110 pfnGetDisplay fGetDisplay; |
|
111 typedef EGLint (* pfnEGLGetError)(void); |
|
112 pfnEGLGetError fEGLGetError; |
|
113 |
|
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; |
|
118 |
|
119 typedef void (* pfnImageTargetTexture2DOES)(GLenum target, EGLImageKHR image); |
|
120 pfnImageTargetTexture2DOES fImageTargetTexture2DOES; |
|
121 |
|
122 typedef void (* pfnBindTexture)(GLenum target, GLuint texture); |
|
123 pfnBindTexture fBindTexture; |
|
124 |
|
125 typedef GLenum (* pfnGLGetError)(); |
|
126 pfnGLGetError fGLGetError; |
|
127 |
|
128 typedef void (*pfnGraphicBufferCtor)(void*, uint32_t w, uint32_t h, uint32_t format, uint32_t usage); |
|
129 pfnGraphicBufferCtor fGraphicBufferCtor; |
|
130 |
|
131 typedef void (*pfnGraphicBufferDtor)(void*); |
|
132 pfnGraphicBufferDtor fGraphicBufferDtor; |
|
133 |
|
134 typedef int (*pfnGraphicBufferLock)(void*, uint32_t usage, unsigned char **addr); |
|
135 pfnGraphicBufferLock fGraphicBufferLock; |
|
136 |
|
137 typedef int (*pfnGraphicBufferLockRect)(void*, uint32_t usage, const ARect&, unsigned char **addr); |
|
138 pfnGraphicBufferLockRect fGraphicBufferLockRect; |
|
139 |
|
140 typedef int (*pfnGraphicBufferUnlock)(void*); |
|
141 pfnGraphicBufferUnlock fGraphicBufferUnlock; |
|
142 |
|
143 typedef void* (*pfnGraphicBufferGetNativeBuffer)(void*); |
|
144 pfnGraphicBufferGetNativeBuffer fGraphicBufferGetNativeBuffer; |
|
145 |
|
146 typedef int (*pfnGraphicBufferReallocate)(void*, uint32_t w, uint32_t h, uint32_t format); |
|
147 pfnGraphicBufferReallocate fGraphicBufferReallocate; |
|
148 |
|
149 bool EnsureInitialized() |
|
150 { |
|
151 if (mInitialized) { |
|
152 return true; |
|
153 } |
|
154 |
|
155 void *handle = dlopen(ANDROID_EGL_PATH, RTLD_LAZY); |
|
156 if (!handle) { |
|
157 LOG("Couldn't load EGL library"); |
|
158 return false; |
|
159 } |
|
160 |
|
161 fGetDisplay = (pfnGetDisplay)dlsym(handle, "eglGetDisplay"); |
|
162 fEGLGetError = (pfnEGLGetError)dlsym(handle, "eglGetError"); |
|
163 fCreateImageKHR = (pfnCreateImageKHR)dlsym(handle, "eglCreateImageKHR"); |
|
164 fDestroyImageKHR = (pfnDestroyImageKHR)dlsym(handle, "eglDestroyImageKHR"); |
|
165 |
|
166 if (!fGetDisplay || !fEGLGetError || !fCreateImageKHR || !fDestroyImageKHR) { |
|
167 LOG("Failed to find some EGL functions"); |
|
168 return false; |
|
169 } |
|
170 |
|
171 handle = dlopen(ANDROID_GLES_PATH, RTLD_LAZY); |
|
172 if (!handle) { |
|
173 LOG("Couldn't load GL library"); |
|
174 return false; |
|
175 } |
|
176 |
|
177 fImageTargetTexture2DOES = (pfnImageTargetTexture2DOES)dlsym(handle, "glEGLImageTargetTexture2DOES"); |
|
178 fBindTexture = (pfnBindTexture)dlsym(handle, "glBindTexture"); |
|
179 fGLGetError = (pfnGLGetError)dlsym(handle, "glGetError"); |
|
180 |
|
181 if (!fImageTargetTexture2DOES || !fBindTexture || !fGLGetError) { |
|
182 LOG("Failed to find some GL functions"); |
|
183 return false; |
|
184 } |
|
185 |
|
186 handle = dlopen(ANDROID_LIBUI_PATH, RTLD_LAZY); |
|
187 if (!handle) { |
|
188 LOG("Couldn't load libui.so"); |
|
189 return false; |
|
190 } |
|
191 |
|
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"); |
|
199 |
|
200 if (!fGraphicBufferCtor || !fGraphicBufferDtor || !fGraphicBufferLock || |
|
201 !fGraphicBufferUnlock || !fGraphicBufferGetNativeBuffer) { |
|
202 LOG("Failed to lookup some GraphicBuffer functions"); |
|
203 return false; |
|
204 } |
|
205 |
|
206 mInitialized = true; |
|
207 return true; |
|
208 } |
|
209 |
|
210 private: |
|
211 bool mInitialized; |
|
212 |
|
213 } sGLFunctions; |
|
214 |
|
215 namespace mozilla { |
|
216 |
|
217 static void clearGLError() |
|
218 { |
|
219 while (glGetError() != GL_NO_ERROR); |
|
220 } |
|
221 |
|
222 static bool ensureNoGLError(const char* name) |
|
223 { |
|
224 bool result = true; |
|
225 GLuint error; |
|
226 |
|
227 while ((error = glGetError()) != GL_NO_ERROR) { |
|
228 LOG("GL error [%s]: %40x\n", name, error); |
|
229 result = false; |
|
230 } |
|
231 |
|
232 return result; |
|
233 } |
|
234 |
|
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 } |
|
245 |
|
246 AndroidGraphicBuffer::~AndroidGraphicBuffer() |
|
247 { |
|
248 DestroyBuffer(); |
|
249 } |
|
250 |
|
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; |
|
274 |
|
275 if (mHandle) { |
|
276 if (sGLFunctions.EnsureInitialized()) { |
|
277 sGLFunctions.fGraphicBufferDtor(mHandle); |
|
278 } |
|
279 free(mHandle); |
|
280 mHandle = nullptr; |
|
281 } |
|
282 |
|
283 } |
|
284 |
|
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 } |
|
292 |
|
293 return true; |
|
294 } |
|
295 |
|
296 bool |
|
297 AndroidGraphicBuffer::EnsureInitialized() |
|
298 { |
|
299 if (!sGLFunctions.EnsureInitialized()) { |
|
300 return false; |
|
301 } |
|
302 |
|
303 EnsureBufferCreated(); |
|
304 return true; |
|
305 } |
|
306 |
|
307 int |
|
308 AndroidGraphicBuffer::Lock(uint32_t aUsage, unsigned char **bits) |
|
309 { |
|
310 if (!EnsureInitialized()) |
|
311 return true; |
|
312 |
|
313 return sGLFunctions.fGraphicBufferLock(mHandle, GetAndroidUsage(aUsage), bits); |
|
314 } |
|
315 |
|
316 int |
|
317 AndroidGraphicBuffer::Lock(uint32_t aUsage, const nsIntRect& aRect, unsigned char **bits) |
|
318 { |
|
319 if (!EnsureInitialized()) |
|
320 return false; |
|
321 |
|
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; |
|
327 |
|
328 return sGLFunctions.fGraphicBufferLockRect(mHandle, GetAndroidUsage(aUsage), rect, bits); |
|
329 } |
|
330 |
|
331 int |
|
332 AndroidGraphicBuffer::Unlock() |
|
333 { |
|
334 if (!EnsureInitialized()) |
|
335 return false; |
|
336 |
|
337 return sGLFunctions.fGraphicBufferUnlock(mHandle); |
|
338 } |
|
339 |
|
340 bool |
|
341 AndroidGraphicBuffer::Reallocate(uint32_t aWidth, uint32_t aHeight, gfxImageFormat aFormat) |
|
342 { |
|
343 if (!EnsureInitialized()) |
|
344 return false; |
|
345 |
|
346 mWidth = aWidth; |
|
347 mHeight = aHeight; |
|
348 mFormat = aFormat; |
|
349 |
|
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(); |
|
355 |
|
356 gTryRealloc = false; |
|
357 } |
|
358 |
|
359 return true; |
|
360 } |
|
361 |
|
362 uint32_t |
|
363 AndroidGraphicBuffer::GetAndroidUsage(uint32_t aUsage) |
|
364 { |
|
365 uint32_t flags = 0; |
|
366 |
|
367 if (aUsage & UsageSoftwareRead) { |
|
368 flags |= GRALLOC_USAGE_SW_READ_OFTEN; |
|
369 } |
|
370 |
|
371 if (aUsage & UsageSoftwareWrite) { |
|
372 flags |= GRALLOC_USAGE_SW_WRITE_OFTEN; |
|
373 } |
|
374 |
|
375 if (aUsage & UsageTexture) { |
|
376 flags |= GRALLOC_USAGE_HW_TEXTURE; |
|
377 } |
|
378 |
|
379 if (aUsage & UsageTarget) { |
|
380 flags |= GRALLOC_USAGE_HW_RENDER; |
|
381 } |
|
382 |
|
383 if (aUsage & Usage2D) { |
|
384 flags |= GRALLOC_USAGE_HW_2D; |
|
385 } |
|
386 |
|
387 return flags; |
|
388 } |
|
389 |
|
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 } |
|
402 |
|
403 bool |
|
404 AndroidGraphicBuffer::EnsureEGLImage() |
|
405 { |
|
406 if (mEGLImage) |
|
407 return true; |
|
408 |
|
409 |
|
410 if (!EnsureInitialized()) |
|
411 return false; |
|
412 |
|
413 EGLint eglImgAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE }; |
|
414 void* nativeBuffer = sGLFunctions.fGraphicBufferGetNativeBuffer(mHandle); |
|
415 |
|
416 mEGLImage = sGLFunctions.fCreateImageKHR(sGLFunctions.fGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)nativeBuffer, eglImgAttrs); |
|
417 return mEGLImage != nullptr; |
|
418 } |
|
419 |
|
420 bool |
|
421 AndroidGraphicBuffer::Bind() |
|
422 { |
|
423 if (!EnsureInitialized()) |
|
424 return false; |
|
425 |
|
426 if (!EnsureEGLImage()) { |
|
427 LOG("No valid EGLImage!"); |
|
428 return false; |
|
429 } |
|
430 |
|
431 clearGLError(); |
|
432 sGLFunctions.fImageTargetTexture2DOES(GL_TEXTURE_2D, mEGLImage); |
|
433 return ensureNoGLError("glEGLImageTargetTexture2DOES"); |
|
434 } |
|
435 |
|
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 } |
|
465 |
|
466 bool |
|
467 AndroidGraphicBuffer::IsBlacklisted() |
|
468 { |
|
469 nsAutoString board; |
|
470 if (!AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "BOARD", board)) |
|
471 return true; |
|
472 |
|
473 NS_ConvertUTF16toUTF8 boardUtf8(board); |
|
474 |
|
475 if (Preferences::GetBool("direct-texture.force.enabled", false)) { |
|
476 LOG("allowing board '%s' due to prefs override", boardUtf8.get()); |
|
477 return false; |
|
478 } |
|
479 |
|
480 if (Preferences::GetBool("direct-texture.force.disabled", false)) { |
|
481 LOG("disallowing board '%s' due to prefs override", boardUtf8.get()); |
|
482 return true; |
|
483 } |
|
484 |
|
485 static nsTArray<nsString> sListAllowed; |
|
486 if (sListAllowed.Length() == 0) { |
|
487 InitWhiteList(sListAllowed); |
|
488 } |
|
489 |
|
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 } |
|
496 |
|
497 LOG("disallowing board: %s\n", boardUtf8.get()); |
|
498 return true; |
|
499 } |
|
500 |
|
501 } /* mozilla */ |