Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have an OpenGL program, and I'm rendering a terrain mesh. I displace the vertices in the vertex buffer and don't really color them in the fragment shader yet. I'm adding a geometry shader one part at a time.

Before I added the geometry shader, when I was just programming the fragment and vertex shading steps of the pipeline, I was getting framerates of about 30+. Enough that I couldn't notice any choppiness. After adding the geometry shader, I get around 5 frames per second. Why? This is the entirety of the geometry shader:

#version 420

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

void main()
{
    for (int i = 0; i < gl_in.length(); i++)
    {
        gl_Position = gl_in[i].gl_Position;
        EmitVertex();
    }
    EndPrimitive();
}

Isn't this exactly what OpenGL was doing without the geometry shader?

share|improve this question

1 Answer 1

up vote 17 down vote accepted

Isn't this exactly what OpenGL was doing without the geometry shader?

No, it isn't. The GS is an optional step, not a step that has a default.

In order for OpenGL to execute a geometry shader, it must perform what is known as "primitive assembly". When you render a series of triangles via GL_TRIANGLE_STRIP, OpenGL will do internal stuff to convert every 3 adjacent vertices into an individual triangle, modifying the winding order appropriately.

Normally, when not using a GS, this process is performed once. When you use a GS however, it must be performed before the GS executes. But it must also be performed after the GS, because a GS can output a totally different primitive type (e.g. quads). So now you're making the system basically do a bunch of extra work for nothing. After all, OpenGL can't assume that your GS is doing nothing.

So now you're making the system basically do a bunch of extra work for nothing. After all, OpenGL can't assume that your GS is doing nothing (that's a undecidable problem).

Furthermore, a number of optimizations no longer function in the presence of a GS. Consider indexed rendering.

Each index from an element array buffer will produce the same outputs from a vertex shader. So the GPU will often cache these outputs in a post-T&L cache. If it sees an index that is already in the cache, the VS is not run again; it just fetches data from the cache.

What is "it"? "It" is... the primitive assembly unit. Yeah, that thing that gets run twice when you use a GS. The index caching stuff? It only works for the inputs of the GS.

So what happens to the outputs of the GS? Well, that's hardware dependent. But it has to go into some kind of memory buffer. And therein lies the problem: that buffer isn't indexed at all. It's like a glDrawArrays situation.

So if you send an index buffer of 0, 1, 2, 0, 2, 3, this would translate into 4 vertices in the post-T&L cache. But the post-GS buffer of vertices now has 6 vertices in it. The post-GS buffer uses more space. So if you go through the trouble of making properly post-T&L optimized triangle lists or strips, and you flip on a pass-through GS like yours, you basically killed about half of your performance gains from that optimization.

It wasn't useless, but it does hurt.

Adding to this is the fact that many GL 3.x-class GPUs (aka: DX10) had rather small post-GS buffers. The smaller the buffer, the fewer GS invocations you can have active simultaneously. So your hardware effectively bottlenecks on the GS. Because tessellation is a big feature of 4.x class hardware, most such hardware has buffers sufficient to make heavier GS use viable.

So using a GS is more likely to make your code vertex processing bottlenecked. Of course, you can always use that to your advantage by making your vertex and fragment shaders more complex, since it's just free performance at that point.

Here is a basic rule of thumb about GS's: never use a GS because you think it will make rendering faster. You should use it when it makes what you're trying to do possible. If what you're trying to do is an optimization, use something else.

The general exceptions to this are:

share
    
I'm trying to calculate the steepness of each polygon by taking its highest height and subtracting its lowest height. However, if a geometry shader will necessarily slow me down by this amount, I think I might be able to do it creatively in the vertex shader. –  Avi Jan 31 '13 at 9:31
1  
@Avi note that the highest and lowest points in a triangle will not give you its steepness; you need all three points. –  Sam Hocevar Jan 31 '13 at 15:05
1  
Personally I've always found instancing more useful for point sprites than a GS. –  Jimmy Shelter Jan 31 '13 at 19:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.