The Render Loop

If you've seen the SDK samples for C# or VB.NET, and have looked much at the graphics samples you may have noticed the render loop, which looks something like this:

while(Created)
{
    FullRender();
    Application.DoEvents();
}

Obviously the point of this method is to loop so long as the window is created (which normally implies the application is still running).  It looks harmless enough, and is relatively easy to understand, which is why we left it in the SDK, but in reality, this isn't the best way you would want to drive your render loop.

Aside from the 'ugliness' factor of the loop, the real problem here is the Application.DoEvents method.  It's surprising that many people don't actually realize what's going on in this method, and i'd bet you would find even more people that were shocked that there's actually an allocation happening in this method.

Let's look at this simple application:

public class Form1 : System.Windows.Forms.Form
{
    public Form1()
    {
        this.Show();
        for (int i = 0; i < 50000; i++)
        {
            Application.DoEvents();
            //System.Threading.Thread.Sleep(0);
        }

        this.Close();
    }
    static void Main()
    {
        try{Application.Run(new Form1());}
        catch { }
    }
}

All this does is show a form, call DoEvents 50,000 times, and exit.  Nothing fancy at all.  Running this through the CLR Profiler on my machine shows that during the lifetime of this application 6,207,082 bytes of data were allocated.  Comment out the DoEvents and uncomment the Sleep call, and the application only allocates 406,822 bytes on my machine.  Doing the math, that averages out to ~116 bytes allocated per call to DoEvents. 

If you're running a high frequency render loop, running at say, 1000 frames per second, that's 100k of data you're allocating per second.  This will cause quicker gen0 collections, which can needlessly promote your own short lived objects to gen1, or worse.

Morale of the story.  Just because it looks easy, doesn't mean it's the right idea.

Published 07 November 03 02:22 by tmiller
Filed under:

Comments

# Shaigan said on November 23, 2003 2:46 PM:
Hi, I just wanted to know what if the best pratice for the main render loop ? Is it better to call the peekmessage win32 function directly into the main render loop ?
# confuzuled said on February 5, 2004 7:19 PM:
Hey -- aren't ya going to answer the question? I mean, its one thing to point out that the examples suck, but, you could at least point people in the right direction... what is a better way to handle that loop?
# Chris said on February 13, 2004 6:26 PM:
Maybe use the paint events to render, or setup a timer?
# Jason Olson's Blog said on February 16, 2004 7:11 PM:
# Jason Olson's Blog said on February 16, 2004 7:18 PM:
# Mike Sax said on March 23, 2004 11:10 AM:
Hi Tom,

This is very true in a background thread, but what if you have a single-threaded application that you want to keep responsive while you're inside your loop... with Sleep() calls, the app will just appear frozen.

I'm not clear why DoEvents would allocate memory... is it really DoEvents allocating the memory, or is it the code that gets an opportunity to run due to the call to DoEvents (ie: the message pump gets processed) that is doing this?

-- Mike
# Mikel Rice said on March 30, 2004 9:00 AM:
Hey Tom, it would have been helpful if you mentioned this in your book "Managed DirectX 9 Graphics and Game Programming" as it sent up major red flags in my C++/MFC trained mind! Invalidating in an OnPaint runs counter to everything I've learned! :-)

I have heard people comment on using the OnIdle for rendering as opposed to DoEvents or OnPaint. Any opinion on that?
# Joshua Birtles said on July 13, 2004 6:04 PM:
I just noticed that the Aplication.DoEvents method was alocating memory. It seems strange that this would happen because in the Microsoft DirectX tutorials, this is what they use. I read somewhere that this may evetually get cleaned up automatically by .NET when resources get low, but I haven't tested it as I have 1gig ram.

I've tried running a render loop with the Application.Idle event, but that's no good because it is only fired once just before the app is about to go into the idle state, then it just sits there while idle.

I may try using a timer to call the loop or see if I can work out how to do a custom event that fires continuously whenever the application is in the idle state, otherwise I'll just stick with Application.DoEvents and blame Microsoft for the extraneous memory use.
# PeekMessage and Filtered Nested Message Loops | keyongtech said on January 18, 2009 12:23 PM:

PingBack from http://www.keyongtech.com/2312059-peekmessage-and-filtered-nested-message

# Label changes not being reflected until loop is done | keyongtech said on January 22, 2009 4:49 AM:

PingBack from http://www.keyongtech.com/720329-label-changes-not-being-reflected

# C# Lesson 1: The Main Loop &laquo; Real Time Software Rendering said on May 7, 2009 6:46 PM:

PingBack from http://code.dawnofthegeeks.com/2009/05/07/c-lesson-1-the-main-loop/

# Tom Miller s Blog The Render Loop | Insomnia Cure said on June 8, 2009 6:02 PM:

PingBack from http://insomniacuresite.info/story.php?id=10280

# Taking advantage of ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Opaque &laquo; Bobobobo&#8217;s Weblog said on June 11, 2009 4:06 PM:

PingBack from http://bobobobo.wordpress.com/2009/06/11/taking-advantage-of-controlstyles-allpaintinginwmpaint-controlstyles-userpaint-controlstyles-optimizeddoublebuffer-controlstyles-opaque/

# Game loop in C# &laquo; Bobobobo&#8217;s Weblog said on June 11, 2009 11:58 PM:

PingBack from http://bobobobo.wordpress.com/2009/06/12/game-loop-in-c/

New Comments to this post are disabled
Page view tracker