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 (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.
For more information about GS-induced slowdowns, read this article.
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: