[Solved] Rendering Lag spikes, 2D texture with vbo and custom shader

Started by TotalSpelNerd, January 31, 2015, 22:57:18

Previous topic - Next topic

TotalSpelNerd

I'm pretty new to all this LWJGL stuff and have been having a problem for the last week or so with lag spikes in my tiny 2D game engine (if I can even call it that). From what I could gather the lag spikes always happen inside my render function, but I'm uncertain what is really causing the lag.

public void render(){
	shader.setUniform(texcoord.getMatrix(),UNIFORM_TEXCOORD); // Texture cordinates inside a matrix
	shader.setUniform(transform.getMatrix(),UNIFORM_MODELVIEW); // Position of the object in the world
	glBindTexture(GL_TEXTURE_2D, ID);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
		
	glVertexAttribPointer(0,2,GL_FLOAT,false,8,0L);
		
	glDrawArrays(GL_TRIANGLES, 0, SIZE);
		
	glDisableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}


I've read on the forums before that it could be because of a garbage collection, but I can't seem to find anything in my render function that would create garbage.

This is also how I create the vbo if it is by any help. I'm just initializing this code once and using the same vbo for all the textures.

vbo = glGenBuffers();
	glBindBuffer(GL_ARRAY_BUFFER,vbo);
	glBufferData(GL_ARRAY_BUFFER,Util.createFlippedBuffer(new float[]{0.0f,0.0f, 0.0f,1.0f, 1.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f}),GL_STATIC_DRAW); // createFlippedBuffer just puts the values in a FloatBuffer
	glBindBuffer(GL_ARRAY_BUFFER,0);


If you need any more information just ask.
Thanks for any help.

Cornix

Thats not really enough code to help you out. You would have to show us more of your loop, perhaps there is some kind of problem there somewhere.

You might also do some additional testing, perhaps with some profiling software if you have access to it. Also use a tool to analyse the heap usage.

Kai

It could be that those .getMatrix() invocations allocate a new matrix behind the scenes on each invocation.
But profiling your app is always the best choice, as Cornix proposed.
Just recently, the Early Access program of the YourKit Java Profiler 2015 started, which gives you this excellent tool for free until March, 16!

TotalSpelNerd

QuoteThats not really enough code to help you out. You would have to show us more of your loop, perhaps there is some kind of problem there somewhere.

You might also do some additional testing, perhaps with some profiling software if you have access to it. Also use a tool to analyse the heap usage.

Okay, didn't think there would be an easy solution to this.

Here is my gameloop if it is by any help, which I don't think it will.

private void run(){
		long lastTime = Time.getTime();
		double unprocessedTime = 0;
		while(running){
			boolean render = false;
			long startTime = Time.getTime();
			long pasTime = startTime-lastTime;
			lastTime = startTime;
			
			unprocessedTime += pasTime/(double)Time.SECOND;
			frameCounter+=pasTime;
			while(unprocessedTime>frameTime){
				if(Window.isCloseRequested())
					stop();
				glLoadIdentity();
				render = true;
				unprocessedTime-=frameTime;
				currentScreen.input();
				Input.update();
				currentScreen.update();
			}
			if(render){
				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				glClearColor(color.getRed(),color.getGreen(),color.getBlue(),color.getAlpha());
				currentScreen.render(); // Actuall rendering
				Display.update();
				frames++;
			}else try{
				Thread.sleep(1);
			}catch(InterruptedException e){}
		}
		currentScreen.dispose();
	}


Here is everything I'm doing in my render method:

@Override
public void render() {
	world.translate((float)x,(float)y);
	setUniform(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha(), UNIFORM_COLOR);
	glUseProgram(program);
	setUniform(world.getMatrix(),UNIFORM_WORLD);
	for(int i = 0;i<100;i++){
		texture.render(); // this is the method I posted before
	}
	world.reset();
}

TotalSpelNerd

Quote from: Kai on January 31, 2015, 23:15:08
It could be that those .getMatrix() invocations allocate a new matrix behind the scenes on each invocation.

this actually gives me one clue that could be making this problem.

in my world.translate, reset, rotate etc.  I use
matrix = matrix.mul(new Matrix3f().initTranslation(x, y)); // this is the translation code


And this is the multiplication code:

public Matrix3f mul(Matrix3f mat){
	Matrix3f res = new Matrix3f();
	for(int i = 0;i&lt;ROWS;i++){
		for(int j = 0;j&lt;COLS;j++){
			res.set(i, j, m[i][0]*mat.m[0][j]+m[i][1]*mat.m[1][j]+m[i][2]*mat.m[2][j]);
		}
	}
	return res;
}


I'm creating a new instance of Matrix3f and returning it, is it not getting disposed automatically or is this getting collected in the garbage?

Kai

Yes. Every object, even your small new Integer(123) (also beware of auto-boxing here) is adding up to memory which the garbage collector must trace and reclaim if unreachable.
Current JVMs are very good at this, but still for real-time critical applications, such as games, it becomes noticeable, even if it is only 2-3 milliseconds.
So, one thing you could do is to pool your matrices and vectors used for intermediate computations as much as possible.

But please use a real profiler for that, like the mentioned YourKit Java Profiler. It will give you a whole lot more insight about what is going on in your app and hints you at some hotspots where the most action is. :)

TotalSpelNerd

I was running YourKit java profiler and see minor collections every 2-4 seconds or so (Time spent ~4%, Collections per second: 1 on every spike), which is actually the times it creates a lag spike. But I don't understand how I can trace it, often it says it's inside Display.update, glBindBuffer, glUniformMatrix3f and Thread.sleep, which I can't controll. I probably can, but I don't really understand how this information can help me search for the garbage.

Edit: I'm in the Garbage collection tab inside YourKit

Kai

Although using YourKit Profiler is beginning to get out-of-scope for this LWJGL forum :), I will try to answer your question.

What you did was probably selecting in the "Memory" tab a specific time interval where some garbage collection occurred and then viewed the "CPU Usage Estimation" tab below.

That will not give a hint about the allocations that are taking place, but only what the CPU did around the time the garbage was collected.

What you really want to do is not to see when GC happens, but when and where objects are being allocated, that ultimately leads to GC.

To view the object allocations, you need to activate "Object Allocation Recording". This is the sixth icon from the left in the icon tool bar showing a green "play" button over some small iconified, I guess, memory RAM chip.

Once you hit that button, the "Memory" view will change into "Allocations" and it will collect memory allocations with the stack traces in a tree view.

For further info have a look at YourKit's documentation, such as these: https://www.yourkit.com/docs/java/help/allocations.jsp

TotalSpelNerd

I've finally found the problem(s) with the code. Every time I'm calling the setUniform I'm generating a new Floatbuffer each time, which created a whole lot of garbage. I'm getting like 0 lag spikes that are noticeable to humans right now.

In the future I'm going to be more careful when creating objects that are going to get collected into the garbage later.

FloatBuffers are my mortal enemies right now and I'm doing all I can to keep them away, or at least have them under my control, and not let them reproduce MWAHAHAHAHA :P

Thank you so much :D