Drawing texture loading progress paradox

Consider this:

Truth: I need to load a bunch of textures up at the start of a level. This takes a very long time.
Truth: I want to draw a progress bar using some textures that have already been loaded.
Truth: In order for progress to be displayed in real loading time, the first and second truths must happen in different threads.
Truth: All OpenGL calls must be done in the same thread that the OpenGL context was set.

Paradox: I must perform OpenGL calls in two separate threads simultaneously, or I need to somehow pause the loading in order to update the loading bar.

Question: Ideas on how to do this? These must be fast ideas. Repainting the entire loading bar every time something changes is not really an option. Switching contexts around is not an option. Having two contexts is not an option.

Most of the cpu work involved in loading a texture is unrelated to any gl calls - it’s disk reading, image decoding and bytebuffer shuffling that takes the time. These can all be done in a low-priority loading thread. When those steps are done you can push the data onto a queue to be processed in your main/gl thread at the end of a frame. If you’ve got particularly huge images you can even spread the upload over multiple frames by using glTexSubImage.

Plug: If you were using Rebirth this is trivially easy since all the hard work is done for you. Resources are automatically loaded in a background thread and then ‘finalised’ in the main thread before they’re considered ‘live’, and all the queing and thread-safety is taken care of for you.

As OrangyTang says (besides plugging his nice engine), there is no need to do loading and decoding of images on the OpenGL thread.

Then again, what is there to update visually if you are loading your textures from disk? Your progressbar is simply frozen for N millis, this is normal, AAA titles do it.


LinkedList<String> pending = ...;
jobs.add("resource/tree1.png");
jobs.add("resource/tree2.png");
jobs.add("resource/water.png");

Map<String, Texture> loaded = ...;

while(true)
{
   float percentage = 100.0f * loaded.size() / (pending.size() + loaded.size());

   // render progressbar

   if(pending.isEmpty())
   {
      break;
   }

   String path = pending.removeFirst();
   Texture texture = uploadToGPU(convertRawBytes(readFileAtOnce(path)));
   loaded.put(loaded, texture);
}

Really? That should take 1ms.

Yeah, 1ms is about right, although this particular problem is on the iPhone, and the loading bar is a large image (64x256). But even though I’ll be making like 1000 calls to update the loading bar (several things get loaded), that’s only a second, so I suppose you’re right. For now, I’ll try to just do it via repainting during loading, if that doesn’t work I’ll load the data first then bind the texture afterwards. Orangy, I’d use your loader but obviously it doesn’t work on the iPhone.

Thanks for the help, guys. :slight_smile:

Really…? That’s like… the equivalent to a pretty big scene.

You might consider not clearing your colorbuffer every frame. Then you can simply make those 1000 calls twice (with 2 buffers), and then simply grow the progressbar every bufferswap, with 1 quad every frame.

Interesting - I assume you’re doing the whole XMLVM cross-compile thing like kev? Is there anything in particular that doesn’t work or is missing on the iPhone?

Sorry I didn’t state that more clearly. By 1000 calls I mean that the progress bar will get updated around 1000 times each time a new level loads. Each progress bar update is about 5 draw calls (different layers of the progress bar / background). The slowest part of that is binding 5 textures, but that really isn’t any kind of problem at all. I can obviously reduce the resolution of progress bar updates if it’s an issue, anyway.

@Orangy
I’m not using XMLVM, actually, I’m doing a game exclusively for the iPhone from scratch, and have been for a few months now. I didn’t ask on an iPhone forum because unless it’s an API specific question I will typically ask here on JGO for the best results. :slight_smile: