Windows游戏循环双核50%CPU

时间:2010-03-02 13:03:17

标签: winapi message-queue game-loop

仅游戏循环使用50%的CPU使用率,我还没有完成任何渲染工作。我在这做什么?

        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

            }
        }

12 个答案:

答案 0 :(得分:8)

经典忙/等待循环。您的CPU忙于检查(并无限制地重新检查)消息。您需要以阻止方式等待消息,或者更有可能使用定期唤醒游戏线程的计时器,以便它可以完成其工作。游戏线程将在下一次被唤醒之前消失。

答案 1 :(得分:5)

您已创建忙等待循环。您可能正在使用100%的一个核心,因此50%是双核心。

您需要找到一种方法来阻止读取(在单独的线程中),根据需要阻止和退出I / O调用,或者在线程中执行其他有用的操作。每种策略都有其优点和缺点。单独的线程需要同步的通信方法,如互斥锁。掉出I / O意味着当没有消息时,此线程中没有其他任何有用的东西。在循环中执行其他操作可能会导致块状处理(“其他内容”会在较少的消息上处理得更多。对更多消息的处理更少。)

答案 2 :(得分:3)

这是动作游戏的标准游戏循环,您必须更新对象位置/游戏世界。
如果您正在制作棋盘游戏GetMessage将是更好的选择 这真的取决于你正在制作的游戏。

答案 3 :(得分:1)

如果您正在制作一个非常简单的游戏并在主循环中完成所有渲染和计算,则需要控制while循环的运行速度,否则游戏将在不同的处理器上以极快的速度运行。作为一个副作用,你还要确保while循环不消耗任何CPU什么也不做。

答案 4 :(得分:1)

我认为这种行为是可以预期的。每当你的游戏代码什么都不做的时候,应用程序就会使用PeekMessage疯狂地检查消息队列 - 它是一个连续的循环所以使用整个1核心。

当您向else{...}块添加逻辑时,您会发现它在一个核心上仍然100%使用,但是花费时间来完成游戏逻辑 - 只有未使用的CPU周期用于{{1调用,但现在100%的周期都没有使用。

如果游戏全屏运行,游戏通常会在可见时最大化CPU。但您应该考虑使用PeekMessage而不是GetMessage

注意,游戏通常与普通应用程序完全不同。普通应用程序通常什么都不做,除非他们收到消息告诉他们做某事。游戏通常会一直做东西,因为他们希望尽可能多地渲染帧​​数。在窗口模式下,窃取所有 CPU有点贪心。

答案 5 :(得分:1)

当您没有要处理的消息且没有要执行的游戏代码时,您需要放弃CPU。 一种方法是使用MsgWaitForMultipleObjects等待消息显示在队列中一段时间到期。

像这样的东西

   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();
               }
            }
         }
      }

答案 6 :(得分:1)

我有同样的问题,并在这里得到答案: Game Loops

在我的程序中,我使用了上面文章“恒定游戏速度和最大FPS”的最后一个循环

答案 7 :(得分:1)

这是因为PeekMessage函数不会删除WM_PAINT消息,因此它将始终返回TRUE。 MSDN说:

  

PeekMessage函数通常不会从队列中删除WM_PAINT消息。 WM_PAINT消息在处理之前保留在队列中。但是,如果WM_PAINT消息具有NULL更新区域,则PeekMessage会将其从队列中删除。

答案 8 :(得分:0)

在你的else块中,尝试添加:

sleep(0);

这会导致你的线程产生CPU,打破忙等待循环。要做你真正的游戏代码,请使用计时器,因为tvanfosson建议唤醒另一个游戏线程。

答案 9 :(得分:0)

它看起来不像标准的win32 app主循环...... 这类似于

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;
 }

因为你在while(true)循环中,甚至用户事件(鼠标和键盘)也无法正确地在消息队列中调度

如果您的目标是在win32中开发游戏应用程序,我建议您查看Directx

答案 10 :(得分:0)

您的游戏在一个核心中尽可能快地运行。这是正常的,取决于你做了什么。

我不知道你是否想要更多的功率(因此达到100%)或更少的功率(并且使用较少的用户能源费用......)

如果你想获得更多功能,你需要以某种方式使用线程,以使用两个核心。由于我没有太多的线程,我甚至不会尝试解释,这完全不是我的领域。

如果你想使用LESS电源,还有两种选择......

一个是“收益”,因为一些游戏库API称之为(像Allegro),它包括制作FPS计数器,并在足够的时间内每帧都给cpu控制权。例如,如果您的游戏想要以120 FPS运行,并且您希望它以60 F的速度运行,您可以向他提供与计算帧相同的时间(约8,3333 ... ms)。

另一个是使用基于事件的方式对游戏进行编码,而不是循环。在该表单中,您将代码放在名为“Update”的函数中,该函数接受自上次调用以来所花费的时间量作为参数(非常重要的是......)。这个“更新”可以是整个应用程序(更经典),或者每个对象都有自己的(Flash发明之后流行,使用它,如“OnEnterFrame”,Unity也使用它,以及其他一些引擎)。然后你创建一个代码来中断并调用Update,通常只是一个定时运行的定时器(60FPS为16.6 ...... ms,30FPS为33.33333 ...... ms)。

显然,还有很多其他方法,但我不会全部解释,因为这对于一本小书来说已经足够了......

我自己使用了各种方法,我喜欢使用CPU全爆炸(没有线程),并且在可用电源时使用越来越多的系统滥用效果。但是对于更简单的游戏,我通常会创建一个“收益”的循环,这是通常计算计算所有内容所花费的时间的最简单方法,从16.6 ms减去该值,并调用睡眠函数(在此时控制OS)结果......就像,如果帧需要3毫秒来计算,我称之为睡眠(16-3)。几个引擎迫使我使用“事件样式”(即:输入来自键盘,鼠标和操纵杆中断,计时器中断并调用“更新(步骤)”),我不喜欢它,但我必须学习。 ..

最后一点:我调用的“step”var(Update参数),通常用于游戏逻辑数学,例如:“position.x = position.x + speed * step”来制作一个对象以实际恒定速度移动......(显然,所使用的操作取决于“步骤”代表什么)。

答案 11 :(得分:0)

问题是陈旧的,但我相信我的推荐可能会帮助新读者。

我在使用Delphi(没有VCL)的纯Win32应用程序,运行Win7 32位Core Duo时遇到了同样的问题(50%的应用程序空闲CPU)。

我在消息循环中尝试了Sleep(0),Sleep(1)等,但没有一个将CPU使用率降低到0%(在应用程序空闲时)。最终,我成功地使用相同的Sleep(),不再在每个循环周期中,但只有在PeekMessage()返回False时。 现在,当应用程序空闲时,我获得0%的CPU使用率。

在简化代码中,这就是我现在正在做的事情:

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;