可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
}
}
回答1:
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.
回答2:
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).
回答3:
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.
回答4:
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.
回答5:
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.
回答6:
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();
}
}
}
}
回答7:
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"
回答8:
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.
回答9:
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.
回答10:
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
回答11:
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).
回答12:
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;