Skip to main content
GameDev.net gamedev.net
🔒 Locked

Bus Bandwidth

Started by dpadam450 Feb 4, 2015 at 8:23 PM 16 replies 3.5k views
Original Post
dpadam450
dpadam450

Back when I first started openGL 8 years ago, I had the naive:

glBegin(GL_TRIANGLES);

for(int i = 0; i < vertices; i++)

{

glNormal(); glTexCoord2f();glVertex();

}

glEnd();

I used to do this with some models that were 10,000 verts or so, I remember running gDebugger and having 100,000 + openGL function calls.

Now I was doing a test on a scene with 4000 objects (the same six objects duplicated 800 times). I internally track redundant state changes and I only bind the objects once, and draw them all. My test case there isn't much pixel overdraw. Here I have only 5000 openGL function calls.

Anyway, back on that old video card I still used to get like 60FPS or so from what I remember, Now I may have only had 50 objects in my scene back then and I now have 4000, but my total openGL calls today per frame is 5,000. Back then it was 100,000+.

Clearly I have a much better motherboard, much faster bandwidth, and way less openGL function calls (but I do have a lot more draw calls). So if it isn't a bandwidth issue, what is slowing down the GPU so much? What is going on internally, clearly doesn't seem to be a bandwidth issue. What happens when the draw() command hits that destroys everything?

NBA2K, Madden, Maneater, Killing Floor, Sims 
dpadam450
dpadam450

Eh, guess maybe my pixel shader could be a big part of it.

NBA2K, Madden, Maneater, Killing Floor, Sims 
Ravyne
Ravyne

I'm not an OpenGL expert, but if I'm not mistaken, under this old GL programming style, all those glNormal/glTexCoord2f/glVertex calls were all client side, and probably didn't even cross into kernel/driver mode code often or at all. glBegin() says "Hey OpenGL, start queing the next commands as one unit of work." and glEnd says "Hey OpenGL, I'm done defining that unit of work. Go ahead and bundle it up and send it over to the GPU."

So you may have had 100,000 openGL "calls" per frame under the old style, but they weren't all the kind of kernel/driver mode calls that we're all wary of making today.

throw table_exception("(? ???)? ? ???");
dpadam450
dpadam450

Interesting that gDebugger isn't tracking glUniform calls very well.

glUniformMatrix4fv() doesn't show up even though I call it for each object (4000 times).

NBA2K, Madden, Maneater, Killing Floor, Sims 
Matias Goldberg
Matias Goldberg
Measuring performance by the number of API calls is as useful as measuring progress on an airplane by how much it weights. It's a good/rough guidance and a factor; but extrapolating like that won't give you anything meaningful.

I internally track redundant state changes and I only bind the objects once, and draw them all.

You're undersestimating that code that tracks and kills redundant state changes has a cost.
21st Century Moose
21st Century Moose

It's also the case that if there's a lot of vertices between a glBegin/glEnd pair (like, say, a 10,000 vert model...) the end result is going to run fast. If you're using dynamic buffers and have a poor updating strategy, then glBegin/glEnd might even be faster, since the driver will be more likely to handle synchronization and resource contention better than a hypothetical poor buffer updating strategy.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
L. Spiro
L. Spiro
Your post is all over the place and frankly I can’t follow it. First you are talking about number of draw calls, then FPS, then “now I was” (so which is it? Eliminating redundant state changes is what you only started doing, or you were doing it long ago?) and all levels of crazy.

I don’t even see a question because, as far as I can best-guess, you used to have a lot of function calls, then you added redundant state-checking, and now you have fewer function calls. And? That seems to be the point of removing redundant state changes—fewer function calls.

And what about 60 FPS? You were getting 60 FPS back then. And? Is it slower now? Faster? Could you try to be just a little bit more ambiguous next time?



I have no idea if this is relevant based on what you didn’t ask, but no one uses immediate mode and vendors have largely stopped supporting it beyond making it functional.
I’m going to ignore the mess about number of draw calls and assume the only important part is 60 FPS back then, and also assume that the whole reason you are confused is because performance has decreased even though cards are faster.
Cards may be faster, but the drivers are suckier for any features that are deprecated (and especially for those that should never have existed).

At the end of the day, you’re raising a fuss about immediate mode. You may as well be stressing out over your 3dfx Glide performance or your Direct3D 6 performance.
Conversations about these things should not be happening today. At all.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
dpadam450
dpadam450

Back when I first started openGL 8 years ago, I had the naive:

At the end of the day, you’re raising a fuss about immediate mode. You may as well be stressing out over your 3dfx Glide performance or your Direct3D 6 performance.

Conversations about these things should not be happening today. At all.

No I'm simply wondering why 100,000 glVertex3f() calls had almost no overhead 8 years ago, but my modern GPU completely dies when I call glDrawElements 4,000 times. I have stripped the geometry and shaders, so its I'm literally not processing anything, and those 4,000 function calls KILL performance nowadays. So my question is.....why are the draw calls so expensive internally compared to say a state change glBind() or a glVertex3f() function?

NBA2K, Madden, Maneater, Killing Floor, Sims 
Promit
Promit

No I'm simply wondering why 100,000 glVertex3f() calls had almost no overhead 8 years ago, but my modern GPU completely dies when I call glDrawElements 4,000 times. I have stripped the geometry and shaders, so its I'm literally not processing anything, and those 4,000 function calls KILL performance nowadays. So my question is.....why are the draw calls so expensive internally compared to say a state change glBind() or a glVertex3f() function?

1) glVertex isn't a draw call - glEnd is. How many times were you actually finalizing the draw call? The cost of glVertex is predominantly CPU side, and as long as you're not saturating the CPU with them it isn't necessarily worse than client side vertex arrays.

2) As you've figured out, not all API calls are equal.

In fact, a state change call is actually "free". Imagine this call sequence:


glEnable(GL_BLEND);
glDisable(GL_BLEND);
glDisable(GL_BLEND);
glDisable(GL_BLEND);
glEnable(GL_BLEND);
glEnable(GL_BLEND);
glDisable(GL_BLEND);
glEnable(GL_BLEND);
glDrawElements(...);

Yes, this happens in real life. All the time. (Scene graphs love to barf up this style of driver calls.) Do you want each one of these calls flipping around GPU state and taking forever? No. So the driver actually buffers up all of your state changes, Bind calls, Vertex calls, etc, and commits everything at the point of the draw call. In a stupidly designed API like classic GL, this also triggers a vast amount of internal validation - up to and including total shader recompilation and potentially pipeline flush. That is why DrawElements is expensive, and that is why DX11/12, Mantle, Metal, and hopefully glNext look nothing like this.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
dpadam450
dpadam450

Yea that is what is seems people suggested. Had I actually had 4000 objects and used glEnd() 4000 times, I would have seen the performance drop.

NBA2K, Madden, Maneater, Killing Floor, Sims 
tonemgub
tonemgub




Now I may have only had 50 objects in my scene back then and I now have 4000, but my total openGL calls today per frame is 5,000. Back then it was 100,000+.

This would mean that your new hardware is 125 times better than the old one, if it can handle 125 times more vertices? I don't think we've advanced that much in the last 8 years. In fact, the hardware performance hasn't even doubled.

Sources:

http://www.overclockersclub.com/guides/roundup_graphics_2007/

http://www.nvidia.com/page/geforce_8800.html

http://en.wikipedia.org/wiki/List_of_device_bit_rates#Graphics_card.27s_RAM

That is why both OpenGL and DirectX started making changes in the API/driver side of things (think Mantle, DirectX12 and the upcoming OpenGL Nextgen that Valve is working on) to improve performance instead of the hardware.

Ravyne
Ravyne




In fact, the hardware performance hasn't even doubled.

How on earth do you arrive at that math?

Comparing 2007's top-of-line 8800 GTS against today's 980 (which, while the current headliner, isn't even nVidia's strongest chip of this generation), the later does 8 times as many GFLOPs. The stronger chip, once released, will do probably 12 times as many, if rumored specs are true (and evidence is strong). So GPU power has nearly doubled every two years -- even in spite of all the difficulty that GPU manufacturers have had getting off of the 28nm silicon node that we've sufficed with since 2011 or so.

Vertex count hasn't been a real problem in forever -- with an overcomplex vertex shader, maybe -- but not for basic transforms.

throw table_exception("(? ???)? ? ???");
dpadam450
dpadam450

So I took my code into work which we have Nvidia 970's and my framerate was like 200FPS vs 88 FPS on my Radeon 7850

My card is fairly new but it seems like the newer cards should crunch some more performance in terms of the draw calls.

NBA2K, Madden, Maneater, Killing Floor, Sims 
tonemgub
tonemgub

How on earth do you arrive at that math?

I thought we were talking about memory bandwidth (which is what I was obviously comparing in the rest of my post), not processing speed. The bus bandwidth itself isn't a bottleneck.

From the last test on the 970's, it's obvious that the problem is the size of the geometry. Those GTX 970's have double the memory amount of his Radeon. I wonder what the size of his geometry is in GB... If it's 4,000 objects * 10,000 vertices each, that would be at least ~ 600MB of memory required (and that's not including the textures and other resources)... Hm. I suck at math. :)

dpadam450
dpadam450

From the last test on the 970's, it's obvious that the problem is the size of the geometry.

No, its the same VBO that holds 1 single triangle.......... that runs a single instruction vertex and pixel shader. It is bound once, and drawn 4000 times in different locations (in a grid pattern). That is all it does, and the performance is drastically different and it isn't because it has more shader processors. It may be the speed of the card or something else, either way I'm not so worried about my performance problem. Would be a good test to try it on a newer ATI card though. I should pop in my old GTS 450 and see if it handles it.

NBA2K, Madden, Maneater, Killing Floor, Sims 
JoshuaWaring
JoshuaWaring

Upload an executable and I'll run it on a 290, A6-5200 and a A4-1250

tonemgub
tonemgub




No, its the same VBO that holds 1 single triangle.......... that runs a single instruction vertex and pixel shader. It is bound once, and drawn 4000 times in different locations (in a grid pattern).

This is the sort of thing that instancing was invented for. :) Or even tessellation.

JohnnyCode
JohnnyCode

You could performance compare this instruction with building a gpu vertex buffer from cpu by a cycle writing values to entire gpu buffer. Wheather buffer would be writtable, or imutable recreated.

4000 separate draw calls is performative operation.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.