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

[.net] Game loop with Managed DX.

Started by wyrd Aug 2, 2004 at 2:48 PM 27 replies 5.8k views
Original Post
wyrd
wyrd
The samples I've looked at all override the OnPaint method. Aside from calling Application.DoEvents() (which is not a good thing to be doing), are there other, more natural ways to do game loops with MDX? Or despite the funkyness of it, is OnPaint really the only valid options? My whole thing is that it doesn't feel right to be putting game logic (like AI updates) in the OnPaint method. Here's a link to the OnPaint loop; http://geekswithblogs.net/jolson/articles/2173.aspx
Zyme
Zyme
you could create a Rendering thread and call Thread.Sleep to allow other threads to execute it may help you.
Zyme the fruit of um, er k what ever :D
DracosX
DracosX
I don't claim to know everything about this, heck, I don't claim to know anything about anything but this is something like I'm using.

class Win32_API{	public enum PeekMessage_Paramaters	{		No_Remove	= 0,		Remove		= 1,		No_Yield	= 2	}	public struct MSG	{		public IntPtr	hwnd;		public uint		message;		public uint		wParam;		public uint		lParam;		public uint		time;		public POINT	pt;	}	public struct POINT	{		public Int32 x;		public Int32 y;	}	[DllImport("user32", EntryPoint="PeekMessage")]	public static extern bool PeekMessage(out MSG lpMsg,                                              IntPtr hWnd,                                              Int32 MsgFilterMin,                                              Int32 MsgFilterMax,                           PeekMessage_Paramaters RemoveMsg);	[DllImport("user32", EntryPoint="TranslateMessage")]	public static extern bool TranslateMessage(ref MSG                                                   lpMessage);	[DllImport("user32", EntryPoint="DispatchMessage")]	public static extern Int32 DispatchMessage(ref MSG                                                   lpMessage);}


class Game{    private GameForm Window = new GameForm();    ...public void Run(){    Win32_API.MSG msg = new System_API.Win32_API.MSG();    while (Window.Created)    {        if (Win32_API.PeekMessage(out msg, (System.IntPtr)0, 0,                          0, Win32_API.PeekMessage_Paramaters.Remove))        {            Win32_API.TranslateMessage(ref msg);            Win32_API.DispatchMessage(ref msg);        }        else        {            // Game Code        }}}


EDIT: Changed PeekMessage to take an "out MSG" instead of a ref. Doh!

[Edited by - DracosX on August 6, 2004 10:34:22 PM]
DracosX:Master of the General Protection Fault
Washu
Washu
Yes, that's what you should be doing, Although, I would have it call another method from the else portion instead of directly inserting game code there. Makes it easier to reuse. You might want to consider actually seperating the whole thing out, so that you can just do something like:

Win32MessagePump pump = new Win32MessagePump();while(pump.DoEvents()) {  //game code}


Also, consider changing the ref to out, as they are output parameters (see PInvoke.net). While this won't really make a difference code wise, it does make a bit more sense, in my eyes. :P
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
DracosX
DracosX
Quote:

Yes, that's what you should be doing, Although, I would have it call another method from the else portion instead of directly inserting game code there. Makes it easier to reuse.

Yeah, that's actually what I do, that was just a quick throw-together for the sake of the OP. Although, I probably should have made that clearer in my post.

You make some great points though, especially with the thought of a pump object... And you're also right, that should be an out instead of a ref (doesn't affect what the code does, afaik, it just makes more sense really) but once again, I just kinda threw that together for the sake of the discussion.
DracosX:Master of the General Protection Fault
Holy Fuzz
Holy Fuzz
Hm... I don't do anything inside OnPaint or the message pump at all. My program just runs in a loop the keeps cycling until the program exits. Basically, it looks vaguely like this:

while(running){  game.GetUserInput();  game.Draw();  if(/*we should update the game logic*/)    game.Update();  Application.DoEvents();}


Which works perfectly fine.
Washu
Washu
Quote:
Original post by Holy Fuzz
Hm... I don't do anything inside OnPaint or the message pump at all. My program just runs in a loop the keeps cycling until the program exits. Basically, it looks vaguely like this:

*** Source Snippet Removed ***

Which works perfectly fine.


Read this, this and this to see why Application.DoEvents() is bad.
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
wyrd
wyrd
Thanks for the replies. It looks like the updated samples use a fairly similar approach.
Holy Fuzz
Holy Fuzz
Okay, so I'll buy that using Winforms and DoEvents() is bad (unless you need the extra functionality, which I don't), but why is drawing in the paint event (however you handle it) the preferred way to do it? It seems a little clunky to me...
wyrd
wyrd
Read the links that Washu posted.
Washu
Washu
Quote:
Original post by Holy Fuzz
Okay, so I'll buy that using Winforms and DoEvents() is bad (unless you need the extra functionality, which I don't), but why is drawing in the paint event (however you handle it) the preferred way to do it? It seems a little clunky to me...


If you read the links I posted you will see that they don't use the paint event at all. They still use the good ol' render loop. The difference is: Instead of using windows forms, they have rebuilt the classes from the ground up using PInvoke. So the good ol' render loop uses our favorite functions: PeekMessage, TranslateMessage and DispatchMessage.

As DracosX showed, doing this yourself is really quite easy. And if you keep in mind the changes I recommended, then it will give you a pretty reusable chunk of code that you can port from code base to code base.
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
Holy Fuzz
Holy Fuzz
Yeah, I read the articles. I just got the impression that you were supporting doing drawing inside the paint event when you said "Yes, that's what you should be doing, Although, I would have it call another method from the else portion instead of directly inserting game code there."

My bad. Sorry for the confusion.

- Fuzz
bL0wF1sH
bL0wF1sH
Geez, I'm gone one week and someone points towards one of my articles on my blog. Wow. Anyways, unfortunately I wrote that article on using OnPaint when coming across a suggestion by Tom Miller without really understanding the true reasons for doing so. I will be updating the article soon enough to use the method that the latest examples released with the Summer update use. While they are supposed to be the best way to achieve good performance when using Managed DirectX, I still feel that it seems like two steps backwards to have to use PInvoke from Managed DirectX....
Jason Olson - Software Developer[ Managed World ]
wyrd
wyrd
Quote:
Original post by bL0wF1sH
Geez, I'm gone one week and someone points towards one of my articles on my blog. Wow. Anyways, unfortunately I wrote that article on using OnPaint when coming across a suggestion by Tom Miller without really understanding the true reasons for doing so. I will be updating the article soon enough to use the method that the latest examples released with the Summer update use. While they are supposed to be the best way to achieve good performance when using Managed DirectX, I still feel that it seems like two steps backwards to have to use PInvoke from Managed DirectX....


It probably is, but for some reason the PInvoke ways feels more natural and intuitive. It just doesn't "feel" right [to me] making a game loop by calling Invalidate() inside the overridden OnPaint method. I'm sure others feel the same way. It's probably one of the main reasons why people still use Application.DoEvents() despite being warned about it.
Washu
Washu
Quote:
Original post by bL0wF1sH
Geez, I'm gone one week and someone points towards one of my articles on my blog. Wow. Anyways, unfortunately I wrote that article on using OnPaint when coming across a suggestion by Tom Miller without really understanding the true reasons for doing so. I will be updating the article soon enough to use the method that the latest examples released with the Summer update use. While they are supposed to be the best way to achieve good performance when using Managed DirectX, I still feel that it seems like two steps backwards to have to use PInvoke from Managed DirectX....


Well, if you look at the .net framework, specifically the WinForms component, you can see that it is all PInvoke. In fact, they even have a special class, System.Windows.Forms.UnsafeNativeMethods in which they have imported pretty much a large chunk of the Win32 API. The only operating system where you don't PInvoke to access the Native OS is on longhorn (which is VERY sweet btw...just an FYI)

So, the thought that PInvoke is a step backwards is really just an illusion. Because when looking at the overall picture you see that pretty much the whole framework, when it gets to core parts, requires PInvoke to access the native OS.

I mean, you don't HAVE to use PInvoke...it's just the "best"1 method at this point in time.

Now, on that note: The DirectX demo framework is quite extensive, and very clean and simple to use. For doing quicky stuff, it sure makes life easy. About the only beef I have with it is that it lacks documentation still. We were promised documentation damn it!

1"Best" as in the most appropriate method in regards to current game programming styles, involving a "game loop"
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
nagromo
nagromo
I just use SDLDotNet for input; it seems a lot easier than going back to unmanaged windows code. You can use it for input without using it for anything else (although I do). It seems a lot easier to cal SDL.Instance.DoEvents() than make a custom message pump.
Obsidience
Obsidience
Does anyone know of the performance of using a separate thread for rendering versus DracosX's render loop?

I'm new to game programming but since I had some multithreading experience decided to head in that direction for my render loop.

Best regards,

Obsidience
DracosX
DracosX
You'll probably get (at least) slightly better performance with a multithreaded render loop (especially on SMP machines), however, ymmv depending on the actual implementation you're using. I haven't (yet) had the time to write up and profile the render loop I posted earlier versus one that is run in a seperate thread, but that is on my list of things to do with my engine this week. Currently, I'm pushing about 380 FPS with my engine on my AthlonXP 2200+, GeforceFX 5200 dev machine using one similar to the one I posted earlier dipping to about 320 FPS during the most graphics intensive sections. That's great at this stage of development, and it shouldn't be too much work to implement a pluggable render loop to test different techniques.

I'll post some code as well as some benchmarks just as soon as I get the time.
DracosX:Master of the General Protection Fault
Stru
Stru
Application.DoEvents() works fine for me and my memory usage remains constant (as long as I just let the game just sit there). And when I profiled it DoEvents was insignificant compared to the other stuff in the render loop. I think it might have to do with the properties of the form its on. My game runs at 60 FPS so memory allocation from DoEvents() would make it choke up pretty fast, either its not allocating memory as some people claim or the memory is being freed immediately.

Another approach could be to separate your game loop into a separate thread from the form. Then just use System.Threading.Thread.Sleep(0) To me that seems the most intuitive way to handle it, and I prefer to avoid messing with windows. Code is so much cleaner when its not full of junk from windows api. Maybe I'm just too picky...
Stru
Stru
Quote:
Original post by Anonymous Poster
Quote:
Original post by Stru
Code is so much cleaner when its not full of junk from windows api.


4 lines of code is considered "full of junk?" That's interesting.


Its structuring the game based on windows that really bothers me, not so much how many lines of code from the api you use.

Topic Locked

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

Sign in to reply to this topic.