Load resources in a thread


pmprog

DNF (Did Not Finish)
Joined
Apr 25, 2011
Messages
4,150
Amongst my other projects, I thought I'd try and knock up a nice little game for the Pandora. I'm (again) using Allegro (which uses GLES)

My framework loads resources in a thread when you start the game. It doesn't take very long, but it just lets me keep the display running nicely. However, on the Pandora, if I load any images in the thread, it gives me all sorts of glitches and performance problems. If I put the load in the main thread, everything looks fine. As I said, there's not a lot of resources, but I'd like to be able to retain the threaded loading.

Does anyone know if there's a way to thread the loading of bitmaps without giving me these graphical issues? I imagine will need to make changes to the Allegro build rather than my project, but that should be okay.

Cheers

BTW - I don't notice these issues on any of my desktops with threaded loading, is this because there's more power, GL runs differently from GLES, or some other reason?
 
OpenGL is usually undefined outside of the main thread. Make sure you aren't loading any GPU state from your other-than-main thread.
 
Do you think loading as a Memory bitmap, then converting to a Video bitmap in the main thread would work? Must try this out later
 
With opengles only one thread can interact with the GPU at a time. You can switch to an other thread though (see the (ugly) source of spi.snes for a demo).
 
You can switch to an other thread though (see the (ugly) source of spi.snes for a demo).
Thanks, where do I find your source? Is it in the PND, or up on a website somewhere?
I'll take a look, though I'm not sure if that'll help, because I'll be rendering the screen on the main thread, whilst loading bitmaps in the other; but I guess I could create a mutex so I can find out when I can switch threads.

I'm hoping the memory bitmap trick might work. Will just have to wait until lunchtime or tonight when I can properly test it.
 
Last edited by a moderator:
It's true you can only have a single GL context bound in one thread at a time, but you can create multiple contexts and share data to get around this - either using 'share contexts' or eglImages.

Both methods have no locking of the texture data itself, so you don't need to re-create the image object if you want to change it, but you need to to mutual exclusion so any draws have completed in threadA before threadB changes anything.

Share contexts:

You can use share contexts to share some resources (including textures) between different contexts, and each context can then be bound in a different thread. In contexts like this, the texture IDs are shared between the contexts.

In pseudocode: (thread 'a' being the main render thread, 'b' doing texture uploads)

Note: the PowerVR sgx driver doesn't support EGL_KHR_surfaceless_context until 1.11 (and then as an option) - if you need to use a driver older than that you can create a 1x1 pbuffer and use that as the surface instead! Check eglQueryString(EGL_EXTENSIONS) before running this.


context_a = eglCreateContext(display, config, NULL, attribs...);
context_b = eglCreateContext(display, pbuffer_config, context_a, attribs...);

surface = eglCreateWindowSurface(display, config, window, attribs...);

global:
GLuint shared_texture;

thread_b:
  eglMakeCurrent(display, NULL, NULL, context_;
  glGenTextures(1, &shared_texture);
  glBindTexture(GL_TEXTURE_2D, shared_texture);
  glTexImage2D(GL_TEXTURE_2D, ..., texture_data);
  glBindTexture(GL_TEXTURE_2D, 0);
  markSharedTextureReady();


thread_a:
  eglMakeCurrent(display, surface, surface, context_a);
  waitUntilSharedTextureIsReady();
  glCalls(shared_texture);
EGL Images:

For EGL images, pass 'null' instead of ContextA in the eglCreateContext (the share_context argument). Then make the different contexts current in each thread, then in thread B upload a texture. Note, the texture IDs are only associated with a single context, you can't just use a texture returned from glGenTextures() in thread B in thread A! (The namespace is unique to the context, so texture '1' will mean something completely different to ContextA as ContextB). The only 'shared' object is the 'GLeglImageOES' object

PowerVR have supported EGL images since forever, but it always was an option. You should check for both:

"EGL_KHR_image" in the EGL extension list

"GL_OES_EGL_image" in the GLES extension list.


global:
  GLeglImageOES shared_image_object;

thread_b:
  GLint local_texture_a;
  glGenTextures(1, &local_texture_a);
  glBindTexture(GL_TEXTURE_2D, local_texture_a);
  glTexImage2D(GL_TEXTURE_2D, ..., texture_data);
  //Create an image object from the texutre ID
  shared_image_object = eglCreateImageKHR(display, context_b, EGL_GL_TEXTURE_2D_KHR, local_texture_a, NULL); //You probably don't want any attribs
  markSharedTextureReady();

thread_b:
  GLint local_texture_b;
  glGenTextures(1, &local_texture_;
  glBindTexture(GL_TEXTURE_2D, local_texture_;
  waitUntilSharedTextureReady();
  //Associate the 'local' texture object with the EGL image
  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, shared_image_object);

  glCalls(local_texture_;
Note: In the PowerVR driver unbinding a context (e.g. making a context 'uncurrent') will flush and wait for all rendering to finish, serialising the render and upload (so there's no advantage over doing it in one thread)

Hopefully this is useful to someone...
 
Last edited by a moderator:
Back
Top