Hello,
My application needs to retrieve a lot of data from GPU. So, as once recommended on ARM dev forums, I use android GraphicBuffer for this purpose as glReadPixels slows down the process quite heavily (by approx. 10 ms per frame, which is disastrous at 30 fps). But, when I render something into it and then try to read it via GraphicBuffer, the memory pointed at the address I get with buffer->lock() contains only partially updated image data (e.g. some of the pixels contain the data from the last draw call, and another pixels contain the data which was in that texture before I made the draw call).
So, here is what I do:
1) Create android graphics buffer
int format = PIXEL_FORMAT_RGBA_8888; int usage = GraphicBuffer::USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_RARELY; EGLClientBuffer clientBuffer = nullptr; buffer = new android::GraphicBuffer(width, height, format, usage); clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
2) Create EglKHR image with this buffer attached to it
EGLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE}; img = eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer) clientBuffer, eglImgAttrs);
3) Attach Egl KHR image to texture
glGenTextures(1, &androidTextureId); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, androidTextureId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Attach the EGLImage to whatever texture is bound to GL_TEXTURE_2D glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img); glBindTexture(GL_TEXTURE_2D, 0);
4) Then, I get frames from camera which are rendered into external texture, which I render into framebuffer to which graphicsBuffer-backed texture is attached
glBindFramebuffer(GL_FRAMEBUFFER, framebufferId); glViewport(0, 0, width, height); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, androidTextureId, 0); //render here, I've skipped it glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0 ); glBindFramebuffer(GL_FRAMEBUFFER, 0);
5) Then, I call glFinish(), lock the graphicsBuffer for reading and load it's data into another texture I'll write on the screen later, just to check that everything is ok.
glFinish(); void *memPtr; buffer->lock(mode, &memPtr); glActiveTexture(GL_TEXTURE0); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, textureToRenderId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->stride, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, memPtr); unlockGraphicsBuffer(); //render here, I've skipped it glBindTexture(GL_TEXTURE_2D, 0); glUseProgram(0);
6) Then, I write zeros to graphicBuffer-backed texture (by writing into it's graphicBuffer counterpart) so I'll clearly see the artifacts. If I skip this step, the artifacts are still present but harder to notice, because two sequential camera frames look similar unless you move the device quite quickly.
This code works absolutely fine on many another devices of different vendors, with one of the problematic devices being Samsung S3 mentioned above.
There was a similar problem on lenovo vibe x2 with PowerVR G6400 gpu (which occured because PowerVR ignores glFinish on newer models completely), and was fixed by addition of fence sync objects before locking the buffer.
For devices which don't support opengl es 3.0, I call only glFinish(), and for SGS3 this doesn't work for some reason
Could anyone please tell what can be wrong with this code?
I can email you the sample project with this problem or provide any other additional info.
Thank you.
Hi sfairat,
From your description, do you mean the corrupted image can be seen because gpu does not finish the job after calling glFinish?
May I ask which android and mali driver version you are using?
Would you please replace glFinish with the following code to see what happens and let us know the result?
EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
if (sync != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync,EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/);
eglDestroySyncKHR(mEGLDisplay, sync);
}
Thanks,
Frank
Hello, frank.lei
Thank you very much for the reply,
Well, from what I see, glFinish seems to almost fulfill it's task, as without it it looks like this, so I assume that gpu does finish it's job, however, the memory pointed by graphicsBuffer isn't being "synced" immediately for some reason. If I add a 20 millisec usleep, then everything is OK.
If I replace glFinish by egl fence sync object, the result is essentially the same as if calling glFinish.
About the device info, it's Android 4.3. I'm not sure where to get the mali driver version, could you please provide the info how to get it?
Thank you,
Fedor.
One way to determine the Mali driver version is to do the following:
adb pull /system/lib/libMali.so strings libMali.so | grep REVISION
adb pull /system/lib/libMali.so
strings libMali.so | grep REVISION
You should see something similar to r3p2-01rel3 for example.
A possibility for this issue is that glFinish may only be affecting the currently bound FBO.
For example if you are rendering to FB1 and then bind to FB0 and then call glFinish, it may only sync on FB0 and not all previous FBO's.
This was resolved last year in our driver already.
A potential workaround (and to help determine whether this is the same issue) would be to call glFinish when the FBO being rendered to is still bound, e.g. calling glFinish on FB1 before binding FB0.
If this doesn't help, then I would like to ask for a simple reproducer for us to begin investigation into this issue.
Kind Regards,
Michael McGeagh
Hello, mcgeagh,
The revision is exactly like in your post, REVISION=Linux-r3p2-01rel3.
Unfortunately, the suggestion you've provided didn't help.
So, here is the link to a simple demo which has this problem. Please note, that of all devices we have around there issue only occurs on SGS3 with latest official updates installed (android 4.3, revision of the driver was mentioned above).
Please feel free to ask any questions.
Thank you very much for help.
Hi Fedor,
One question about your original application. Are you using multiple graphics contexts?
Instead of glFinish() can you please try glFlush() and lets us know if that solves the issue.
Wasim
Hello, wasimabbas,
No, we're using a single graphics context.
I've already tried glFlush, glFinish, fence sync objects and other things in various places, and it didn't help.
Anyway, thank you for the suggestion.
Hello, everyone,
Didn't you have a look at the sample project? Maybe I'm just missing something simple yet important.
Hi,
We have raised this to the team responsible for the driver and will be investigating further.
Once we have any further information for you, we will let you know.
Thank you for your patience
Just an update,
Not much progress has been made as we cannot fully reproduce the issue.
There isnt anything obvious that we can see in the code either.
Can you help us reproduce the issue more reliably?
For example, an APK instead of sources, explicit instructions on how to reliably reproduce the issue, etc.
I have the same problem. In my case libMali.so does not contain a string "REVISION", so I am unable to tell you the driver version, but it is a Mali-450 MP4.
In conjunction with frank.lei's advice, where glFinish is substituted with EGL synchronization primitive, when locking the GraphicBuffer, I use
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN
In my usecase I only need GRALLOC_USAGE_SW_READ_OFTEN, since there is only reading going on. For whatever reason, locking also with GRALLOC_USAGE_SW_WRITE_OFTEN, seem to solve the problem. To be honest, I am not sure if this is related at all, because in my case the artifacts appear rarely. It may be just a coincidence, that the extra bit of work introduced removes the concurrency issue.
In your post it is unclear with what usage mode you are locking the buffer, but I would assume it is GRALLOC_USAGE_SW_WRITE_RARELY.