Bizarre interaction between glGetError and Display.update

Started by TLH, September 20, 2012, 17:30:50

Previous topic - Next topic

TLH

Greetings!

I've found some rather strange behaviour in terms of time taken. Obviously the entire process of producing a frame needs to take less than 16.6 ms in order to run at 60 FPS, and I've been trying to chase down the most time-consuming processes.

Amongst my findings were that, as part of the total scene time increasing with more objects on screen, the majority of it was being caused by glGetError. None of my calls to glGetError were actually reporting any GL errors (i.e. the return value was GL_NO_ERROR).

I then commented out all calls to glGetError, and the bulk of the time disappeared from the scene draw, but re-appeared in the call to Display.update. A quick experiment, involving causing an intentional GL error then testing for it afterwards, showed that Display.update (or one of its sub-procedures) itself calls glGetError, or otherwise causes the built-up time to be served then and there.

Does anyone know what could be happening? Also, if yes, is it something easily preventable or do I just have to accept it as part of the scene time budgeting?

Cheers!


spasi

The glGetError call on Display.update only happens when the system property org.lwjgl.util.Debug is set to true.

As for timing OpenGL calls, it's inherently inaccurate if you do it on the CPU side. GL is an asynchronous API by nature, one of its functions returning does not mean that its execution has completed. It may very well happen much much later. In practice what usually happens is that whenever you read back a value from GL, a pipeline flush will be caused and from the CPU perspective that one single call will last much longer than it should. This does not mean that function is slow, it just ends up "paying" the cost of whatever has been pipelined before it.

The most common functions that will cause a flush are glGetError, the occlusion query functions and VBO mapping. That's why you see glGetError taking a suspicious amount of time. If such a function does not exist, the next most common target for a stall is SwapBuffers with vsync enabled, which happens on Display.update(). So again, seeing Display.update() taking too long is normal.

For more accurate GPU-side measurements I recommend ARB_timer_query.

TLH

Ah, cheers for those hints of knowledge. The pipeline flush makes sense, and it does it on Display.update regardless of vsync and hidden switch settings.

CodeBunny

Wait a second. If OpenGL is asyncronous, and the render methods take time after they finish, but we can still perform actions on the CPU, is it actually the best idea to perform our update calls after we render?

This is essentially what I'm currently doing now:
while(!shouldExit())
{
	update(deltaTime());
	render();
	Display.update();
	Display.sync(fps());
}


If I change is to this:
while(!shouldExit())
{
	render();
	update(deltaTime());
	Display.update();
	Display.sync(fps());
}


Will my update commands be performed while the graphics card finishes the OpenGL calls, basically increasing my utilization of the CPU and GPU?

TLH

I actually tried that before posting originally, as well as some Thread trickery, though I couldn't get any noticeable change. By 'not noticeable', I mean that any effect it may have had was not strong enough to observe amongst random chance in the scene (player and AI actions, procedural elements etc), not even a slight trend.

Unless I missed something during the testing, I guess it's too good to be true for such a simple change to give an instant 33% boost to how much stuff can be on screen. There definitely wasn't any GL calls in the update phase, else it would have thrown an exception due to being in a thread.

CodeBunny

You'd only notice a difference in performance if you were already pushing the limit of operations per frame and were below your target FPS. This would raise the computational ceiling more than anything else.

Still, I could be wrong.