10
\$\begingroup\$

I have seen different "basic" game loops both from books like "Introduction to 3D game programming with DX11" or online tutorial ( Rastertek ) and if we take off all the fancy stuff to control framerate and more they all look roughly like this :

while (msg != WM_QUIT) // 
{
    if (PeekMessage(&msg,0,0,0,PM_REMOVE)
    {
        // Translate and dispatch message
    }
    else
    {
        // Do update, rendering and all the real game loop stuff
    }
}

If you run it this way it works perfectly, but the thing that i do not understand is the else. If i look naively at the loop i might think that if the system keeps sending events ( key presses, mouse clicks and more ) the else part will never be executed, am i wrong ? ( yes!! i am but this is exactly the point, i don't understand why! ).

I'd like to understand why the loop works this way. If the messages were strictly related to the window ( resizing,moving,closing ) i'd understand it, but they are not. Let's say that i keep pressing any key a WM_KEYDOWN message is repeatedly sent and a message is peeked, thus never allowing the else part to execute

I have not a lot of experience with the Win32 library and i'm probably missing something^^.

Thank you for your time and happy coding!

\$\endgroup\$

5 Answers 5

10
\$\begingroup\$

I have not seen an else being used in this instance (the Rastertek tutorial you mentioned does not use an else).

My guess would be if that you tried to resize your window the game rendering would freeze proving that the else part is never executed, as the resize message would be constantly sent.

A common loop would have this structure.

while (true) 
{
    while (PeekMessage(&msg,0,0,0,PM_REMOVE)
    {
        // Translate and dispatch message
    }

    if (msg.message == WM_QUIT)
    {
        break;
    }

   // Do update, rendering and all the real game loop stuff

}
\$\endgroup\$
6
  • \$\begingroup\$ yes sorry, my fault, Rasterek tutorial does exactly as you say. But the code sample i provided is used in "Beginning 3D Game programming with DX11" \$\endgroup\$ Jul 28, 2013 at 18:49
  • 3
    \$\begingroup\$ Avoiding the else can cause problems, by delaying important windows messages like the ones for mouse or keyboard input. If you get two key down messages in one frame you probably want to process them both before rendering it for example. \$\endgroup\$
    – Adam
    Jul 28, 2013 at 20:03
  • \$\begingroup\$ Yes sorry, it was supposed to be while. \$\endgroup\$
    – Syntac_
    Jul 28, 2013 at 20:09
  • 1
    \$\begingroup\$ A bit off topic here but I'm assuming that in the example above the msg-declaration is missing inside the while(true) statement correct? If that is not the case, the if statement will fail if there are two or more messages and the WM_QUIT message isn't last one in the queue. \$\endgroup\$
    – Dadgron
    Jul 30, 2013 at 16:44
  • 1
    \$\begingroup\$ PeekMessage will only ever return WM_QUIT once the message queue is empty therefore guaranteeing that the last message is in fact WM_QUIT. msdn.microsoft.com/en-us/library/windows/desktop/… \$\endgroup\$
    – Syntac_
    Jul 30, 2013 at 23:54
4
\$\begingroup\$

I think the thing you need to grasp is that the message processing will run much faster than the rate at which messages are sent.

For the example code you've provided, here's an estimate of timings:

while (msg.message != WM_QUIT) // 
{
    if (PeekMessage(&msg,0,0,0,PM_REMOVE)
    {
        // Translate and dispatch message
        // [A] --> This takes a very small amount of time (epsilon ms)
    }
    else
    {
        // Do update, rendering and all the real game loop stuff
        // [B] --> This takes way more time, ~16 ms at 60 fps for instance
    }
}

I've not searched the documentation for this but Windows is probably not spamming messages all the time: they have to come at regular intervals or they'd be clobbering the system. Maybe they use the keyboard refresh rate if you keep pressing one key. Maybe they use the screen refresh rate.

Let's say a new message comes every t ms, reasonably greater than epsilon. The [A] part of the loop will process all stacked messages very quickly: it will run for epsilon ms and start again, which leaves no chance for new messages to arrive. As soon as all messages are consumed, the [B] part will run and new messages will stack again.

Here's an illustration to try to make my point, [An] are processings of the [Mn] messages sent by Windows, and [Bn] are actual frame updates:

Game:    --[A1][B1----------][A2][A3][A4][B2----------][A5][A6][A7][B3------->

            ^       ^       ^       ^       ^       ^       ^      ^      
Windows: ..[M1]....[M2]....[M3]....[M4]....[M5]....[M6]....[M7]....[M8]....

Here you can see that as long the duration of [A] stays reasonably small compared to the message rate (it's already very exaggerated here), the game will still have plenty of time to update its frames.

So this explains why such a game loop structure doesn't prevent a game to run normally. But the solution CoderScott proposed in his answer makes this execution flow much more explicit, so in my opinion it should be preferred.

\$\endgroup\$
3
\$\begingroup\$

No, you are not wrong. Actually that's true - if there are too many events the else will never be run, or at least will not be run quickly enough. If your UI methods take too much time to process the event this could lock the OpenGL refresh. First of all you should check why processing events is taking that much processing, but in sometimes you can be spammed by events (like quick mouse move with big refresh rates) and then even simple processing takes too much time to be in line with OpenGL frame refresh timings.

This loop actually is really bad and it hit me as I copy pasted it from many resources on the internet too (note this is wrong approach):

while( true ) {
    if ( PeekMessage( &m_msg, m_hWnd, 0, 0, PM_REMOVE ) ) {
        if ( WM_QUIT == m_msg.message ) {
            break;
        } else {
            TranslateMessage( &m_msg );
            DispatchMessage( &m_msg );
        }
    } else {
        processFrame();
    }
}

The correct one is like this:

while( !finished ) {
    DWORD currentTick = GetTickCount();
    DWORD endTick = currentTick + 1000/FRAMES_PER_SECOND;

    while (currentTick < endTick) {
        if ( PeekMessage( &m_msg, m_hWnd, 0, 0, PM_REMOVE ) ) {
            if ( WM_QUIT == m_msg.message ) {
                finished = true;
                break;
            } else {
                TranslateMessage( &m_msg );
                DispatchMessage( &m_msg );
            }
            currentTick = GetTickCount();
        } else {
            break;
        }
    }

    processFrame();
}

So with additional break, if the processing events procedure consumed too much then a forced OpenGL frame refresh is made.

\$\endgroup\$
1
  • \$\begingroup\$ Instead of that while (currentTick < endTick) loop consider use of MsgWaitForMultipleObjects(0, NULL, FALSE, timeLeftInFrame, QS_ALLEVENTS); otherwise your loop will cause 100% CPU load. \$\endgroup\$
    – c-smile
    Jan 17 at 3:24
1
\$\begingroup\$

if/else in your loop will process windows messages before anything. It will not block loop too long (of course if your WndProc will be good). Usually there is not so much messages. I dont think there is a better way to prevent window from stop responding and save FPS. If you will keep your message processing code good i think your loop will be great.

Here is my code, i believe it will be interesting for you, and it is quite usable. But one day i will code a new one. At least it is much better than many other. For playable game you will definitely need a stable state update() rate. Windows messages will not break it.

// Game loop paramters.
const int TICKS_PER_SECOND = 70; // Update() rate per second
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 10;

Run() function:

DWORD next_game_tick = GetTickCount();
int loops;
MSG msg = {0};
m_bIsGameRunning = true;
while( msg.message != WM_QUIT && m_bIsGameRunning )
{
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        g_pWindow->HandleMessage(msg); // translate and dispatch the message
    else
    {
        // *Update*
        loops = 0;
        while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP)
        {
            Update();

            next_game_tick += SKIP_TICKS;
            loops++;
        }

        // *Draw*
        Render();
    }
}
m_bIsGameRunning = false;
\$\endgroup\$
1
-1
\$\begingroup\$

I am working on an OpenGL Shader Engine from www.MarekKnows.com and the framework is setup as follows: An Engine Class that is part of a static library with other helpful and important classes. Then there is the main startup project where the Game Class resides along with Game Properties. This structure is to keep all Engine code separate from Game code. The nice thing about this setup is it can be reused to create a variety of games just by inheriting from the Engine lib. It is not complete as of yet as each week another segment or class object is being incorporated.

This hierarchy is based on the folder & VS filter structure:

Project - Static Lib Engine

  • Animation
    • AnimationManager: Inherits from Singelton

  • Engine
    • AssetStorage: Inherits from Singleton - Stores All Game Assets & handles their memory
    • Batch: Stores vao & vbo information into batches before rendering to the GPU
    • BatchManager: Inherits from Singleton - Manages Batches, Adds To queue when necessary and empties buckets when full sending batches to GPU
    • BlockProcess & BlockThread - Helper Classes for Process & Threads
    • Engine: Inherits from Singleton - Abstract Base Class to Game**
    • ExceptionHandler - Handles All exceptions works closely with the Logger & console output.
    • FontManager: Inherits from Singleton - handles all fonts to be loaded into memory and prepares them before rendering and stores them in the AssetStorage.
    • Logger: Inherits from Singleton
    • Settings: Inherits from Singleton Various Settings needed for Engine: physics refresh rate, isWindowed, randomNumGen, gamePixelSize, openglVersion etc.
    • Singleton - Base Class
    • Utility - Utility Functions that are useful - converting functions from numbers, vectors etc. to strings and vice versa.
    • CommonStructs - Header Only basic structs & enums common to many objects

  • FileHandlers
    • FileHandler: Base Class
    • MkoFileReader: Inherits from FileHandler - Reads in MKO object Files & Parses Data
    • TextFileReader: Inherits from FileHandler - Reads in Text Files
    • TextFileWriter: Inherits from FileHandler - Writes to Text Files
    • TextureFileReader: Inherits from FileHandler - Reads in Texture Files (currently supported - tga & png )

  • GUI

    • Gui.h - helper *.h file to easily include all GUI elements
    • GuiCompositeElement: Inherits from GuiElement, Abstract Base Class
    • GuiElement: Abstract Base Class
    • GuiImage: Inherits from GuiRenderable
    • GuiImageElement: Stand alone class that is used with Gui Objects
    • GuiLabel: Inherits from GuiText
    • GuiLayout: Inherits from GuiRenderable - Abstract Base Class
    • GuiLayoutAbsolute: Inherits from GuiLayout
    • GuiLayoutGrid: Inherits from GuiLayout
    • GuiLayoutSlices: Inherits from GuiLayout
    • GuiLayoutStack: Inherits from GuiLayout
    • GuiLoader: Loads in a text file and parses data to load & render any gui elements
    • GuiRenderable: Inherits from GuiCompositeElement - Abstract Base Class
    • GuiScreen: Inherits from GuiCompositeElement - Can have children nodes but does not have any parents for it is the top level parent
    • GuiText: Inherits from GuiRenderable
    • GuiTextArea: Inherits from GuiText

    MKO - This MKO Object Hierarchy Relies Heavily On Templates

    • Array2d: Inherits from BaseMko
    • BaseMko: Abstract Base Class
    • FontFile: Inherits from BaseMko
    • Image2d: Inherits from VisualMko
    • Surface3d: Inherits from BaseMko
    • VisualMko: Inherits from BaseMko, Abstract Base Class

    Shader - This Shader Hierarchy Relies Heavily On Templates

    • ShaderManager: Inherits from Singleton, Manages Shaders
    • ShaderProgramSettings
    • ShaderVariable

Start Up Project - Game

  • Game: Inherits from Engine
  • Properties: - Properties unique to current game

With this frame work I'll demonstrate the message loop for you

void Engine::start() {
    while( true ) {
        if ( PeekMessage( &m_msg, m_hWnd, 0, 0, PM_REMOVE ) ) {
            if ( WM_QUIT == m_msg.message ) {
                break;
            } else {
                TranslateMessage( &m_msg );
                DispatchMessage( &m_msg );
            }
        } else {
            processFrame();
        }
    }
}

If you would like to see more of this code then you will have to visit www.MarekKnows.com for his wonderfully well structured and clearly explained video tutorials for Game Development using OpenGL. For copy right reasons I will only give small snippets as suggestions to others that may help them with a specific case problem. I can not claim this as my own source code, however with following his course setup everything is personally hand typed and personally debugged. There is no copying and pasting! Almost everything I have learned about Game Programming is accredited to Marek's videos along with a few other sources.

I can try to explain this the best I can from what I have learned and following the code's execution. Somewhere in the program the Engine::start() function is invoked. The first thing that happens is we go into an infinite while loop; within this while loop we check to see if any messages from windows were peeked and if they are we check to see if the WM_QUIT message was set from some other place. If it was set we break out of the while loop and return from this function and the application will clean up memory, destroy window and close; otherwise we then call TranslateMessage() & DispatchMessage(). If the PeekMessage() returns false then we go into the else statement and call proccessFrame().

On my machine I am rending basic GUI objects at about 100-120 frames per second with this framework. I have an EVGA NVidia GeForce GTX 750Ti 2GB with overclocking and dual fan coolant running on Windows 7 64bit with 8GB Ram on an Intel Core 2 Quad Extreme 3.0Ghz.

We have not yet delved into extensive animations, physics, particles systems, collision detections as of yet. We did however cover all of that in one of his older video series were the older GameEngine was based on OpenGL v1.0 and everything was done on the CPU. With this series we are now working on using Shaders & the GPU. I am sure with this frame work we can have a full 2D or 3D game running on a system similar to mine at a full 40 - 60 FPS with ease.

Another great source for learning is to check out www.GeometricTools.com and check out the book section. A few years ago I purchased their book: 3D Game Engine Design (2nd Edition) by David H. Eberly, The Morgan Kaufmann Series in Interactive 3D Technology, and it is a great read. I have not yet had the chance to purchase their newest book: GPGPU Programming for Games and Science by David H. Eberly, but would soon like to own it since it is more relevant, current and up to date with modern hardware & software development. Good luck to you!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .