Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fuyufjh/graphicbuffer
Use GraphicBuffer class from Android native code
https://github.com/fuyufjh/graphicbuffer
android
Last synced: 19 days ago
JSON representation
Use GraphicBuffer class from Android native code
- Host: GitHub
- URL: https://github.com/fuyufjh/graphicbuffer
- Owner: fuyufjh
- License: apache-2.0
- Created: 2016-06-14T07:13:46.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2021-03-30T02:48:41.000Z (over 3 years ago)
- Last Synced: 2024-10-14T09:29:59.186Z (about 1 month ago)
- Topics: android
- Language: C++
- Homepage:
- Size: 25.4 KB
- Stars: 200
- Watchers: 11
- Forks: 53
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# GraphicBuffer
Use GraphicBuffer class in Android native code in your project, without compiling with Android source code.
This repository is for APIs 23-27. API 23 is supported without additional tricks, APIs 24-25 need making your application a system application.
APIs 26 and 27 do not need code from this repository since a more convenient alternative is available: `HardwareBuffer`.
Moreover, this README provides an example of usage of the buffer to obtain a rendered texture image using simple and fast `memcpy()` calls, both for `GraphicBuffer` (API <= 23) and `HardwareBuffer` (API >= 26).
Inspired by [tcuAndroidInternals.cpp](https://android.googlesource.com/platform/external/deqp/+/master/framework/platform/android/tcuAndroidInternals.cpp)
# How to use
The usage is exactly the same with `android::GraphicBuffer` on API <= 25 or `HardwareBuffer` on API >= 26.
The example below shows a pseudo-code which renders something to a texture attached to a framebuffer and get the result using simple `memcpy()` calls.
Examples for both API >= 26 (HardwareBuffer) and API < 26 (GraphicBuffer) are provided.
If something doesn't work, it's worth checking if pointers are valid, if `eglGetError()` shows no issues, if there are any errors from Android system and also checking return codes with `glGetError()` if drawing issues occur.An example for API <= 25 using this repository, GraphicBuffer:
```c++
// for EGL calls
#define EGL_EGLEXT_PROTOTYPES
#include "EGL/egl.h"
#include "EGL/eglext.h"
#include "GLES/gl.h"
#define GL_GLEXT_PROTOTYPES
#include "GLES/glext.h"// Use code from this repository. Note that define __ANDROID_API__ must be set properly for it to work
// Also add -lEGL at link stage
#include "GraphicBuffer.h"// bind FBO (create FBO my_handle first!)
glBindFramebuffer(GL_FRAMEBUFFER, my_handle);// attach texture to FBO (create texture my_texture first!)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, my_texture, 0);// usage for the GraphicBuffer
int usage = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_NEVER;// create GraphicBuffer
GraphicBuffer* graphicBuf = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, usage);// get the native buffer
auto clientBuf = (EGLClientBuffer) graphicBuf->getNativeBuffer();// obtaining the EGL display
EGLDisplay disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);// specifying the image attributes
EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};// creating an EGL image
EGLImageKHR imageEGL = eglCreateImageKHR(disp, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf, eglImageAttributes);// Doing some OpenGL rendering like glDrawArrays
// Shaders also work, need `#extension GL_OES_EGL_image_external : require`
// Now the result is inside the FBO my_handle// binding the OUTPUT texture
glBindTexture(GL_TEXTURE_2D, my_texture);// attaching an EGLImage to OUTPUT texture
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, imageEGL);// Obtaining the content image:
// pointer for reading and writing texture data
void *readPtr, *writePtr;// locking the buffer
graphicBuf->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &readPtr);// setting the write pointer
writePtr =// obtaining the stride (for me it was always = width)
int stride = graphicBuf->getStride();// loop over texture rows
for (int row = 0; row < height; row++) {
// copying, 4 = 4 channels RGBA because of the format above
memcpy(writePtr, readPtr, width * 4);// adding stride * 4 to read pointer
readPtr = (void *)(int(readPtr) + stride * 4);// adding width * 4 to write pointer
writePtr = (void *)(int(writePtr) + width * 4);
}// NOW data is in writePtr memory
// unlocking the buffer
graphicBuf->unlock();
```Example for API >= 26. This repository is NOT needed, because there is an open alternative in NDK [1].
The example does exactly the same thing as the one above.
```c++
// for EGL calls
#define EGL_EGLEXT_PROTOTYPES
#include "EGL/egl.h"
#include "EGL/eglext.h"
#include "GLES/gl.h"
#define GL_GLEXT_PROTOTYPES
#include "GLES/glext.h"// for API >= 26
// Also add -lEGL -lnativewindow -lGLESv3 at link stage
#include "android/hardware_buffer.h"// bind FBO (create FBO my_handle first!)
glBindFramebuffer(GL_FRAMEBUFFER, my_handle);// attach texture to FBO (create texture my_texture first!)
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, my_texture, 0);// OUR parameters that we will set and give it to AHardwareBuffer
AHardwareBuffer_Desc usage;// filling in the usage for HardwareBuffer
usage.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
usage.height = outputHeight;
usage.width = outputWidth;
usage.layers = 1;
usage.rfu0 = 0;
usage.rfu1 = 0;
usage.stride = 10;
usage.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER
| AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;// create GraphicBuffer
AHardwareBuffer* graphicBuf;
AHardwareBuffer_allocate(&usage, &graphicBuf); // it's worth to check the return code// ACTUAL parameters of the AHardwareBuffer which it reports
AHardwareBuffer_Desc usage1;// for stride, see below
AHardwareBuffer_describe(graphicBuf, &usage1);// get the native buffer
EGLClientBuffer clientBuf = eglGetNativeClientBufferANDROID(graphicBuf);// obtaining the EGL display
EGLDisplay disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);// specifying the image attributes
EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};// creating an EGL image
EGLImageKHR imageEGL = eglCreateImageKHR(disp, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf, eglImageAttributes);
/**
* @note this part should be earlies than any draw or framebuffer options.
* @note refer to answer of @solidpixel at https://stackoverflow.com/questions/64447069/use-gleglimagetargettexture2does-to-replace-glreadpixels-on-android
* @{
*/
// binding the OUTPUT texture
gl->glBindTexture(GL_TEXTURE_2D, my_texture);// attaching an EGLImage to OUTPUT texture
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, imageEGL);
/**
* @}
*/
// Doing some OpenGL rendering like glDrawArrays
// Shaders also work, need `#extension GL_OES_EGL_image_external_essl3 : require`
// Now the result is inside the FBO my_handle// Obtaining the content image:
// pointer for reading and writing texture data
void *readPtr, *writePtr;
/**
* @note We must make sure all drawing options finished before read back.
* @{
*/
glFinish();
/**
* @}
*/
// locking the buffer
AHardwareBuffer_lock(graphicBuf, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, (void**) &readPtr); // worth checking return code// setting the write pointer
writePtr =// obtaining the stride (for me it was always = width)
int stride = usage1.stride;// loop over texture rows
for (int row = 0; row < height; row++) {
// copying, 4 = 4 channels RGBA because of the format above
memcpy(writePtr, readPtr, width * 4);// adding stride * 4 to read pointer
readPtr = (void *)(int(readPtr) + stride * 4);// adding width * 4 to write pointer
writePtr = (void *)(int(writePtr) + width * 4);
}// NOW data is in writePtr memory
// unlocking the buffer
AHardwareBuffer_unlock(graphicBuf, nullptr); // worth checking return code
```# How to access private libraries on API 24-25
On API 26, there is a public `HardwareBuffer` [1] option which replaces GraphicBuffer hacks. On API <= 23 the hack from the repo worked because the access to private libraries such as `libui.so` was allowed.It's still allowed [2] in 24-25, however, `libui.so` also requires `gralloc.exynos5.so` (see full list of its dependencies [3]) which is **not allowed to use on API 24-25**. The app is killed when trying to dlopen `libui.so` (on `new GraphicBuffer()`).
It seems that there is a solution for API <= 23 and for API >= 26, but on 24 and 25 it seems that it's impossible to use any kind of GraphicBuffer-like access.
The solution for API 24-25, along with using code from this repository, is to make your application a system application.
It requires root privileges.
The process is described in https://stackoverflow.com/questions/24641604/qt-application-as-system-app-on-android for Qt-based apps.# How to tweak API
The API 23 version in https://github.com/fuyufjh/GraphicBuffer/blob/fa346e1f6266a717758d32aee9c75c85da8a7263/GraphicBuffer.cpp uses the `_ZN7android13GraphicBufferC1Ejjij` constructor symbol, which was replaced by `_ZN7android13GraphicBufferC1EjjijNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE` in API 24-25 (a std::string argument added at the end). This repository works for the API 24-25 as well.
Since I'm not sure if any other APIs have different constructors, below you can find directions on how to tweak the code for your API.
1. Copy your file `/system/lib/libui.so` from your Android device to your PC. This is the file that contains symbol names for `GraphicBuffer`.
2. Using Android NDK's `nm` for your architecture, run:
```
$ /somewhere/android-ndk/find-it/arm-linux-androideabi-gcc-nm -C -D libui.so | grep GraphicBuffer | sort
```It will produce output similar to this:
```
0000de2c T android::GraphicBuffer::GraphicBuffer()
0000de2c T android::GraphicBuffer::GraphicBuffer()
0000dec8 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, std::__1::basic_string, std::__1::allocator >)
0000dec8 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, std::__1::basic_string, std::__1::allocator >)
0000dfd8 T android::GraphicBuffer::initSize(unsigned int, unsigned int, int, unsigned int, std::__1::basic_string, std::__1::allocator >)
0000e074 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool)
0000e074 T android::GraphicBuffer::GraphicBuffer(unsigned int, unsigned int, int, unsigned int, unsigned int, native_handle*, bool)
0000e120 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool)
0000e120 T android::GraphicBuffer::GraphicBuffer(ANativeWindowBuffer*, bool)```
Find a constructor that is suitable for you. Try googling for source code of `GraphicBuffer.cpp` for your API, for example: https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/ui/GraphicBuffer.cpp. Once you have identified the constructor signature you would like to use, find the name of it's symbol in
```
# same as above but without -C
$ /somewhere/android-ndk/find-it/arm-linux-androideabi-gcc-nm -D libui.so | grep GraphicBuffer | sort
```Copy the name of the symbol, for example: `_ZN7android13GraphicBufferC1EjjijNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE`. It corresponds 1-to-1 to the human-readable signature. For my API 24-25 the difference from API 23 code was that there was an `std::string` argument added.
For lower APIs (unclear which ones) the solution uses `_ZN7android13GraphicBufferC1Ejjij` which is absent in API 24-25.
3. Having identified your constructor, change the code in `GraphicBuffer.cpp` appropriately. For API 24-25 a `std::string` argument was added, it has to be passed by reference and the variable with the string should remain after function call ends (I just made it `static`).
4. Debug the code using `print` statements showing error codes[1] https://developer.android.com/ndk/guides/stable_apis https://developer.android.com/reference/android/hardware/HardwareBuffer
[2] https://developer.android.com/about/versions/nougat/android-7.0-changes
[3] `libbacktrace.so libbase.so libbinder.so libc.so libc++.so libcutils.so libdl.so gralloc.exynos5.so libhardware.so libion.so liblog.so liblzma.so libm.so libsync.so libui.so libunwind.so libutils.so`, obtained by a simple recursive jupyter notebook using `!{readelf_bin} -d {file} | grep NEEDED`