6

I am trying to use EGL_KHR_image_base in an android native process in order to replace glReadPixels because it is to slow ( 220ms for 1280x800 RGBA ).

This is what I have so far, but my it produces an empty buffer ( only zeros )

uint8_t *ptr;
GLuint mTexture;
status_t error;

GraphicBufferAlloc* mGraphicBufferAlloc  = new GraphicBufferAlloc();    
sp<GraphicBuffer> window = mGraphicBufferAlloc->createGraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_HW_TEXTURE,&error);
EGLClientBuffer buffer = (EGLClientBuffer)window->getNativeBuffer();
EGLint eglImageAttributes[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_MATCH_FORMAT_KHR,  EGL_FORMAT_RGBA_8888_KHR, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
EGLImageKHR image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,buffer, eglImageAttributes);

glGenTextures(1, &mTexture);    
glBindTexture(GL_TEXTURE_2D, mTexture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
window->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, (void**)&ptr); 
memcpy(texture, ptr, width * height * 4);
window->unlock();

What am I doing wrong ?

jacob
  • 1,397
  • 1
  • 26
  • 53

2 Answers2

8

You're creating an empty buffer and then reading the contents out of it. Walking through the code:

GraphicBufferAlloc* mGraphicBufferAlloc = new GraphicBufferAlloc();
sp<GraphicBuffer> window = mGraphicBufferAlloc->createGraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_HW_TEXTURE,&error);

This creates a new GraphicBuffer (sometimes referred to as a "gralloc buffer"), with the specified dimensions and pixel format. The usage flags allow it to be used as a texture or read from software, which is what you want.

EGLClientBuffer buffer = (EGLClientBuffer)window->getNativeBuffer();
EGLint eglImageAttributes[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_MATCH_FORMAT_KHR,  EGL_FORMAT_RGBA_8888_KHR, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
EGLImageKHR image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,buffer, eglImageAttributes);

This takes the ANativeWindow object (which is a queue of buffers under the hood) and attaches an EGLImage "handle" to it.

glGenTextures(1, &mTexture);    
glBindTexture(GL_TEXTURE_2D, mTexture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);

This creates a new texture object, and attaches the EGLImage to it. So now the ANativeWindow can be used as a texture object.

window->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, (void**)&ptr); 
memcpy(texture, ptr, width * height * 4);
window->unlock();

This locks the buffer for reading, copies the data out of it, and unlocks it. Since you haven't drawn anything, there's nothing to read.

For this to do something useful, you have to render something into the texture. You can do this by creating an FBO and attaching the texture to it as the color buffer, or by using glCopyTexImage2D() to copy pixels from the framebuffer to the texture.

I was able to get your example to work by adding the following before the call to grallocBuffer->lock():

glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0);
glFinish();

The glFinish() is necessary to ensure that GL has finished copying the pixels before we try to look at them.

Edit: my office-mate suggested that the GL_TEXTURE_2D needs to be GL_TEXTURE_EXTERNAL_OES. Haven't tried it yet.

Edit: see also this question.

Community
  • 1
  • 1
fadden
  • 51,356
  • 5
  • 116
  • 166
  • Thanks, for you help, did it work for you ? I am still getting an empty buffer ( although now it takes about 50ms ) even after adding: glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0); glFinish(); can you please take a look: https://dl.dropboxusercontent.com/u/36894441/Overlay.cpp https://dl.dropboxusercontent.com/u/36894441/EglWindow.cpp – jacob Jan 18 '14 at 16:52
  • I now see that its not an entirely empty buffer, but only the beginning of every 4096000, is with data and all the rest is zeros. can you post the code you tried ? – jacob Jan 19 '14 at 08:55
  • Hmm, I wasn't saving the frame off as an image, just checking to see if it wasn't all zeroes. At the start of your function, add `glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT);`. That will fill the frame with 50% grey, ensuring that none of the byte values are zero. What do you see then? (It'll be a couple of days before I can post my experiment.) – fadden Jan 19 '14 at 16:37
  • On the Nexus 5, where I was testing, I got what looks like good data out, but `glCopyTexImage2D()` starts throwing errors after the first frame. On the Nexus 10, everything returns without errors so long as I don't specify any attributes to `eglCreateImageKHR()`, but the buffer is always filled with zeroes (same thing you see). So something isn't quite right. – fadden Feb 03 '14 at 18:55
  • @fadden I want to copy pixel, I has copied from PBO when the mobile phone support OpenGL es 3.0, and when they only support OpenGL es 2.0, I want use GraphicBufferAlloc in Android NDK proj. but I got an error "error: 'GraphicBufferAlloc' was not declared in this scope" when I compile the cpp file, I don't know which header file need import. Could u help me, plz? my include files are as follows: #include #include #include #include #include #include #include – dragonfly Dec 23 '15 at 13:40
  • @jacob Dear jacob, I have tried to do the same thing as you, but I event can't compile the source codes successfully with ndk. The reason should be that GraphicBufferAlloc is not in ndk. But why can you compile the source codes? How did you make it? Could you please give me some help? – dragonfly Jan 06 '16 at 05:10
0

GraphicBuffer is a part of Android Source Code, not in NDK. see this for your reference: https://github.com/fuyufjh/GraphicBuffer

Anptk
  • 1,125
  • 2
  • 17
  • 28