Can you use the same vertex data more than once per draw call?

Started by
5 comments, last by Sean_Seanston 9 years, 7 months ago

Just something that came into my head while messing around with sprite drawing and thinking about making things more efficient and cleaner in the code...

Right now I have a situation where all of my quads (actually 2 triangles) use the same vertex data and texture coordinate data, before being altered per instance in the shader using ModelView matrices and texture offsets passed into the shader.

It seems a bit wasteful (in principle at least, if not actually significant to performance) that I'm having to send new copies of the same 6 vertices every time I want to add another quad to be drawn, but I haven't been able to find anything online about this (can't phrase it the right way when I search maybe) and messing around to try and make it do this has yielded nothing (all I did was add another ModelView matrix and tell glDrawArraysInstanced to draw twice as much, but after that I'm out of ideas).

With only 6 vertices, is it just coming to the end of those 6 and then ending the draw and ignoring the fact there's another ModelView matrix? The ModelView matrix is of course set up using glVertexAttribDivisor.

Though when I think about it... is 1 instance actually defined by the number of "indices" you tell glDraw* to render...? In which case that would make sense... but I still would have no clue how to do what I'm thinking of doing.

Is there some kind of command that tells it to loop so many times or something? Or do you just have to send separate vertex data for every vertex you want to draw, even if it's the same as another vertex already in the buffer?

Advertisement

How are you setting up the ModelView matrix to use instancing?

New C/C++ Build Tool 'Stir' (doesn't just generate Makefiles, it does the build): https://github.com/space222/stir

From the Reference pages:

glDrawArraysInstanced has the same effect as:

if ( mode or count is invalid ) generate appropriate error
else {
for (int i = 0; i < primcount ; i++) {
instanceID = i;
glDrawArrays(mode, first, count);
}
instanceID = 0;
}

As I understood this is exactly what you want? In addition to what beans said the code of your draw call might be helpful.

How are you setting up the ModelView matrix to use instancing?

I added a 2nd ModelView matrix to the same buffer as the 1st working ModelView matrix, like this:


//Add modelView matrix to VBO
glm::vec2 spritePos( posX, posY );
glm::mat4 modelView = glm::translate( glm::mat4(1.0f), glm::vec3( spritePos, 0.0f ) );
//ModelView matrix (per instance)
vboMat.addData( &modelView, sizeof( glm::mat4 ) ); //Using a VBO wrapper class, this is confirmed to work though.

Then the vertex attribute is set up like this:


for ( int i = 0; i < 4; i++ )
{
glEnableVertexAttribArray( 2 + i );
glVertexAttribPointer( 2 + i, 4, GL_FLOAT, GL_FALSE, sizeof( glm::mat4 ),
                     (const GLvoid*)(sizeof(GLfloat) * i * 4));
glVertexAttribDivisor( 2 + i, 1 );
}

Which appeared to work fine for the 1st ModelView matrix when I used it for instancing sprites. My idea was that maybe by telling glDrawArraysInstanced() to draw twice as many vertices as was actually in the buffer, it might reach the end, find the 2nd ModelView matrix, and then restart drawing the vertices again from the beginning using the 2nd ModelView matrix.

As I understood this is exactly what you want?

I don't think so... I have instancing working with a different ModelView matrix for every instance of drawing a set of 2 triangles that make up a quad for a sprite. My "problem" with that way of doing things was that I was sending 6 identical vertices for every single quad, and then just altering their position later in the shader using a different ModelView matrix for each instance. Meaning I had a lot of essentially redundant data that I felt I should be able to reduce. My question is probably as relevant to the behaviour of glDrawArrays as it is to glDrawArraysInstanced.

My question is whether or not vertex data can be used more than once in a single draw call, in order to reuse this data instead of having to resend identical data for every instance. I think we can ignore instancing for the moment to simplify things, because if that part isn't possible then sending the matrices won't help because I'll still only have 1 quad or have to send the vertices individually.

In other words... if we take just glDrawArrays, is it possible to fill a buffer with e.g. 3 vertices for a triangle, and then use glDrawArrays (or perhaps some other function) to draw 2 or more triangles by just using the same data as the first triangle?

Or is that what happens when you e.g. put 3 vertices in a buffer, then call glDrawArrays( GL_TRIANGLES, 0, 6 )? I wonder because it doesn't error when I try it, IIRC, but it's hard to tell what's actually happening (maybe it just ignores the instruction to draw 6 vertices when it reaches the end of the buffer after drawing only 3?).

i.e. If you give glDrawArrays (or any draw function) a number of vertices to draw that exceeds the number of vertices it has available in the currently bound buffer, will it simply stop rendering once it goes past the last vertex, or will it loop around from the beginning? Or if it stops, is there a way of making it loop?

Sorry if I'm not explaining this well. It's a very specific thing I'm wondering about that might not even be possible for all I know.

It sounds like instancing is extactly what you want.


I don't think so... I have instancing working with a different ModelView matrix for every instance of drawing a set of 2 triangles that make up a quad for a sprite. My "problem" with that way of doing things was that I was sending 6 identical vertices for every single quad, and then just altering their position later in the shader using a different ModelView matrix for each instance. Meaning I had a lot of essentially redundant data that I felt I should be able to reduce. My question is probably as relevant to the behaviour of glDrawArrays as it is to glDrawArraysInstanced.

You can create a vertex array with only the instance attribute(s) enabled/setup and avoid using the 6 vertices all together, then in the shader use gl_VertexID? to determine which vertex of the quad it is. Use glDrawArraysInstanced normally [eg for 2 instances of the quad: glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 2) ]

New C/C++ Build Tool 'Stir' (doesn't just generate Makefiles, it does the build): https://github.com/space222/stir

You can create a vertex array with only the instance attribute(s) enabled/setup and avoid using the 6 vertices all together, then in the shader use gl_VertexID? to determine which vertex of the quad it is.

Interesting... that seems to make a lot of sense, if I'm understanding it right.

So it's basically...

1. Don't use the VAO at all for passing in anything constant like the vertices and texture coordinates (I did actually briefly consider uniforms for this myself since it seemed like the same kind of concept... but with 6 different vec3s per quad it seemed unworkable and I didn't know any better way).

2. Then shove all the vertex and texture coord data into the shader, assigning the first vertex coords to the vertex with gl_VertexID == 0 and so on, then multiplying with the ModelView matrix as normal?

Sounds like exactly what I need if I have it right.

EDIT: Though another thing is that I need some values in order to calculate the texture coordinates. The width and height of both the texture and the sprite area to be drawn from the texture. Should I just use 4 uniforms or is there a better way?

I see now there's a function called "ivec textureSize(gsampler sampler?, int lod?);" which can apparently get the dimensions of a texture... I'll try to use that and pass the sprite size in as uniforms...

Cool, so I implemented it that way and it all seems to be working fine. Saves a lot of code and makes things seem a lot more elegant. Thanks for that.

This topic is closed to new replies.

Advertisement