Loading textures in a background thread on Android

I’m writing an Android application with OpenGL ES 2.0 that requires frequent texture loading and unloading. The optimal solution for me is to do this on a background thread to maintain responsiveness and to keep the architecture simple. It would also be easiest to handle everything in Java’s GLSurfaceView; I’m trying to stay away from writing any native code.

So with those constraints in mind, what’s the right way to load textures on a non-render thread in Android? I’ve found surprisingly little information on this topic. I know I need a second EGLContext and that I probably have to bind it on the loading thread with eglMakeCurrent(). That call also requires EGLDisplay and EGLSurface objects. I know I can get the EGL object with EGLContext.getEGL(). With that, I can get the current context, display, and surface(s). Should it be as simple as getting these object references and then calling eglMakeCurrent() on the loading thread? Or is there more to it? Do i need to hook into the EGLContextFactory and EGLWindowSurfaceFactory? Do I actually need a second GLSurfaceView?

Any direction you all can provide will be greatly appreciated.

[QUOTE=suspense;29397]I’m writing an Android application with OpenGL ES 2.0 that requires frequent texture loading and unloading. The optimal solution for me is to do this on a background thread to maintain responsiveness and to keep the architecture simple. It would also be easiest to handle everything in Java’s GLSurfaceView; I’m trying to stay away from writing any native code.

So with those constraints in mind, what’s the right way to load textures on a non-render thread in Android? I’ve found surprisingly little information on this topic. I know I need a second EGLContext and that I probably have to bind it on the loading thread with eglMakeCurrent(). That call also requires EGLDisplay and EGLSurface objects. I know I can get the EGL object with EGLContext.getEGL(). With that, I can get the current context, display, and surface(s). Should it be as simple as getting these object references and then calling eglMakeCurrent() on the loading thread? Or is there more to it? Do i need to hook into the EGLContextFactory and EGLWindowSurfaceFactory? Do I actually need a second GLSurfaceView?

Any direction you all can provide will be greatly appreciated.[/QUOTE]

Android apps are normally required to execute all OpenGL ES calls from a single thread because EGL contexts can only be associated with a single thread and rendering graphics on the main UI thread is strongly discouraged. So the best approach is to create a separate thread specifically for all of your OpenGL ES code that will always execute from this same thread. If your app uses GLSurfaceView, it creates this dedicated OpenGL ES rendering thread automatically.

Maybe it’s possible for an Android app to have 2 EGL contexts open simultaneously, but I have not seen this done in Java apps. Obviously, it is done by the Android frameworks, such as the SurfaceFlinger. You would need to use TextureViews instead of GLSurfaceViews and create your own rendering threads. This is because each GLSurfaceView instance will have its own View in the hierarchy. TextureView is more flexible and uses SurfaceTextures which can be off-screen and it is possible to pass SurfaceTextures between threads.

If you manage to get this working, please let us know. This article may help:

I’ve tried reimplementing the GLSurfaceView by copying the Android source for that class and building in the capability to create a second EGLContext and make it current. The app still runs, but when resources are loaded from the second EGLContext, they’re not sharing OpenGL object names. For example, the main thread would generate textures 1-3, and then on the loading thread it would generate those some texture names again.

From more searching on the web, it sounds like GL context sharing is just not available on many Android devices? I’m starting to feel like the hardware/driver situation on Android is just not ready for this kind of thing. It’s hard to tell because some of the discussions I’ve found were several years old.

I also tried replacing the GLSurfaceView with a TextureView, as detailed on a Stackoverflow discussion I can’t link (really?) because I’m a new member here. When I got it running, the frame rate was only around 10 FPS. So I haven’t gone any farther yet in that direction. Any ideas why the TextureView would have such poor performance?

[QUOTE=suspense;29430]I’ve tried reimplementing the GLSurfaceView by copying the Android source for that class and building in the capability to create a second EGLContext and make it current. The app still runs, but when resources are loaded from the second EGLContext, they’re not sharing OpenGL object names. For example, the main thread would generate textures 1-3, and then on the loading thread it would generate those some texture names again.

From more searching on the web, it sounds like GL context sharing is just not available on many Android devices? I’m starting to feel like the hardware/driver situation on Android is just not ready for this kind of thing. It’s hard to tell because some of the discussions I’ve found were several years old.

I also tried replacing the GLSurfaceView with a TextureView, as detailed on a Stackoverflow discussion I can’t link (really?) because I’m a new member here. When I got it running, the frame rate was only around 10 FPS. So I haven’t gone any farther yet in that direction. Any ideas why the TextureView would have such poor performance?[/QUOTE]

I would expect the texture names to be duplicated, because they were allocated by different contexts.

Google has never indicated that it’s possible for an Android app to have more than one EGL context at a time, and they state that an EGL context can only ever be associated with a single thread. I think this is a limitation of the Android frameworks, not the EGL or OpenGL ES drivers, because this can be done on Linux.

TextureView offers a lot more flexibility. For example, GLSurfaceView does not allow allocating surfaces which are off-screen, but TextureView does. The getBitmap() method of TextureView is slow because it uses glReadPixels(). But, Bitmaps should not be used for textures anyway because of the pre-multiplication problem. Otherwise, it will not affect the rendering performance.

I just found an interesting discussion on StackOverflow that indicates that in newer versions of Android, they added EGL Context reference counting inside eglInitialize() and eglTerminate() to allow multiple components to use OpenGL ES and have several contexts open in an app simultaneously. Although, this seems to conflict with the EGL specification and is never mentioned in the Android documentation.

My understanding was that if I pass the original context as a parameter in eglCreateContext, it would create a share group and the two contexts would then share object names and the associated resources. Is that not correct?

Google has never indicated that it’s possible for an Android app to have more than one EGL context at a time, and they state that an EGL context can only ever be associated with a single thread. I think this is a limitation of the Android frameworks, not the EGL or OpenGL ES drivers, because this can be done on Linux.

So that explains why I’ve only seen working examples that use native code… they were using EGL directly instead of going through Android.

I just found an interesting discussion on StackOverflow that indicates that in newer versions of Android, they added EGL Context reference counting inside eglInitialize() and eglTerminate() to allow multiple components to use OpenGL ES and have several contexts open in an app simultaneously.

So this suggests I might be able to create two components (TextureViews / GLSurfaceViews) and pass the context from the first into the second to enable context sharing… Although if this does conflict with the spec it’s probably something I shouldn’t be doing in case the framework is made to conform with the spec in the future.

Tried to write a lengthy post with code but was got denied posting as a new member. Suffice to say, I can confirm that everything works pretty much as spec’d. You create a second context with the GLSurfaceView’s context as the parent, plus a PBufferSurface. You then create a new thread, call eglMakeCurrent with the display, pbuffer surface, and loading text and set up your context. The last crucial bit (which seems to be missing from any posts I could find online) is that you need to draw the texture in the loading thread (in between your glTexImage2d and glFinish), otherwise the driver is lazy and defers uploading the texture to the first draw. So the code should look like: loading thread does glTexImage2d, glDrawArray, glFinish, then syncs with the render thread somehow, render thread does a glBindTexture and draws as normal.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.