Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Drawing to the screen uses too much CPU

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Drawing to the screen uses too much CPU
kdevil
Member #1,075
March 2001
avatar

I have this tiny little program:

1#include <allegro.h>
2 
3volatile int counter;
4void update_counter()
5{
6 counter++;
7}
8END_OF_FUNCTION(update_counter)
9 
10int main()
11{
12 allegro_init();
13 install_keyboard();
14 install_timer();
15 LOCK_VARIABLE(counter);
16 LOCK_FUNCTION(update_counter);
17 
18 set_color_depth(16); //same problem when using 24 and 32
19 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0)
20 {
21 allegro_message("Couldn't set up a screen mode!");
22 return 1;
23 }
24 
25 install_int_ex(update_counter, BPS_TO_TIMER(50));
26 counter = 0;
27 
28 while (!key[KEY_ESC])
29 {
30 while (counter > 0)
31 {
32 clear_to_color(screen, makecol(0, 0, 0));
33 //or rectfill(screen, 0, 0, 640, 480, makecol(0, 0, 0));
34 //or create a black bitmap and blit it to the screen
35 counter--;
36 }
37 rest(1);
38 }
39 
40 allegro_exit();
41 return 0;
42}

My problem is that this takes up 100% of my CPU time. Can anyone figure out what's going wrong? It seems that anything that draws to the scren takes way too much processor time.

-----
"I am the Black Mage! I casts the spells that makes the peoples fall down!"

gillius
Member #119
April 2000

Unless you relinqusish CPU time to the operating system, with software drawing you will always use 100% on any machine, regardless of its speed. All you will do is spin your loop faster.

You need a framerate reguluator that either sleeps when it is going too fast, or use something that blocks, like vsync (does Allegro's vsync do true blocking on Windows?), to limit your framerate and reduce your CPU use below 100%.

I see you are using rest method, but maybe it is not working? 1ms may be too small of a sleeping time.

EDIT: I guess I assumed you were working with Windows. What OS are you using, and is rest implemented properly there?

Gillius
Gillius's Programming -- https://gillius.org/

Rampage
Member #3,035
December 2002
avatar

It might not help you, but this is what my game cycle looks like:

1 BITMAP *bmp = create_bitmap(SCREEN_W, SCREEN_H);
2 
3 while (!key[KEY_ESC])
4 {
5 while(counter <= 0) {
6 rest(1);
7 }
8 while (counter > 0)
9 {
10 // do some logic
11 counter--;
12 }
13 clear(bmp);
14 draw_graphics(bmp);
15 blit(bmp, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
16 }
17 
18 destroy_bitmap(bmp);

Last night this code allowed me to watch DVD's while testing my framework.

-R

kdevil
Member #1,075
March 2001
avatar

Can someone try the code I posted and see if they get the same result?

-----
"I am the Black Mage! I casts the spells that makes the peoples fall down!"

Tomoso
Member #3,128
January 2003
avatar

Just wondering, why are you using rest(1) ?
My game loop is also like yours rampage just without the rest bit :P
Sorry, off your topic but I'm just curious.

Lazy Noob - Blog

kdevil
Member #1,075
March 2001
avatar

I use rest(1) so that the game takes as little CPU time as possible. Anyway, has anyone tried my code and seen whether they have the same problem?

Edit: Come to think of it, all the allegro examples I've tried are taking 100% CPU as well. I'm going to try recompiling Allegro...

Edit #2: Huh. Trying exaccel gives me this:

Quote:

Clear: software (urgh, this is not good!)
Blit: software (urgh, this program will run too sloooooowly without hardware acceleration!)

Could that be what's wrong?

-----
"I am the Black Mage! I casts the spells that makes the peoples fall down!"

Elverion
Member #6,239
September 2005
avatar

You are using clear_to_color(screen, makecol(0, 0, 0)); within your logic loop. Move it outside of that loop and it should be just fine.

--
SolarStrike Software - MicroMacro home - Automation software.

Billybob
Member #3,136
January 2003

As was said, your game will always use 100% CPU unless it's event driven. It's a fact. Sleeping for 1ms won't help at all because sleep(1) ends up sleeping for 16ms, giving you a terrible framerate. This is due to timer innaccuracy.
It's not a bad thing unless your game is to be used in parallel with another program.

kdevil
Member #1,075
March 2001
avatar

Ah, right.

Still, all OpenLayer games I've tried on the laptop barely go above 30% CPU, so it's a bit troubling when a plain Allegro game takes up 100%. Is there a more accurate sleep function or something else that'll help me lower the CPU usage?

-----
"I am the Black Mage! I casts the spells that makes the peoples fall down!"

Billybob
Member #3,136
January 2003

Remove the counter:

  while (!key[KEY_ESC])
  {
      clear_to_color(screen, makecol(0, 0, 0));
      //or rectfill(screen, 0, 0, 640, 480, makecol(0, 0, 0));
      //or create a black bitmap and blit it to the screen
    rest(1);
  }

If your drawing is fast enough (~1/60th of a millisecond) you'll run at 60fps. The slower your drawing the slower your framerate, but this should keep CPU usage pretty low. (if it doesn't work, try sleep instead of rest).

rest(1) will sleep for ~16ms (timer innaccuracy), so if you can draw really fast it will ...
loop:
draw: 0.1ms
rest: 16ms
in 1 second that's roughly 60 frames.

---

vsync may reduce CPU usage. If it doesn't use any CPU while waiting on the screen, then you'll see a drop in CPU usage. That may be what's going on with OpenLayer (if it's vsync-ing automatically).

---

By the way, you can still use a counter if you want, but only to keep track of time. I wouldn't use it to control FPS in the this case, because sleep is going to mess it up.

Richard Phipps
Member #1,632
November 2001
avatar

Try different amounts of rest, i.e. rest(2) rest(3) and see what effect this has on your CPU usage.

Billybob
Member #3,136
January 2003

Richard: It should have none. As I said, the timer resolution is roughly 16ms. So anything < 16ms is going to end up near 16ms.

As a side note, timer resolution can be changed on most operating systems, but it is done manually, doesn't usually get below 1ms, and obviously you can't count on it being so (nor ask users to do it).

Goalie Ca
Member #2,579
July 2002
avatar

Quote:

rest(1) will sleep for ~16ms (timer innaccuracy), so if you can draw really fast it will ...

DO NOT RELY ON OS SCHEDULING FOR TIMING. Your program can be put in at any time basically.

-------------
Bah weep granah weep nini bong!

James Stanley
Member #7,275
May 2006
avatar

rest(0) waits until the operating system says it can go, AFAIK.

The reason all the examples use 100% is because they are effectively on a continuous loop. Write a continuous loop without allegro, and it will still use 100% unless you let the operating system know you're willing to let it go and do it's thing, usually be rest(0)-ing.

Elverion
Member #6,239
September 2005
avatar

rest(0) will still use 100% of the CPU, but it tells the OS to let other processes have a turn, if needed. So, rest(0) basically just uses up whatever extra CPU time it can. Rest(1) will actually decrease CPU usage, but is inaccurate and may have adverse effects on your program. Games are very time critical, so I wouldn't worry about about how much CPU usage it shows that you are using, but rather performance.

--
SolarStrike Software - MicroMacro home - Automation software.

HoHo
Member #4,534
April 2004
avatar

Quote:

Games are very time critical, so I wouldn't worry about about how much CPU usage it shows that you are using, but rather performance.

unless you are runnign on batteries ;)

__________
In theory, there is no difference between theory and practice. But, in practice, there is - Jan L.A. van de Snepscheut
MMORPG's...Many Men Online Role Playing Girls - Radagar
"Is Java REALLY slower? Does STL really bloat your exes? Find out with your friendly host, HoHo, and his benchmarking machine!" - Jakub Wasilewski

gillius
Member #119
April 2000

Yes, this is critically important if you are running on batteries, or with variable cooling systems (esp when using those massive wattage Pentium D processors). On my work box (thankfully I don't play games or peg the CPU a lot), when something takes 100% CPU it goes from dead silent to a flippin' jet that needs FAA clearance to take off. Of course it's a small form-factor Dell machine.

Even on my AMD machine, the same happens, except no where near as loud. So there's no reason to burn CPU if you're just going to spin and can't display any more frames.

On a laptop you can cut your battery life in half or more by burning up the CPU like that in a loop.

If you are using proper triple buffering with waits or another similar system, the OS will block for you if your game runs too fast, so you can use a "standard" limiting/timing system but still get the blocking advantage, without any framerate stutter since you render 2 frames ahead you get that buffer.

Gillius
Gillius's Programming -- https://gillius.org/

Billybob
Member #3,136
January 2003

gillius is correct, triple buffering and other methods that use show_video_bitmap do indeed reduce CPU usage. show_video_bitmap locks the current thread until vsync (unlike vysnc() itself, mind you). Which means that if your program can draw faster than the refresh rate, your CPU usage will go under 100%.

Note, though, that this only works on systems with triple buffering, and only in fullscreen.

I have developed a better solution. It is by no means an end all, but it will work in both fullscreen and windowed.

1#include "pthread.h"
2#include <allegro.h>
3 
4void *timer(void *ptr)
5{
6 // DO NOT sleep for less than 16ms
7 // Most OS timers are not accurate under 16ms
8 sleep(16);
9 // You may change the sleep length if you want a different FPS < 60
10 // sleep(1000/FPS);
11 // DO NOT use any FPS > 60
12}
13 
14void wait_for_timeslice(pthread_t *thread)
15{
16 pthread_join(*thread, NULL);
17 pthread_create(thread, NULL, timer, NULL);
18}
19 
20int main()
21{
22 allegro_init();
23 install_keyboard();
24 install_timer();
25 
26 set_color_depth(16); //same problem when using 24 and 32
27 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0)
28 {
29 allegro_message("Couldn't set up a screen mode!");
30 return 1;
31 }
32
33 pthread_t thread;
34 pthread_create(&thread, NULL, timer, NULL);
35 while(!key[KEY_ESC])
36 {
37 clear_to_color(screen, makecol(255, 255, 255));
38 line(screen, 0, 0, rand() % SCREEN_W, rand() % SCREEN_H, makecol(0,0,0));
39
40 wait_for_timeslice(&thread);
41 }
42 
43 allegro_exit();
44 exit(0);
45}
46END_OF_MAIN()

It sets up a thread that runs in the background for 16ms. After drawing is done it does "pthread_join" on the sleeper thread. This will lock the main thread until the sleeper thread is done sleeping, during which time no CPU is used. After that it recreates the sleeper thread so it can be used again for the next frame.

This method will draw at most 62.5 frames per second. If your program can run faster than that the CPU usage will drop while continuing at 60FPS.
In the case where the host machine is too slow, your program will run at under 60FPS and 100% CPU. This is an unfortunate case, but what do you expect when the host machine is slow?

I tested the above code on my machine and got 10% CPU usage (0 without the line call). What do others get? (You'll need pthreads to compile) Attached is an EXE in case you can't compile, as well as the pthreads DLL (for windows).

CGamesPlay
Member #2,559
July 2002
avatar

Quote:

// Most OS timers are not accurate under 16ms

Ugh, both Windows and Linux have 10 Hz timer frequencies by default. It's 10 ms, not 16 :P

kdevil: Yes, the problem is probably that you are using the GDI driver. I don't know why that would be, but I suggest starting from scratch with Allegro, and make sure you have the proper DirectX framework (from Allegro's SF page) installed, and DirectX installed, and graphics drivers and all that. If that doesn't help, try posting your specs.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

gillius
Member #119
April 2000

I don't think there is a cause to use pthreads for this. I developed a timer that would sleep appropriately and would have different (automatic) modes of operation, including frameskip modes. So it would go (slower than real-time with max frameskip) -> (real-time with some frameskip) -> (real-time with sleeps when needed).

The way I did it was to keep a game time counter and a real time counter and to do different actions based on where the game time was in relation to the real time -- sleep or skip frames, or slow the logic down (skip logic frames).

The original thread is at http://www.allegro.cc/forums/thread/305773, but I think I attached the code and it is only on my machine now. I can post it if people want. The original thread has much much more detail on what I did.

After I wrote the code, I realized that you could do something simpler by just sleeping if your gametime was ahead. This is fine if you don't care about the game running ahead about 8ms on average.

Gillius
Gillius's Programming -- https://gillius.org/

Billybob
Member #3,136
January 2003

Quote:

Ugh, both Windows and Linux have 10 Hz timer frequencies by default. It's 10 ms, not 16 :P

Did you mean 100Hz?
And no matter what timer it may be using I've always seen timer function have an accuracy of 16ms, not 10ms.

CGamesPlay
Member #2,559
July 2002
avatar

Quote:

Did you mean 100Hz?
And no matter what timer it may be using I've always seen timer function have an accuracy of 16ms, not 10ms.

Yes :P
I suggest you run more tests... We've had several threads on this topic, agreeing on the 100 Hz timer...

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Jakub Wasilewski
Member #3,653
June 2003
avatar

Quote:

I suggest you run more tests... We've had several threads on this topic, agreeing on the 100 Hz timer...

I'm going to bet my left sock that William is using 60hz as his refresh rate, and the vsync caps his timer rate (100/60 = 16.666...).

Also, under Windows it is possible to change the resolution of the scheduler - see timeBeginPeriod() and timeEndPeriod(). IIRC, they're not guaranteed to work, but they're worth trying if you really can't stand sleeping for 10ms instead of 1ms. And I'm quite certain that the default under W2K and WXP is 10.

---------------------------
[ ChristmasHack! | My games ] :::: One CSS to style them all, One Javascript to script them, / One HTML to bring them all and in the browser bind them / In the Land of Fantasy where Standards mean something.

CGamesPlay
Member #2,559
July 2002
avatar

Quote:

I'm going to bet my left sock that William is using 60hz as his refresh rate, and the vsync caps his timer rate (100/60 = 16.666...).

Yeah, this was my thought also. I didn't say anything because I hope he wasn't using a graphical program to test that.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

Also, under Windows it is possible to change the resolution of the scheduler - see timeBeginPeriod() and timeEndPeriod().

That's rather dangerous, as it can have adverse affects on the rest of the system (especially older systems) and you'll be in for problems if your program dies without calling timeEndPeriod. Under Linux, the timer is configurable (250hz, 100hz, 1000hz, IIRC), so you should not assume any kind of granularity when resting. If you rest(0), it will get back to you ASAP after checking other threads. If you rest more, it will get back to you ASAP after the time is over. That's all you can gaurantee.

Quote:

// DO NOT sleep for less than 16ms
// Most OS timers are not accurate under 16ms

Why shouldn't you sleep for less than 16ms, even if that's the granularity you get? What's the difference if you rest for 1ms and get back in 16ms, or rest for 16ms and get back in 16ms? In fact, trying to match it like that can cause you more problems if it checks your process just ahead of schedule, sees that you want to sleep a bit more, then goes another round without you.

Your granularity is determined b y a number of factors, not the least of which is how much CPU time the other processes take. Even with a 10ms timeslice, if you rest for 1ms, and after 5ms, no process wants to use the CPU, the OS will see your thread and wake you up (thereby causing you to sleep for 5ms, instead of 10 or 16).

There is another issue, though. Allegro's timers themselves are very innaccurate, so you shouldn't use them to time your logic if you can help it. Every single running timer is run on the same thread, so after resting and being woken up to run your callback (incurring the 5~10ms delay), it'll check all other timers to see if they want to run. And if one of them happens to want to, you'll get a longer delay.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

 1   2 


Go to: