What's the best way to draw lots of large textures?

Started by CuppoJava, May 15, 2005, 05:42:08

Previous topic - Next topic

CuppoJava

Hi,
I'm programming a 2D tile-based game and I'm having performance issues getting a large number of textures to paint. I'm wondering if someone could help me pinpoint the problem.

When I paint a 16x16 texture 1400 times a frame it looks really smooth on my computer, but when I switch to painting a 64x64 texture 90 times the framerate drops noticeably.

Under -Xprof i noticed that org.lwjgl.opengl.Win32Display.swapBuffers is taking up 80% of the native code.

Is there anything I can do to make it faster with a 64x64 texture?

Would opengl display lists make it faster?
Should I prerender all the tiles into one REALLY BIG texture, and map that onto a single quad?

Thanks for taking the time to read this. I really need some help with this.
 -Cuppo

princec

You need to do two things:

1. Use vertex arrays, and preferably, VBOs or NV_vertex_array_range2, and glDrawRangeElements, to actually render your quads.

2. Sort your quads by texture order, and build up a number of runs of textures drawn in the same texture. You are aiming to minimize the number of calls to GL, and this means batching wherever possible.

SwapBuffers takes a lot of time because it sits and waits until GL has done all its drawing before it can swap the buffers.

Cas :)

elias

Quote from: "princec"You need to do two things:

1. Use vertex arrays, and preferably, VBOs or NV_vertex_array_range2, and glDrawRangeElements, to actually render your quads.


Cas :)

If I may be so blunt: Don't bother with NV_vertex_array_range(2), Cas is really just being an old OpenGL fart here :D. VBO is superior to NV_VAR, and I'm guessing that VBO is much more widespread, given that VBO has been out for a while and that NV_VAR only works on NVIDIA hardware (and not even on Mac OS X).

- elias

CuppoJava

Thanks for that advice,

Are vertex arrays similar to display lists? Do you know of a tutorial that goes into these?

Sort your quads by texture order, and build up a number of runs of textures drawn in the same texture. - Can you explain what you mean by building a run? Do you mean putting the drawing and texturing code for the quad into a display list?

Sorry I'm quite new to ogl so my terminology is still pretty shallow

Many thanks.
 -Cuppo

gimbal

Vertex buffers are used to draw large amounts of geometry using a single opengl call (glDrawArrays or glDrawElements). This geometry needs to be rendered with the same state, so what you need to do is batch all primitives together that have the same texture and render them using a vertexbuffer. This page has helped me a lot to understand vertex buffers and their usage:

http://www.devmaster.net/forums/lofiversion/index.php?t360.html

Also check out the nVidia website, they have a whitepaper that explains what VBO's are.

Fool Running

Quotebut when I switch to painting a 64x64 texture 90 times the framerate drops noticeably
What is your framerate before and after? Are you sure the increase in texture size is just not causing a slowdown that will normally occur (you are using more of your fill rate)?

EDIT: What I mean is that a framerate drop from 700fps to 600fps is probably normal.  30fps to 20fps is not normal. :wink:
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

CuppoJava

Thanks for that reply gimbal. That page helped me out a lot.
One thing I did notice though, was that vertex buffers are an Extension?!?
Does this mean that my game will only be able to run on cards that support this?

In reply to Fool: from switching from 16x16 textures to 64x64 textures my framerates dropping from ~70fps to 15fps.  :(

elias4444

I'm a little curious as to what exactly it is you're "painting." That's a lot of tiles per frame you mentioned, especially for a 2D game. Are you reusing and precaching your textures? Are you painting just the screen portion, or an entire level each time? Also, are you using culling?
=-=-=-=-=-======-=-=-=-=-=-
http://www.tommytwisters.com

Fool Running

Pasting your code would help us a little to help you (unless you don't want us to see it  :wink: )
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

CuppoJava

The game is going to be a 2D top-down shooter. At any one time there is only 256 different textures maximum, who are repeatedly painted in different positions. I'm painting only tiles that are visible on the screen.

I have a resolution of 600 by 600 right now. So (600/16)^2 gives me about 1400.

I can paste the code, but its highly structured and split up into almost 20 classes, so I wouldn't want anyone to crawl through my code when I'm the one asking for help. When I get home, I'll try posting some pseudo-pseudocode.

Thanks very much for your replies

Jan

QuoteAre you painting just the screen portion

@ elias4444

What's the best way of ensuring this? I know this might be a very simple question. To me, beeing new to OpenGL it is not. After all the translation and Rotation taken place, I don't know wether the vertex is inside or outside the screen portion.

elias4444

Generally speaking, if it's a 2D top-down game (Orthographic?), you could just do a simple collision check with the screen resolution to see if it's on the screen or not before you call the draw command. It does become more complicated in a full 3D scene, but it doesn't sound like that's what you're doing.
=-=-=-=-=-======-=-=-=-=-=-
http://www.tommytwisters.com

oNyx

If its 2d you *know* which tiles end up on screen. You know the size of the tiles, the world offset and the size of the window. You simply use 2 for loops for drawing those tiles, which are within that rect. No need to check (collide) anything there.

And other things... well, you spawn em, throw em into some list, draw em each frame as long as they are there and you remove em from that list when they die or when they left the screen or some bigger rectangle. Sorta depends on the game itself.

Jan

Thanks to the two of you. You are right there is no problem with that. It is going to be 2D top-down. But what if I wanted to rotate the scene on the z-axis? I guess I will have to read something about collision checks :)

elias4444

It definitely gets harder when you start rotating the axis. There's some "easy" (a.k.a. works, but not exactly the best way) ways, and some harder ways. It really is going to depend on your game though, as different games will have different view requirements. For now, if you're still learning, I suggest keeping with the simple "is it inside my resolution rectangle" method. If you decide that you must rotate an axis, a quick/simple method, would be to create a type of bounding sphere around your resolution rectangle, and check to see if the object is within the sphere or not before drawing it. Just check the x and y coordinates (drop the z), and it'll act as a sort of bounding cylinder (so z-depth won't matter).

Here's some sample code:
public boolean isOnScreen(float objectx, float objecty, float objectz, float objectsize) {
		float dx = (float)((screencenter.x) - (objectx));
		float dy = (float)((screencenter.y) - (objecty));
		float dz = (float)((screencenter.z) - (objectz));
		float minDistance = screenwidth + ((objectsize) / 2f);

		return (dx*dx + dy*dy + dz*dz < minDistance*minDistance);
	}


Note, screencenter is either going to be 0,0,0 if you're camera's focus never moves, or you'll need to keep it updated to whatever your camera's focus happens to change to. For the cylinder approach, just drop the " + dz*dz" from the return line.
=-=-=-=-=-======-=-=-=-=-=-
http://www.tommytwisters.com