Optimize drawing lots of objects

Started by
11 comments, last by DrDerekDoctors 18 years, 10 months ago
Hi, I'm working on a 2D project in OpenGL and now trying to optimize it, and basically I wondered if you could take a look at the code and tell me if there are any faster ways of doing it. I think it's the drawing that is time consuming and that's why I posted it here in the OpenGL department. The part that I am interested in handles the units, I have a chain where every unit knows the adress to the previous and the next unit, and one controller that knows the adress to the first and last unit. Every frame the controller tells the first unit to update itself, then the first unit tells the next unit to update, etc etc. For example I tried removing the glLoadIdentity() and positioning the unit relative to the last unit, but that was no success. Here is the code that every unit executes:

void unit::Update()
{
	
		//calculate the direction to the target

		if (TC.tank1->getX() > x)
			dir = atanf((y - TC.tank1->getY()) / (TC.tank1->getX() - x));
		else 
			dir = pi - atanf((y - TC.tank1->getY()) / (x - TC.tank1->getX()));
	
		x += v*cosf(dir);
		y -= v*sinf(dir);

	//////////////////////////////////////
	//	Perform collision detection //
	//////////////////////////////////////
		float r=18.0f;
		if ((TC.tank1->getX() - x) < r && (TC.tank1->getX() - x) > -r)
			if (TC.tank1->getY() - y < r && TC.tank1->getY() - y > -r)
			{
				//the unit is inside the tank quad
				dead = true;
			}	

	
	//////////////////////////////////////
	//         Draw the unit	    //
	//////////////////////////////////////
	glLoadIdentity();
	glTranslatef(x,y,-1.0f);

	//translate radians to degrees
	glRotatef(-dir*57.29577, 0, 0, 1);
	
	glBegin(GL_QUADS);

//	glNormal3f( 0.0f, 0.0f, 1.0f);

	glTexCoord2f(0.0f, 0.0f); glVertex3f(-16.0f, -16.0f,  0.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(16.0f, -16.0f,  0.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(16.0f, 16.0f,  0.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-16.0f, 16.0f,  0.0f);
	
	glEnd();

	// if there are more units, update them too
	if (unit_next != 0)
		unit_next->Update();

	if (dead)
		delete this;
}


Here is the class declaration (not really important?):

class unit
{
public:
	float getX();
	float getY();
	unit *unit_next;		//keeps the adress to the next unit, creates one big chain of units
	unit *unit_previous;	//keeps the adresss to the previous....

	void Update();
private:
	float x;
	float y;
	float dir;		//the current direction to target
	float v;		//velocity

	bool dead;	//dead or not
};


Advertisement
You could use a Display list to draw the quad. That is if you are drawing a lot of them.
Quote:Original post by nefthy
You could use a Display list to draw the quad. That is if you are drawing a lot of them.


Every unit consists of one textured quad so I guess display lists wont help much.
This may be a little too common sense, but sometimes it is overlooked, just make sure you aren't rendering units you can't see. Back when I was using OpenGL for 2d, I just brute force drew everything and got a FPS of 50ish, which is passable, but when I culled out everything that isn't visible, I was got an FPS of 500.

The one thing that also could be slowing down your rendering is using OpenGL's immediate mode. You could dump the units into a VB and render from that, that could give you a boost.
Quote:Original post by PureW
Hi, I'm working on a 2D project in OpenGL and now trying to optimize it, and basically I wondered
if you could take a look at the code and tell me if there are any faster ways of doing it.
I think it's the drawing that is time consuming and that's why I posted it here in the OpenGL
department.


dont guess, profile.
Also, what kind of speeds are you getting right now on what kind of machine?
JohnnyCasil: The units are visible at all times so that wont work.

_the_phantom_: I acctually tried drawing the units in one frame out of ten, wich resulted in higher speeds and a flickering screen.
At the moment I can draw approximately 4000 units on the screen with 100fps (I think it's 100fps, since my monitor runs with 100hz and v-sync seems to be enabled) before the cpu reaches 100%.
confirm the fps first without vsync on, then use some kind of profiler (either a proper external program or your own in built code to time different sections of the game), both with and without vsync.
If the rendering isnt the bottle neck all the improvememts in the world wont help you
OpenGL has a relatively low overhead for draw calls. But assuming that you are being limited by draw calls, try doing this,

//called 1000+ times
draw(unit);


draw(Unit* u)
{
vertexArray.addVerticesFrom(u);
}

At the end of all your draw calls,
call something like submitBuffer();

submitBuffer()
{
glVertexPointer(...);
glTexCoordPointer(...);
glDrawArrays(.., vertexArray.data);
}

Basically the idea is to buffer all your quads and draw them throught 1 big vertex array to minimise draw calls. this is the approach i use for my fonts. Also i am assuming that all your units are using the same texture. Otherwise you will have to make seperate buffers for different textures. To minimise this, you should try to use 1 big texture containing all your sprites instead of 1 small texture for each sprite.

Hope this is helpful.
One thing you can do to reduce your CPU overhead is to make a sin/cos/atan table. I noticed you're doing a lot of trig on each unit every frame, and these are pretty expensive calls. Especially since today's memory is so big, it's worthwhile to make a table of all sin/cos/atan values. You'll probably want to experiment with the step size to find something that gives you good results, then all you have to do each frame is round to the nearest step and look it up in a table...much faster.

If you were just computing a new camera position, this wouldn't be an issue, but since you're doing trig on each of 4000 objects, it really adds up.

You may not see a significant framerate increase, but I promise you'll be clearing up CPU cycles for the rest of your engine that you'll need later!
the only time u would want to maybe use the tables is inside a tight loop that uses those functions, the above doesnt adhere to that.

to the OP the best way to speed it up would be to do all the math yourself, ie loop first through all the units and workout the 4 corners of the quad. and then once u have all the units sussed draw them in one fell swoop

itll save in calling the following commands for every unit
glLoadIdentity();
glTranslatef(x,y,-1.0f);
glRotatef(-dir*57.29577, 0, 0, 1);
glBegin(GL_QUADS);

This topic is closed to new replies.

Advertisement