• Advertisement
Sign in to follow this  

[.net] Help with message loop, MDX/C#

This topic is 4318 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So, I've been trying to port my game over from C++/SDL to C#/MDX. It's going pretty smoothly, but I'm encountering problems now, I think. I don't want to use Application.Run() to run my application, I'm using an object-oriented stack-based method for managing gamestates and input and such, so that's a no go. I've been taking a look at this article http://www.codeproject.com/cs/media/ManagedRenderLoop.asp?df=100&forumid=198963&exp=0&select=1426549 for some guidance, and I'm not quite sure if that's what I'm looking for. Firstly, I fail to understand how putting the game loop in an idle event increases performance... wouldn't the game lag if the user was moving their mouse across the screen or something? secondly, my game loop is set up like this: while( looping ) { Application.DoEvents(); HandleInput(); Render(); } And I can't get more than 65 or so FPS, so I'm assuming it's locked. Is there something I can do to unlock the framerate? This all seems like a terrible pain in the rear end, I hope I'm just not understanding something and that it's really not this difficult :/

Share this post


Link to post
Share on other sites
Advertisement
that is the normal way to do it.

The 'idle' event gets fired when the message queue is empty, so it's not really 'idle', more 'about to go idle'. So acting on it by rendering (and repeated rendering until there is a message to be dealt with) is pretty much as high performance as you can get.

65 fps sounds like you have v-sync enabled. Either that or your frame rate counter is broken :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
Is there something I can do to unlock the framerate?

Yep, just set the PresentParameters property PresentationInterval to PresentInterval.Immediate.

Share this post


Link to post
Share on other sites
Quote:
Firstly, I fail to understand how putting the game loop in an idle event increases performance... wouldn't the game lag if the user was moving their mouse across the screen or something?


That was my first impression too, but it works like a charm. Because you stop rendering for a few millissecs to handle messages (using the AppStillIdle 'trick'), there's absolutely no lag in the user input (but maybe I'm just slow [wink] ).

In this recent thread you'll find a somewhat better explaination, but the OnIdle/AppStillIdle loop is better since it does not require any extra allocations/collections/wrapping around Window Messages. This means it comes with a minimum of overhead, only yielding to messages/events when there actually are some to process.

Hope this answers your question :)

Share this post


Link to post
Share on other sites
If using C#/MDX, I would recommend using the DXUT toolkit ("Framework") that comes with the Empty Project that you can get from the DirectX Sample Browser. It takes care of device handling for you, and calls you back to "step" and "render" your application.

Share this post


Link to post
Share on other sites
The SampleFramework also uses the OnIdle/AppStillIdle loop btw, so that would mean it's the 'official' way to do it. Although I've used the SampleFramework quite extensively, I'd recommend coding your device handling and looping yourself.

The framework is great to get things done quickly (and the GUI, Timer etc are good components to reuse), but it has quite some overhead and it doesn't always work as well or staightforward as it should IMO. If you have the time and are comfortable with it, I'd go for coding things from ground up yourself.

Share this post


Link to post
Share on other sites
maybe I'm still misunderstanding something... but wouldn't something like this:

while( running )
{

if( !peekmessage( ... ) )
{
Application.DoEvents();
}

OtherStuff();

}

give the exact same performance, and make it easier to do framerate-indepentant tasks? I'm finding it rather silly that this kind of hack is required to get the best performance.

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
maybe I'm still misunderstanding something... but wouldn't something like this:

while( running )
{

if( !peekmessage( ... ) )
{
Application.DoEvents();
}

OtherStuff();

}

give the exact same performance, and make it easier to do framerate-indepentant tasks? I'm finding it rather silly that this kind of hack is required to get the best performance.


A lot of it is because Application.DoEvents() allocates memory every time, which increases the frequency of garbage collections. Using the idle loop actually makes few or no allocations as part of the loop itself.

Share this post


Link to post
Share on other sites
no no no no no no no no no

DO NOT USE: Application.DoEvents

Unless you want to have memory problems, performance issues, and overall beat on the GC for no apparent reason.

Share this post


Link to post
Share on other sites
Before everyone beats on poor old DoEvents please go and profile some code using the different styles of RenderLoop.

Bottom line you will see more GC0s. In a very constrained memory situation you might be unlucky and get some GC1s but if that's the case you either have very little memory to start with or your app is very memory hungry in which case the DoEvents overhead is the least of your problems. If you don't know what a GC0 or a GC1 is then read the advice at the end.

I've looked at Perfmon while running my MDX apps and as long as you keep the memory doing just GC0's then you will be spending less than 1% of your time in the GC. That's tiny - unless you are some kind of game programming guru you can usually improve perf more than 1% by fixing your algorithms and drawing less. If you start seeing GC1 or GC2 then you have other problems that the render loop alone probably won't fix. Now sure, my profiling has just been done on some small test apps so I'm not claiming 100% certainty but I've sat and watched framerates and perfmon traces and not seen anything significant. I'm also convinced that the bigger the app the less significant any DoEvents overhead becomes. Yes total allocations is higher - but memory doesn't actually wear out.

Sure Ricks Blog showed a 200000% increase.... when it was doing nothing else. At this point its just statistics. The rest of the stuff inside your render loop is going to take orders of magnitude more time than DoEvents therefore dwarfing any effect the actual call will have.

So yes OnIdle/Peek events is the most efficient memory and speed wise and IMO there's no real reason to avoid it. But if you are more comfortable using DoEvents and you are not writing Half Life 3 then really, really don't worry about it.

*NOTE: Last time we had this debate I was informed that there is/was a memory leak situation with DoEvents where sometimes memory does not get reclaimed until the application quits. I've tried to repro this and have never been able to, nor have I seen a .Net bug report so its either an edge case or something that got fixed from an early .Net version.

Share this post


Link to post
Share on other sites
Quote:
Original post by Saruman
no no no no no no no no no

DO NOT USE: Application.DoEvents

Unless you want to have memory problems, performance issues, and overall beat on the GC for no apparent reason.


Guess you can't get it any clearer than that [smile] RenderTaget already pointed out why, but I can also confirm that the performance improvement *may* be very significant. You don't need to worry if it's a hack (it's the official way after all) and conceptually I think it's quite elegant.

Edit: ZMan obviously also has a point, but if you know the most efficient way, why not just go with that?

[Edited by - remigius on April 22, 2006 5:59:53 AM]

Share this post


Link to post
Share on other sites
Alright, thanks for all the info. I think for now, while I'm building the app, I'm going to leave it at DoEvents just to keep things simple and clean, and when I have some significant work done I'll switch it to AppIdle and see what kind of performance I get. It seems like it would be kind of a pain to get the timing done properly if the render loop won't be running while there are messages on the queue, but I'll see how it goes.

Share this post


Link to post
Share on other sites
No matter which loop you use you have to 'pause' at some point to look at or remove messages otherwise you are a bad windows app. None of these render loops tries to do anything clever with messages other than remove/process them between frames. Even Halo/Half Life/Doom etc etc have to do it.

Share this post


Link to post
Share on other sites
I understand that, but with DoEvents(), the pausing is contained within the main loop, so you could more easily track the framerate, and such. Messages are handled once per frame, easy as pie. With AppIdle, who knows how long it takes for the app to process the messages? Seems like it would make framerate-independent tasks a chore.

EDIT: I'll just ask this here, to avoid spamming the boards... are arrays of events feasible? I think it would be useful for my Keyboard class. Something like this is what I'm looking for:

keyboard.KeyDown[(int)Key.W] += MoveForeward();

I haven't found anything on it, so I'm not sure if it's possible, but someone here ought to know.

Share this post


Link to post
Share on other sites
I think you are confusing what AppIdle is. AppIdle is called by the main thread when no messages are being processed. Not when the CPU is idle. If you look at the AppIdle loop it sits in a tight loop calling Render (one render per frame) unless it sees something in the message queue in which case it drops out of the tight loop and allows the message to be processed. As soon as that message is done then Idle will be called again.

So in this case the 'pausing' is also contained within the loop except it only pauses when it needs to. The DoEvents (and its overhead) is called in the other loop whether its need or not (another reason this is the right way).

There's been lots of smart thinkers look at this during MDX's lifetime - its not going to get any better than this even though it shows up in the forums at least once a month with people who think they can improve on it.

Share this post


Link to post
Share on other sites
Quote:
Original post by thezbuffer
I think you are confusing what AppIdle is. AppIdle is called by the main thread when no messages are being processed. Not when the CPU is idle. If you look at the AppIdle loop it sits in a tight loop calling Render (one render per frame) unless it sees something in the message queue in which case it drops out of the tight loop and allows the message to be processed. As soon as that message is done then Idle will be called again.


I understand how it works. My worry lies in timing. Since messages are handled behind the scenes as far as the programmer is concerned, how can you reliably time each frame? I mean, it's easy to time the Idle loop, but how are we to know how much time the application spent NOT idle, outside of the loop?

Quote:
So in this case the 'pausing' is also contained within the loop except it only pauses when it needs to. The DoEvents (and its overhead) is called in the other loop whether its need or not (another reason this is the right way).


Couldn't that easily be fixed by something like, if( !AppStillIdle ) App.DoEvents();?

Not that it matters, I'll just go with AppIdle. I'm curious though as to how much of a performance increase AppIdle gives. I think I'll do some tests.

Hmm, average framerate for AppIdle was about 2870, average for DoEvents() was 2840, for a program that does nothing but clear the screen and draw a triangle. Curious thing was that AppIdle used about 400kb less memory. Although, I guess it's not that odd if the GC really is collecting more often.

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
Since messages are handled behind the scenes as far as the programmer is concerned, how can you reliably time each frame? I mean, it's easy to time the Idle loop, but how are we to know how much time the application spent NOT idle, outside of the loop?

Because you time frames by checking the timer at the start of each frame and subtracting the value from the start of the last frame. There is no 'missed' time. Any time spent processing messages will be included in the frame that was rendered right before the message was processed.

Quote:
Original post by nsto119
Couldn't that easily be fixed by something like, if( !AppStillIdle ) App.DoEvents();?

I'm not sure its a fix but you can use that if you want the extra memory overhead.

Quote:
Original post by nsto119
Hmm, average framerate for AppIdle was about 2870, average for DoEvents() was 2840, for a program that does nothing but clear the screen and draw a triangle. Curious thing was that AppIdle used about 400kb less memory. Although, I guess it's not that odd if the GC really is collecting more often.

And thank you for proving my point - there is no significant difference and thats on an application that does nothing. Lets look at a real app with a bit of math here using your numbers.

OnIdle 2870fps = 0.348ms per frame
DoEvents 2840 = 0.352ms per frame
which is a difference of 0.004ms

Now lets assume your final release version game with all its optimising runs is targeted to 100fps. This means your total render time per frame is 10ms. The 'overhead' to perf is 0.004ms or 0.04% of the total.

But still use OnIdle... there's no point avoiding or arguing over something thats already been well researched and investigated. I just wanted to make the point that your app won't actually suck because you chose DoEvents and anything you read otherwise is, IMO right up there with the stolen kidneys as urban legends.

Share this post


Link to post
Share on other sites
I'm not trying to argue against using AppIdle, just questioning the things I don't understand about it. But it all makes sense now. Thanks for the help :)

Share this post


Link to post
Share on other sites
Quote:
But still use OnIdle... there's no point avoiding or arguing over something thats already been well researched and investigated. I just wanted to make the point that your app won't actually suck because you chose DoEvents


Quoted for truthosity (can't tell on the kidneys [smile]). Despite my efforts I was unable to reproduce any significant performance gain by switching from DoEvents to the OnIdle loop. After discussing this with ZMan, I think the performance gain I experienced was mostly due to cutting out the OnPaint/Invalidate form of looping. Since this is mostly associated with DoEvents, that should be where a good part of the legend comes from.

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
I'm not trying to argue against using AppIdle, just questioning the things I don't understand about it. But it all makes sense now. Thanks for the help :)


Sorry didn't mean you in particular - I meant in general when this thread comes up every couple of months and a whole bunch of folk go through the same old arguments and the same 'DoEvents is evil, 'DoEvents will kill your perf' arguments get thrown back into a searchable thread. I like to make sure the threads are ended with the facts for future readers.

Share this post


Link to post
Share on other sites
Quote:
Original post by thezbuffer
OnIdle 2870fps = 0.348ms per frame
DoEvents 2840 = 0.352ms per frame
which is a difference of 0.004ms

Now lets assume your final release version game with all its optimising runs is targeted to 100fps. This means your total render time per frame is 10ms. The 'overhead' to perf is 0.004ms or 0.04% of the total.

I utterly disagree with this theory as it proves absolutely, positively nothing in the context. I have been in a situtation with an actual real scene and it wasn't even close to the same as having a blank screen. Having a blank screen only shows your best possible outcome, once you have an actual game and scene and you are making memory allocations you will find that your performance does take a much bigger hit because now these DoEvents are bumping up other allocations in generations.

We ran into a big problem with this in our commercial C# game and had to switch away from DoEvents due to memory and GC issues.

So I agree.. with blank screens, single triangles, tetris, breakout, etc you aren't going to notice any type of performance difference. If you are building a more complex game with a large scene than you most definately will.

Share this post


Link to post
Share on other sites
Well at least we all agree on the fact that you should all use AppIdle and PInvoke even if we can't agree on the other stuff.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement