Asynchronous texture loader in jogl

Hi everybody,
I’m quite new to jogl and try to implement my first project.

I came across the problem, that I would like to start the program and while running loading further textures. I know that OpenGL originally isn’t intended for multi threading, but there has to be a solution. I believe many games load additional textures when entering other world regions.

I use a very simple texture loader with a static method:

public static synchronized Texture getTexture(String fileName) {
		if (textures.containsKey(fileName)) {
			return textures.get(fileName);
		} else {
			Texture text = null;
			try {
				text = TextureIO.newTexture(new File(fileName), true);
				text.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
				text.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST);
				text.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
				text.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
				text.disable();
			} catch (Exception e) {
				System.out.println(e.getMessage());
				System.out.println("Error loading texture " + fileName);
			}
			textures.put(fileName, text);
			return text;
		}
	}

now i tried to load the textures somewhere like this:

new Thread(new Runnable() {
			public void run() {
				for (String pic : pics) {
					add(TextureLoader.getTexture("textures/" + pic));
				}
				loaded = true;
			}
		}).start();

which fails because “No OpenGL context current on this thread”:

TextureIO.newTexture() -> newTexture() -> Texture() -> GLU.getCurrentGL() which calls GLContext.getCurrent()

Then I thought about setting the current context of the new thread to the one from the old one. Unfortunately setCurrent of GLContext is not public. With the following hack I set it to public:

final GLContext cont = GLContext.getCurrent();
new Thread(new Runnable() {
	public void run() {
		try {
			Class glcontClass = Class.forName("javax.media.opengl.GLContext");
			for(Method m:glcontClass.getDeclaredMethods()){
				if(m.getName().equals("setCurrent")){
					m.setAccessible(true);
					m.invoke(null, cont);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		for (String pic : pics) {
			add(TextureLoader.getTexture("textures/" + pic));
		}
		loaded = true;
	}
}).start();

I know that this is quite bad style but at first it seemed to be working. Then I got the following Error “May not call this between glBegin and glEnd”. I think this comes from a gl.glGenTextures() or some other function which cannot be called while drawing.

Now I consider implementing some kind of semaphore mechanism which prevents drawing and texture loading at the same time. But this means there would be some frame rate drops unless the texture can be loaded between two frames.
As you can see this doesn’t seem to be a very satisfying approach…

Any Ideas? Or is there any example of how this is done in “reality”?
Thanks for your help/suggestions.

p90

A few suggestions:

  1. Scrap all your hacks with setCurrent(), this is wrong (not your fault, but it comes from a misundestanding of how opengl contexts work). In opengl, a drawing surface such as a window has a context associated with it. This context contains all the opengl state necessary for rendering things a certain way (e.g. lights and coloring, etc.).

Theoretically opengl can be multithreaded, but often this just doesn’t work out. Instead a context is limited to one thread at a time, and is only usable if it’s active or current on that thread. To achieve this, you should use the makeCurrent() method which is public. However, as a beginner to opengl and jogl I would strongly recommend using the GLEventListener mechanism, which guarantees that a context is available.

To achieve multithreaded use, you can create contexts which share resources such as textures. To do this create a GLPbuffer and then a GLCanvas that uses the longer constructor that takes a GLContext argument. Then you need to disable single threading with Threading.disableSingleThreading().

  1. Another alternative is to load parts of a texture each frame so that the frame rates don’t take too long. You can use glTexSubImage() to upload scanlines at a time for a texture.

On the other hand, I think that texture loading can probably be done once at the beginning of your app and then go from there. In most of the cases you do not need a dynamic texture loader, and when you have to reload textures, the performance bottleneck is not from sending the texture to the graphics card.

In your example, I’d recommend using newTextureData(new File(…)) instead, because this will perform all of the loading but doesn’t need any gl context. Then in your display method call newTexture() and give it your TextureData instance. The texture data loading can be much more easily multithreaded.

I seem to remember trying to set up context sharing between several GLCanvases in the past and it made my app really slow for some reason.

Hmmm, I haven’t had those problems, but I’m on relatively new hardware ( 3 years is new? ).

I’m on an 8600 atm… but come to think of it, it may have been on my old laptop that it was slow. I might try again with the same code and see how it goes. Come to think of it, I think I might’ve had one canvas that was continuously animated while others where redrawn on MouseEvents… that could have been my problem.

Thank you for your answers! I will try to implement some of it.
Sharing textures between multiple contexts sounds interesting. Are there some good examples or tutorials on this matter?

My aim is to create a VJ-ing software with many windows. For example one fullscreen on the beamer and several smaller ones to create and adjust effects/loops on a laptop screen. Those can then be faded into the “live view”.

I have already created a multi layer/channel/effects system for this with rendering to texture feature and shaders to mix and distort the results. Of course it would be nice if the user could load new textures/sprites/loops while the program is running. Typically there is a rather big collection of data (one loop maybe 100mb) which cannot be all loaded at the init…

I think what I had was a static GLContext which you get from the first GLCanvas you make, then when you make more GLCanvases you pass it in as a construction parameter. From that point on, any of your texture handles etc should be valid for binding while rendering any of your canvases.

[quote]My aim is to create a VJ-ing software with many windows. For example one fullscreen on the beamer and several smaller ones to create and adjust effects/loops on a laptop screen. Those can then be faded into the “live view”.

I have already created a multi layer/channel/effects system for this with rendering to texture feature and shaders to mix and distort the results. Of course it would be nice if the user could load new textures/sprites/loops while the program is running. Typically there is a rather big collection of data (one loop maybe 100mb) which cannot be all loaded at the init…
[/quote]
Sounds cool… I’ve done some VJing myself; I’d like to get back to it a bit more again… one day I’ll finally pull off the audio/visual stuff I dream of…
I’ve got a little playable thing (not video based, but with several JOGL windows) at xinaesthetic.net fwiw. Need to put more material there… no audio at all at the moment iirc. But I digress. ::slight_smile:

This sounds better than using multithreading with OpenGL. I’ve tried that and it doesn’t work well… what seems working well is to have different opengl context in different threads WITHOUT sharing of contexts.

What metric can be used to auto-tune the scanline amount that can be uploaded at once to not create any lags? Just timing how long glTexSubImage takes? Or some PBO usage? Or GPU timers? Anyone has experience with this? :slight_smile:

I haven’t personally implemented it, but a strategy that could work is to fix some amount of time you’re willing to spend on uploading textures per frame. Then upload 1 scan at a time until you’ve used up all of the time. Next frame, you can upload that same many scanlines in one batch. Of course, this will probably go faster than 1 scanline per call, so you might be able to increase the numbers even more, and you’ll have to account for different sized scanlines with textures of different widths.

I have now tried out several methods and this seems to work nicely: An independent, low priority thread loads the texture data from the hard disk using “TextureIO.newTextureData”. Before drawing the scene, I call a function which looks for prepared texture data and loads it to the video card (max. 1 texture per frame).
At first I tried to create the texture with “TextureIO.newTexture(TextureData)” but this is too slow. Now I do it like this:

...
Texture texture = TextureIO.newTexture(GL.GL_TEXTURE_2D);
texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
texture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
texture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);

texture.bind();
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, data.getInternalFormat(),
Helper.texSize, Helper.texSize, data.getBorder(), data.getPixelFormat(), data.getPixelType(), data.getBuffer());
texture.disable();

This works quite well for 512x512 textures. I didn’t succeed with other approaches like shared glcontexts and uploading only parts of the texture with “glTexSubImage2D”. But after spending many hours trying to get something running, I’m happy with the current solution for now. :slight_smile:

But there’s another thing which bugs me a little (somewhat OT). When I create an animator like this:

FPSAnimator animator = new FPSAnimator(canvas,60)

According to FRAPS the application only runs at ~40fps. Setting the paramerter to 80 lets it run at ~60fps. Any ideas why this happens? It doesn’t matter if the scene is empty or if I actually draw something…

Thanks!