Windows Game Loop 50% dual-core processor

Only in the game loop, 50% of the CPU is used, I have not yet done the rendering. What am I 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 } } 
+4
source share
12 answers

Classic busy / wait cycle. Your processor carefully checks (and rereads infinity) for messages. You need to wait for messages in blocking mode or, more likely, use a timer that periodically wakes up the game stream so that it can do its job. The game thread will then disappear until the next time it wakes up.

+8
source

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

You need to find a way to block reading (in a separate thread), block and exit the I / O call as needed, or do something else useful in the thread. Each strategy has its advantages and disadvantages. Individual threads need synchronous communication methods, such as mutexes. Exiting I / O means that nothing useful happens in this thread when there are no messages. Doing something else in the loop can lead to lumpy processing ("something else" is processed more on fewer messages. Less on more messages).

+5
source

This is the standard game loop for action games where you must update the position of the objects / game world.
If you are making the GetMessage board game, this is the best choice.
It really depends on what kind of game you are doing.

+3
source

If you make a very simple game and do all your rendering and calculations in the main loop, you need to control how fast the while loop works, otherwise the game will work with completely different speeds on different processors. As a side effect, you also make sure that the while loop does not consume the processor without doing anything.

+1
source

I think this behavior is expected. Whenever your game code does nothing, the application vehemently checked the message queue using PeekMessage - it is a continuous loop, therefore it uses all 1 core.

When you add logic to your else{...} block, you will find that it remains 100% on one core, but time was spent executing your game logic. The PeekMessage call uses only unused CPU cycles, but now 100% of the cycles are not used.

Typically, a game maximizes the processor when visible if it is running in full screen mode. But you should probably use GetMessage instead of PeekMessage .

Note. Games usually do not work just like regular applications. Regular applications usually do nothing unless they receive a message telling them to do something. Games usually do things all the time because they want to display as many frames / second as possible. Stealing the entire processor is a bit greedy in windowed mode.

+1
source

You need to abandon the processor 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 appear in your queue or to wait before the deadline.

Something like that

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

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

In my program, I used the last cycle from the article above "Constant game speed with maximum FPS"

+1
source

This is because the PeekMessage function does not delete the WM_PAINT message, so it always returns TRUE. MSDN says:

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

+1
source

In your else block, try adding this:

 sleep(0); 

This will cause your thread to issue a CPU, interrupting the busy-wait cycle. Use your timer to make your actual game code, as tvanfosson offers to wake up another game.

0
source

it does not look like a standard win32 application loop ... which is similar

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

since you are inside the while (true) loop, it is possible that even user events (mouse and keyboard) cannot be sent to the message queue correctly

If your goal is to develop an application for playing win32, I suggest you look at DirectX

0
source

Your game runs as fast as possible in one core. This is normal depending on what you have done.

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

If you want to take more energy, you need to use threads somehow to use both cores. Since I am not very versed in threads, I will not even try to explain, this is not my area.

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

One of them is the "exit", since some game library APIs call it (for example, Allegro), it consists of creating an FPS counter and providing processor control for every frame in sufficient time. For example, if your game wants to work at a speed of 120 FPS, and you want it to run at 60, you can give it about the same time as the frame takes to calculate (about 83,333 ... ms).

Another uses an event-based method to encode a game, not a loop. In this form, you put your code in a function called "Update", which takes as argument the amount of time spent since the last call (this is very important ...). This "update" can be either for the entire application (more classical), or for each object that has its own (popular after the invention of Flash, which uses it as "OnEnterFrame", Unity also uses this and some other engines). And you make code that throws an interrupt and causes this update, usually it's just a timer that starts at normal times (16.6 .... ms for 60FPS, 33.33333 ... ms for 30FPS).

Obviously, there are many other ways, but I will not explain them, because this is sufficient information for a small book ...

I used various approaches on my own, I like to just use the full CPU stream (without streaming) and use more and more offensive effects of the system as power becomes available. But for simpler games, I usually make a loop that "gives", the easiest way, as a rule, to calculate how much time it took to calculate everything, subtract from 16.6 ms and call the sleep function (which gives control over the OS for this time) for the result ... For example, if the frame took 3 ms to calculate, I cause sleep (16-3). And several engines make me work with the "event style" (i.e., the input comes from keyboard, mouse and joystick interrupts, and the timer interrupts and calls "Update (step)"), I don’t like it, but I had to learn .. .

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

0
source

The question is old, but I believe that my review can help new readers.

I encountered the same problem here (50% CPU on an idle application) with a clean Win32 application using Delphi (without VCL) working with Win7 32-bit, Core Duo.

I tried Sleep (0), Sleep (1), etc. in the message loop, but none of them reduced the CPU usage to 0% (during application downtime). In the end, I managed to use the same Sleep (), no longer in every cycle of the cycle, but only if PeekMessage () returns False. Now I get 0% of CPU usage when the application is down.

In simplified code, this is what I am 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; 
0
source

Source: https://habr.com/ru/post/1302863/


All Articles