English 中文(简体)
Windows Game Loop 50% CPU on Dual Core
原标题:

The game loop alone is using 50% of CPU Usage, I haven t done any rendering work yet. What I m doing here?

        while(true)
        {
            if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
            {
                    if(msg.message == WM_QUIT || 
                           msg.message == WM_CLOSE || 
                           msg.message == WM_DESTROY)
                            break;

                    TranslateMessage(&msg);
                    DispatchMessage(&msg);                   
            }
            else
            {
                    //Run game code, break out of loop when the game is over

            }
        }
问题回答

Classic busy/wait loop. Your CPU is busily checking (and rechecking ad infinitum) for messages. You need to wait for messages in a blocking way or, more likely, use a timer that periodically wakes up your game thread so it can do its work. The game thread would then go away until the next time it is woken up.

You have created a busy-wait loop. You are probably using 100% of one core, thus 50% of a dual core.

You need to find a way to block on read (in a separate thread), block and fall out of an I/O call as necessary, or do something else useful in the thread. Each strategy has its advantages and disadvantages. Separate threads need synchronized communication methods such as mutexs. Falling out of I/O means nothing else useful happens in this thread when there are no messages. Doing something else in the loop can result in lumpy processing (the "something else" gets processed more on less messages. less on more messages).

That s a standard game loop for action games, where you must update objects positions / game world.
If you are making a board game GetMessage would be a better choice.
It really depends on what game you are making.

You need to give up the CPU when you have no messages to process and no game code to execute. One way to do this is to use MsgWaitForMultipleObjects to wait for a message to show up in your queue or a slice of time to expire.

Something like this

   DWORD g_msNextGameCall;
   DWORD g_msGameTickTime = 1000/75;

   while (true)
      {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
         {
         if (WM_QUIT == msg.message)
            break;

         TranslateMessage(&msg);
         DispatchMessage(&msg);  
         }
      else
         {
         DWORD ms = GetTickCount();
         DWORD msNext = g_msNextGameCall;
         LONG  lWait = 0;
         DWORD dwRet = WAIT_TIMEOUT;

         if (ms < msNext)
            lWait = min((LONG)g_msGameTickTime, (LONG)(msNext - ms));

         if (lWait <= 1)
            {
            g_msNextGameCall = ms + g_msGameTickTime;
            DoGameStuff();
            }
         else
            {
            if (WAIT_TIMEOUT == MsgWaitForMultipleObjects (0, NULL, FALSE, lWait, QS_ALLEVENTS))
               {
               g_msNextGameCall = GetTickCount() + g_msGameTickTime;
               DoGameStuff();
               }
            }
         }
      }

If you re making a very simple game and do all your rendering and calculation in the main loop you need to control how fast the while loop is running, or else the game will run at wildly different speeds on different processors. As a side effect of that you re also making sure the while loop isn t consuming any CPU doing nothing.

I think this behavior is expected. Whenever your game code does nothing, the application furiously checked the message queue using PeekMessage - it s a continuous loop so uses the whole of 1 core.

As you add logic to your else{...} block you will find it remains at 100% use on one core, but the time is spent doing your game logic - only the unused CPU cycles are used on the PeekMessage call, but right now 100% of the cycles are unused.

It is common for a game to max out the CPU when visible, if it s running full-screen. But you should probably consider using GetMessage instead of PeekMessage.

Note, games don t normally work quite the same as normal apps. Normal apps typically do nothing, unless they get a message telling them to do something. Games typically do stuff all the time, because they want to render as many frames/second as possible. Stealing all the CPU is a bit greedy in windowed mode though.

I had the same problem and got answer here: Game Loops

In my program i used last loop from the article above "Constant Game Speed with Maximum FPS"

This is because the PeekMessage function does not remove the WM_PAINT message, so that it will always return TRUE. MSDN says:

The PeekMessage function normally does not remove WM_PAINT messages from the queue. WM_PAINT messages remain in the queue until they are processed. However, if a WM_PAINT message has a NULL update region, PeekMessage does remove it from the queue.

In your else block, try adding this:

sleep(0);

That will cause your thread to yield the CPU, breaking the busy-wait loop. To do your actual game code, use a timer as tvanfosson suggests to wake up another game thread.

it doesnt look as a standard win32 app main loop ... that is similar to

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
 {
   MSG msg;
   while(GetMessage(&msg, NULL, 0, 0) > 0)
   {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
   }
   return msg.wParam;
 }

as you are inside the while (true) loop maybe even user events (mouse and keyboard) cant get dispatched in the message queue correctly

If your goal is to develop a Game app in win32 i suggest you to look at Directx

Your game is running the fast as possible in one core. This is normal depending of what you did.

I don t know if you want take MORE power (thus reach 100%) or LESS power (and use less of the poor user energy bill...)

If you want to take MORE power, you need to use threading somehow, to use both cores. Since I am not much into threading, I will not even attempt to explain, it is totally not my field.

If you want to use LESS power, there are also two options...

One is "yield" as some game library APIs call it (like Allegro), it consists of making a FPS counter, and giving back control to the cpu every frame for the suffcient time. Example, if your game wants to run at 120 FPS, and you want it to run at 60, you can give to him about the same amount of time that a frame is taking to calculate (about 8,3333... ms).

The other one, is use a event based way to code the game, instead of looping. In that form, you put your code inside a function named "Update" that accepts as argument the amount of time taken since last call (very important this...). This "Update" can be either for the entire app (more classical), or each object having its own (popular after invention of Flash, that use this, as "OnEnterFrame", also Unity use this, and some other engines). And you make a code that throw a interruption and call that Update, usually just a timer that run at a regular time (16.6.... ms for 60FPS, 33.33333... ms for 30FPS).

Obviously, there are a LOT of other ways too, but I will not explain them all, because that is sufficient info for a small book...

I ve used the various approaches myself, I like to just use the CPU full blast (without threading), and use more and more system abusive effects as power is made available. But for simpler games, I usually make a loop that "yields", the easiest way usually counting how much time took to calculate everything, subtract that from 16.6 ms, and call a sleep function (that give control to OS for that time) on the result... Like, if a frame took 3 ms to calculate, I call a sleep(16-3). And several engines force me to work with the "event style" (ie: input comes from keyboard, mouse and joystick interruptions, and a timer interrupts and calls "Update(step)" ), I dislike it, but I had to learn...

And a final note: The "step" var as I called (the Update argument), usually is used in the game logic maths, example: "position.x = position.x + speed*step" to make a object move at actual constant speed... (obviously, the operation used, depends on what "step" represent).

The question is old, but I believe my testimonial might help new readers.

I was facing the same problem here (50% of CPU on app idle) with a pure Win32 application using Delphi (without the VCL), running Win7 32-bit, Core Duo.

I tried Sleep(0), Sleep(1), etc, within the message loop, but none reduced the CPU usage to 0% (on app idle). Eventually, I succeeded by using the same Sleep(), no longer in every loop cycle, but only if PeekMessage() returns False. Now, I get 0% of CPU usage when application is idle.

In simplified code, that s what I m doing now:

AppIsDone := False;  // turned on after wm_Close (not shown below)

repeat
  if not PeekMessage(...) then
  begin
    Sleep(1);
    Continue;  {repeat}
  end;

  if GetMessage(...) then
  begin
    TranslateMessage(...);
    DispatchMessage(...);
  end;
until AppIsDone;




相关问题
How to read exact number of bytes from a stream (tcp) socket?

In winsock, both the sync recv and the async WSARecv complete as soon as there is data available in a stream socket, regardless of the size specified (which is only the upper limit). This means that ...

AcquireCredentialsHandle returns SEC_E_NO_CREDENTIALS

I created a self-signed certificate (created using OpenSSL) and installed it into the Certificate Store using the Certificates MMC snap-in (CertMgr.msc) on Windows Vista Ultimate. I have managed to ...

Calling Win32 EnumThreadWindows() in C#

I m trying to get a call to EnumThreadWindows working, but I always get a Wrong Parameter-Error, although my code is nearly the same as this example on pinvoke.net. I don t know why this doesn t work: ...

COM Basic links

folks can you provide me the tutorial link or .pdf for learning basic COM?. i do google it.. still i recommend answers of stackoverflow so please pass me.. Thanks

Handling multiple windows WIN32 API

HI I m trying to create an application in the Win32 environment containing more than one window. How do i do that? all the Win32 Tutorials on web i found only showed how to manage one window. How do i ...

Creating a thread in DllMain?

It seems that when a thread is created from within DllMain upon DLL_PROCESS_ATTACH it won t begin until all dll s have been loaded. Since I need to make sure the thread runs before I continue, I get a ...

热门标签