PDA

View Full Version : graphing z=f(x,y,t) on large x-y grids



nicholas maxwell
12-28-2008, 08:12 PM
Hi guys, new to the forum,

So I'm working on some quantum mechanics and acoustic wave problems. For now we're focusing on two-dimensional problems. So we need to visualize functions that look like z = F(x,y,t). So I'm doing animations of 2D-plots. simple enough. I've been doing this with freeglut under xubuntu, and I'm really happy with the results so far.

My main problem is that the animations are very unsmooth for large grid sizes. I get about 16 FPS on an X-Y grid of 128*128 points. For 256*256 that goes down to about 5 FPS, which is pretty unusable.

I'm doing all of the computations before hand, and storing the triangle data in the data structures that are detailed further down. Bascially the object glutFrame2D contains all the data the animation needs for a particular frame. I've got a scheme going to make sure that each time a new frame is drawn, the program chooses the frame whose time stamp best matches the current wall clock time, so that the animation is running in 'real time'.

So the problem is that my PC (lenovo x60 with 3 gigs ram) can't draw enough of these glutFrame2D objects per second, and I'm kind of stuck; I'm not sure how to make this any faster. All the glutFrame2D objects are already in memory before the animation starts. For a 256*256 grid each frame is 2.5 megs, so I couldn't load too many into video memory. I tried parallelizing the for-loop that draws each quad, using Intel's TBB library, but got ugly results, also that approach will give me at best about a 70% speed gain, which won't solve the problem.

Is there generally a better way to approach this?

There are some basic things I could do to at least make each frame smaller in size; most of the vertex data is repeated as the quads are next to each other for instance. But I don't think any of that will really solve the problem.

Here are the objects I'm using to store the triangle data; take it as pseudo code - many details omitted.



struct glut_4color // pretty obvious
{
public:
GLubyte red;
GLubyte green;
GLubyte blue;
GLubyte alpha;

void set()
{ glColor4ub(red,green,blue,alpha); }
};

struct glut_quad // two triangles with common hypotenuse
{
public:
float x0,x1,y0,y1,z00,z01,z10,z11; //locations of vertexes
glut_4color c0,c1; //color of each triangle

void draw() {
c0.set();
glBegin(GL_TRIANGLES);
glVertex3f( x0, y0, z00);
glVertex3f( x1, y0, z10);
glVertex3f( x0, y1, z01);
glEnd();

c1.set();
glBegin(GL_TRIANGLES);
glVertex3f( x1, y1, z11);
glVertex3f( x1, y0, z10);
glVertex3f( x0, y1, z01);
glEnd();
}
};


class glutFrame2D // each frame is a collection of glut_quads
{
public:
unsigned N; // number of glut_quads - 256*256
float t; // time stamp for this frame
glut_quad * U; // memory block

void draw() {
for (unsigned i = 0;i<N;i++)
U[i].draw(); } /// draws each quad; this is what takes so long.
};




Thanks in advance for any advice,
cheers,
nick maxwell

Pfirsich
12-28-2008, 09:20 PM
You should consider not using the immediate mode, but either Vertex Arrays (or Vertex Array Objects?) or at best, I would say, Vertex Buffer Objects, with something like DYNAMIC_DRAW as the usage flag and updating your current buffer, so you don't have to upload a new VBO all the time, or store many VBOs simultaneously.
You can consider using indexed meshes, so every point in your Grid is only one vertex, and only updating the "ARRAY_BUFFER".
If that is not possible (I don't know why it wouldn't be) you really shouldn't glBegin and glEnd for every triangle (just around the for-loop cycling the quads is enough).

Zengar
12-29-2008, 01:53 AM
Pfirsich already said all his, but I want to stress this again.

The way you do your drawing is the most ineffective way possible. If you follow this guidelines, you will get good performance: 1) avoid immediate mode 2) use VBOs 3) draw as much as possible with one draw call (submit all triangles at once, not lots of batches with single triangle).

nicholas maxwell
01-07-2009, 01:03 AM
OK, thanks much, finally got back to this - had to spend some days figuring out adaptive step control and whatnot for integrating ODEs.

This is what I have so far:


class glutFrame2D
{
public:
unsigned N;
float t;
GLfloat * vertices;
GLubyte * colors;

public: // constructors, etc

public:
void set(unsigned i, GLubyte red ,GLubyte green, GLubyte blue, GLubyte alpha , GLfloat x, GLfloat y, GLfloat z );
void draw();

};

void glutFrame2D::set(unsigned i, GLubyte red ,GLubyte green, GLubyte blue, GLubyte alpha , GLfloat x, GLfloat y, GLfloat z )
{
colors[i*4+0] = red;
colors[i*4+1] = green;
colors[i*4+2] = blue;
colors[i*4+3] = alpha;

vertices[i*3+0] = x;
vertices[i*3+1] = y;
vertices[i*3+2] = z;
}

void glutFrame2D::draw()
{

unsigned colorsSize =N*4*sizeof(GLubyte);
unsigned verticesSize = N*3*sizeof(GLfloat);

GLuint ID = 0;
glGenBuffersARB(1, &ID);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, ID);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, verticesSize+colorsSize, 0, GL_DYNAMIC_DRAW_ARB);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, colorsSize, colors);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, colorsSize, verticesSize, vertices);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, ID);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0);
glVertexPointer(3, GL_FLOAT, 0 , (void *)(colorsSize) );
glDrawArrays( GL_TRIANGLES , 0, N);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

}

draw() is what's called to draw each frame in real time. set() is what's used before the animation is run to get each frame set up before hand; pretty straight forward. So far I'm still doing individual triangles.

It works, fairly well; looks better than what I had before at least, but it's still slow, maybe 2-3 fps faster. I could switch to triangle strips, I don't really see that making that much of a difference though.

Am I missing something here? I was under the impression that using VBOs should give at least a two fold improvement on performance. This is on a 128*128 grid, each frame takes about 2.5 MB; I'd really like to get to at least 512^2 grids.

again, thanks for the help,
nick

Pfirsich
01-07-2009, 07:13 AM
You create the VBO in every frame. No wonder everything is super-slow. You should create the VBO once, maybe even bind it only once and maybe set the Vertex/Color-Pointers only once (but I don't know if you can still update them in this case). And I think you should store you're data in only one array, so you can upload it with only one glBufferData-Call.