|
Stevie wrote: I'd like to use C#/VB.NET for the task.
well, I can't help you with that side of it, though you could easily integrate this as an off screen render using render to texture methods and then scale the viewer to look at the texture subportions with pan&scan or scaling.
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
I'm writing a game using C++/CLI (.NET 2.0), taking full advantage of .NET framework for the ease of writing dialog boxes, menus, etc, but keeping my core logic and OpenGL code in "native" C++. Since my main window is a .NET window, I am using a Timer control to trigger the rendering cycle. Since I want to draw as fast as possible, the timer interval is set to 1 ms.
The test scene I'm rendering contains a measely 10,000 triangles. My framerate is a poor 64fps -- running the executable directly (running in the IDE drops my frames to 5fps). I don't know a lot about graphics cards, but I do know their performance is sometimes measured in millions of triangle per sec. If you do the math, 10k triangles * 64fps = 640,000 triangles per second. NOT GOOD.
I do most of my development on a notebook that has a screen refresh rate of 60Hz. I thought that might be the problem, so I switched over to my tower, which is running an Nvidia GeForce 5900XT and a refresh rate of 75Hz. Guess what? I still get the 64fps rendering rate.
Is this the best I can expect from a .NET-encompassed application? Would switching back to an older technology like MFC or even plain old Win32 programming increase my framerate? I do have a good C++ background with pointers and all that stuff, so I'm not afraid to roll up my sleeves if that's what it takes.
Thanks in advance for your help.
|
|
|
|
|
It's possible your Timer interval is too low. If the CPU spends all it's time dealing with that, then there's little time left to run the overhead for DX/OpenGL.
You could either shuffle the loop to simply call itself when it finishes, or change the Timer interval to something like 10. This gives you a rate of 100, but remember: the human eye can't tell the difference between 100 and 30...
Ninja (the Nerd)
Confused? You will be...
|
|
|
|
|
I thought about the CPU time spent processing a low timer interval. But I was using the single-threaded Forms timer. The timer isn't going to continue toward the next interval until after the graphics processing is complete.
I also read an article by Luc Pattyn on this site that suggests the problem is that the accuracy of timers is dependent on the user's individual system. For instance, if my system operates such that my timer interval is limited to a minimum of 20ms (for round numbers), then I am limited to 50fps no matter what I do with the timer control.
That's a good argument for moving to a loop like you had suggested. But if I move everything to a self-calling loop, won't that keep adding to the call stack and eventually cause the memory to run out?
I know the human eye can't really distinguish much above 30ms. But what's going to happen when I create a more complete scene with 100k triangles instead of 10k triangles? I'll be down to 10fps or worse. And that's something the human eye WILL notice. I want to see a good chunk of framerate overhead so I know I'll still be OK when I get to rendering complex scenes.
|
|
|
|
|
The way I usually work it is to have the painting (or a call to the painting) done in the Form Paint event. Then you Invalidate the form at the end of this code, and the paint event finishes, before executing again at the first available opportunity.
Or, you use a Goto statement
Ninja (the Nerd)
Confused? You will be...
|
|
|
|
|
"Goto" statement.
I actually considered using the Paint event, but for some reason I got all paranoid that tying up the paint event would prevent other events from firing, effectively locking out the menu system, etc. Plus I'm not rendering to the form directly. The form houses a "System::NativeWindow" instance, which is where the rendering is actually done.
But I am doing something similar now, and it has boosted the framerate substantially:
<br />
void MyRenderingWindow::Shown(System::Object^ sender, System::EventArgs^ e)<br />
{<br />
while (Form::Created)<br />
{<br />
if (bDataAvailable)<br />
RenderScene();<br />
Application::DoEvents();<br />
}<br />
}<br />
I guess it's basically the same idea, just a little different approach. I'm now pulling between 80-110fps on my notebook, which is what I was getting for a similar application I wrote with VC++6/MFC.
Interestingly, my tower refuses to budge over 75fps. My graphics card is weird and has some kind of limiter that ties the glFlush() command directly to the refresh rate, which is set at 75Hz right now -- the most my monitor supports. I tried knocking the refresh rate to 60Hz, and my app limits itself to 60fps. I'll have to figure out how to override that garbage. There's no way a stock notebook graphics card should outperform a $250 GeForce card!!!
|
|
|
|
|
Xpnctoc wrote: My graphics card is weird and has some kind of limiter that ties the glFlush() command directly to the refresh rate, which is set at 75Hz right now -- the most my monitor supports. I tried knocking the refresh rate to 60Hz, and my app limits itself to 60fps. I'll have to figure out how to override that garbage.
ahhhhh, the picture grows clearer.... yup, graphics card setting. You are tied with Vsync. When this is set within the graphics driver, as soon as you issue a frame command, your program will wait until the next vertical sync before starting.
Go in and turn off the vertical sync.
nvidia control panel->
3D settings ->
Adjust image settings with preview ->
[check] Use advanced settings ->
[click] link "Take me there"
look for vertical sync and turn it to "off"
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
Xpnctoc wrote: I'll have to figure out how to override that garbage.
Actually, it's not garbage. Vsync helps prevent tearing and wasted processing time. Personally, I leave it off for development (FPS can help you determine bottlenecks) but switch it back on for testing, etc.
|
|
|
|
|
Jeremy Falcon wrote: wasted processing time.
It's not wasted if you have bragging rights. oooo... I have 185fps on my 60hz monitor! woohoo!
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
El Corazon wrote: It's not wasted if you have bragging rights.
Touché
|
|
|
|
|
El Corazon wrote: I have 185fps on my 60hz monitor!
That's it? And you're bragging?
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark Salsbery wrote: That's it? And you're bragging?
I didn't say what was being displayed....
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
Any time there's numbers being thrown around, I think of this dialog...
(from This Is Spinal Tap (1984))
Nigel: This is a top to a—you know, what we use on stage, but it's very, very special because if you can see...
Marty: Yeah...
Nigel: The numbers all go to eleven. Look...right across the board.
Marty: Ahh...oh, I see....
Nigel: Eleven...eleven...eleven....
Marty: ..and most of these amps go up to ten....
Nigel: Exactly.
Marty: Does that mean it's...louder? Is it any louder?
Nigel: Well, it's one louder, isn't it? It's not ten. You see, most, most blokes, you know, will be playing at ten. You're on ten here...all the way up...all the way up....
Marty: Yeah....
Nigel: ...all the way up. You're on ten on your guitar.. where can you go from there? Where?
Marty: I don't know....
Nigel: Nowhere. Exactly. What we do is if we need that extra push over the cliff, you know what we do?
Marty: Put it up to eleven.
Nigel: Eleven. Exactly. One louder.
Marty: Why don't you just make ten louder and make ten be the top number and make that a little louder?
[pause]
Nigel: These go to eleven.
"I think we can all appreciate the relevance of that now..." (Shaun - Shaun of the Dead)
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Overall I thought that was a dumb movie. But being a guitar player myself and having played in a couple local bands, I did really appreciate that scene.
"But these go to 11."
|
|
|
|
|
|
Xpnctoc wrote: Is this the best I can expect from a .NET-encompassed application? Would switching back to an older technology like MFC or even plain old Win32 programming increase my framerate? I do have a good C++ background with pointers and all that stuff, so I'm not afraid to roll up my sleeves if that's what it takes.
The first question should be identifying the bottleneck. Look at your CPU usage, if you hit 100% of a core, you are usually CPU bound (or very near). The problem with graphics drawing is there are many ways to do things. Immediate mode is the worst. If you are using immeadiate mode you will not likely get 1/100th the value listed for your card.
However, think about your triangle count for a minute. 64fps at 10,000 triangles is 640,000 triangles a second. Just below a million. Not bad for someone who hasn't even started trying to tune for performance. There are lots of ways to speed this up. If groups of your triangles don't move, you can use compiled vertex arrays, display lists, or vertex buffer objects. Each have different advantages.
If you have triangles textured, this reduces performance, but moreso if you have a larger number of texture binds than usual.
You might start looking through these[^] presentations to see if anything applies. Although nothing is specific to .NET, it offers hints for finding out what the bottleneck is, as well as drawing techniques that are faster.
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
First, I got rid of the v-sync. Between that and getting rid of the timer and going for a more direct loop, I'm cranking out a better framerate -- to where I was expecting the rates to be.
But you have an excellent point that I have not begun optimizing. There certainly are places I could use display lists, and I could re-sequence some of my data-binding to reduce texture binding calls. It will be interesting to see what additional performance I get once I make those modifications.
|
|
|
|
|
Let me ask you this too: Right now my code is set up to render a bunch of objects from closest to farthest away -- which I read somewhere is good practice to take advantage of the depth buffer. I could re-sequence the objects to render by texture binding, but that might mean more "in front" objects to render. So which is more costly -- repeated data binding operations or rendering more objects from back to front?
|
|
|
|
|
Xpnctoc wrote: So which is more costly -- repeated data binding operations or rendering more objects from back to front?
both, neither, and it depends. I wish there was a hard answer I could give you. Texture binds are more expensive. But if you have a reverse case (worst case) of drawing back to front, then you are rasterizing more than you need. Sometimes this is desired, for instance with transparency. So the answer is almost always a balance. It is a careful set of decisions, where one answer isn't always true.
Personally I would bind by texture, then sort within that binding. Are you doing a partical system that you have that many binds? There are ways to speed both up. You can used VBO's for static objects which also speed your texture binds since the textures are on the card and a VBO tells the card to keep the vertex object local to the card too.
There is no absolute, this is the only way to go.
I do use a scene graph myself, which attempts to make these decisions for you, organizing groups of objects by textures and materals first, then by distance. But even then you sometimes want your own control on the node to tell it when to render for specific appearance or calculaton issues.
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
No, not a particle system. I'm attempting to write my own railroad simulator, akin to MSTS or Auran's "Trainz" -- partly because I have complaints on how those software operate and partly because I just want to see if I can do it.
So I'm thinking from many different perspectives. Right now, the terrain engine is the main focus. I know terrains are a giant topic by itself, and I'm not terribly interested in making a science of it. The terrain is supposed to be user-modelable at runtime, which in my mind negates some of the really fancy algorithms anyway, because you don't know ahead of time what texture will be required from one tile to the next. So I'm just trying to improve on the brute-force algorithm a little bit.
I abandoned the idea of straight-forward triangle strips in favor of a spiral algorithm working from the camera outward to take advantage of the depth buffer test, especially when the camera is facing a large hill. Why bother to rasterize everything behind the hill? I also have some code in there that flags each tile as "in view" or not every time the user moves the camera, so at any time I'm only drawing about 1/4 of the actual data grid.
If I sorted by texture type, that would reduce the number of texture binds GREATLY, but at the expense of more vertex calculations because there would be no guarantee of a linear block of tiles having the same texture.
|
|
|
|
|
Xpnctoc wrote: which in my mind negates some of the really fancy algorithms anyway,
not necessarily, it all depends on how you are doing the blending. You can also use a shader in which case you can bind up to 8 textures to all materials, and leave them bound, use a shader to look up the appropriate texture at run time and use it. This is how multi-texture blends work. You can do some REAL fancy footwork with dynamic terrain, I do so now and then. Right now I run quad-tree, I have one bound texture per quad, plus some global textures working off shaders that are blended via shading program. Check out vterrain.org. (not mine, though my project is mentioned as one of the 100's of projects on one small page out of the dozens).
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|
Xpnctoc wrote: I am using a Timer control to trigger the rendering cycle. Since I want to draw as fast as possible, the timer interval is set to 1 ms.
There's one bottleneck most likely. I'm not a .NET person, but if it's anything like VBs timer control, it uses WM_TIMER. That 1ms resolution from WM_TIMER is a joke, you'll never get that. Instead, your rendering cycle should just be in a loop that will allow important messages to interrupt it and otherwise not care one bit about them.
|
|
|
|
|
Xpnctoc wrote: Is this the best I can expect from a .NET-encompassed application? Would switching back to an older technology like MFC or even plain old Win32 programming increase my framerate? I do have a good C++ background with pointers and all that stuff, so I'm not afraid to roll up my sleeves if that's what it takes.
Personally, and this is just my opinion, I'd never use .NET for a game. Most of your code .NET won't handle anyway. And, adding the extra layers of garbage for just dialogs when VC++ has a native dialog designer (ok, it sucks though) isn't worth it IMO.
|
|
|
|
|
I noticed in your first response you said you're not really a .NET guy. In general you are right that .NET does add layers of garbage. I sure wouldn't try writing something requiring high performance in VB.NET or C#.NET. There are a number of known areas where .NET lacks performance. For instance, a quick programming experiment easily shows that, the .NET List<> class implementation of a quick sort is easily outperformed by a natively-programmed quick sort.
HOWEVER, C++/CLI allows you to mix .NET code with native C++. They call it "IJW" code (It Just Works). These sections of code are build directly to machine language rather than MSIL/CLR. This allows you to pipe through native code for execution without incurring the overhead of .NET and runtime compilation or interpretation.
In fact I have been architecting my software to do just that. The whole app is containerized in the .NET framework because of the ease of the form designers, etc. BUT all of my core logic is written in native classes that get built directly to machine code rather than interpretable CLR.
My software is also more what I would classify a modeler/simulator rather than a "game". Without going into all the detail, suffice it to say a very large number of dialogs is required, such that in my mind the little extra effort to create a "mixed mode" program is more than worth the effort.
Since I have managed to ditch the timer and move to a continuous rendering loop, my framerates have increased to where I expected to see them. I also haven't yet optimized any of my OpenGL instructions (using display lists, sequencing rendering to reduce state changes, etc.), so even more is yet to be gained. I don't think at this point I'm losing any performance by using "mixed mode" programming and letting .NET make life a little easier where it can.
Current performance: 160-239 fps running the exe directly, and even 20-25 fps through the Visual Studio IDE with all the debugger garbage running in the background. That's much better than the 64/5 I was getting when I started this thread!
-- modified at 11:03 Wednesday 26th September, 2007
|
|
|
|
|
Xpnctoc wrote: My software is also more what I would classify a modeler/simulator rather than a "game".
I normally joke that work pays me to play games all day. Nudge, nudge, wink, wink, know what I mean?
_________________________
Asu no koto o ieba, tenjo de nezumi ga warau.
Talk about things of tomorrow and the mice in the ceiling laugh. (Japanese Proverb)
|
|
|
|
|