Replacing glReadPixels with EGL_KHR_image_base for a faster pixel copy

I am trying to use EGL_KHR_image_base in my own Android process to replace glReadPixels because it has to slow down (220ms for 1280x800 RGBA).

This is what I have so far, but it creates an empty buffer for me (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?

+4
android android-ndk egl
source share
2 answers

You create an empty buffer and then read the contents from it. Code walk:

 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 called a "gralloc buffer") with the specified pixel size and format. Use flags allow you to use it as a texture or read from software, which is what you need.

 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 an 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 an EGLImage to it. So, now 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 data from it, and unlocks it. Since you did not draw anything, there is nothing to read.

To do something useful, you need to do something in the texture. You can do this by creating an FBO and attaching the texture to it as a color buffer, or using glCopyTexImage2D() to copy pixels from the framebuffer to the texture.

I managed to get your example to work by adding the following before calling grallocBuffer->lock() :

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

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

Edit: My assistant office suggested that GL_TEXTURE_2D should be GL_TEXTURE_EXTERNAL_OES . I have not tried it yet.

Edit: see also this question .

+4
source share

GraphicBuffer is part of the Android source code, not the NDK. see this for reference: https://github.com/fuyufjh/GraphicBuffer

0
source share

All Articles