Sign in to follow this  
caleb_yau

pygame eating up my cpu!

Recommended Posts

caleb_yau    122
Here is what i would think would be the almost canonical pygame game loop:
import pygame
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((400, 480))
clock = pygame.time.Clock()

while True:
    time = clock.tick(60)
    pygame.event.pump()
    p = pygame.key.get_pressed()
    if p[K_ESCAPE]:
        sys.exit(0)
    paint_area = pygame.Rect(0, 0, 100, 100)
    screen.fill((255, 0, 0), paint_area)
    pygame.display.update(paint_area)    



Unfortunately this guy eats about 99% of my cpu at all times. If I can't fix the loop is there any way to program 2d games in python with good cpu efficiency? [Edited by - caleb_yau on December 20, 2008 12:09:13 PM]

Share this post


Link to post
Share on other sites
Antheus    2409
It could be that your computer has poor timer, and your requested resolution is higher than that.

For example, worst-case could be 18.2Hz, but you're requesting 60Hz. This could (although it shouldn't) lead to timer waiting for 0 ms to avoid overshooting by too much.

Another possibility is that your rendering is taking long due to lack of proper hardware optimization.

The tick() function actually operates on old and current time. If the body if your loop takes longer than 16.7ms, and if it accounts for the declared 10ms timer granularity of SDL_Delay, then it will only allow for 6.7ms for body of the loop, anything larger would result in no wait time, since tick() would determine it's late already.

Reduce the frame rate to 30 or even 15 and see if the problem persists. If that fixes the problem, then the SDL_Delay which is used internally by pygame isn't usable for this purpose.

Also, print out the result of tick() function. Documentation isn't clear, but it may return the time it actually waited.

Share this post


Link to post
Share on other sites
I believe SDL_Delay works by maxing out your CPU.

I know this because I was attempting to take a screen shot of an SDL application I had compiled from the sources and the screen shot was always of AFTER the app had ended. It was a simple draw one thing to the screen, delay for fives seconds, and close.

What I believe happened is my computer could not even register the print screen command while SDL_Delay was running.

If there's any kind of system sleep function, see if that does a better job.

This would be an example of where portability decides to bite you in the ass.

EDIT:
Take this with a grain of salt. I wasn't too scientific about the whole thing and it's been a while. Still, might want to check it out.

Share this post


Link to post
Share on other sites
Zaris    183
Looking at the code I think it made have something to do with the line:
paint_area = pygame.Rect(0, 0, 100, 100)

your creating a new rect for each frame and since your frame rate is 60. your creating 60 rects a second. It may not be the main issue, but you may want to try creating the rect outside of the loop and see how that effects cpu use.

Share this post


Link to post
Share on other sites
caleb_yau    122
Fixed! Splinter of Chaos' advice worked. Well first getticks did print its wait time, which was a steady 16 ms. I ended up using a combination of the time.sleep function from the time module and python.time.get_ticks(). So it ends up being a pretty os ambigous game still. Now python hardly shows up on top! The tick() solution is in like every pygame tutorial and many games on the pygame website suffer poorly probably because of it. I feel like I discovered some ultimate secret to python game programming!


import pygame
import time
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((400, 480))
clock = pygame.time.Clock()
old_time = pygame.time.get_ticks()

while True:
new_time = pygame.time.get_ticks()
waited = new_time - old_time
old_time = new_time
if waited < 60:
time.sleep(1.0 / (60 - waited))
print 'waited: ', waited
pygame.event.pump()
p = pygame.key.get_pressed()
if p[K_ESCAPE]:
sys.exit(0)
paint_area = pygame.Rect(0, 0, 100, 100)
screen.fill((255, 0, 0), paint_area)
pygame.display.update(paint_area)





Thanks alot!

Share this post


Link to post
Share on other sites
Oluseyi    2112
Quote:
Original post by caleb_yau
Here is what i would think would be the almost canonical pygame game loop...

Nope. I'm not a fan of artificially slowing down your application, though you can limit the number of display refreshes.


from pygame import *

running = True
fps = 60
DISPLAY_REFRESH = USEREVENT
time.set_timer(DISPLAY_REFRESH, int(1000.0/fps))

while running:
for evt in event.get():
if evt.type == KEYDOWN and evt.key == ESCAPE:
running = False
elif evt.type == DISPLAY_REFRESH:
paint_area = pygame.Rect(0, 0, 100, 100)
screen.fill((255, 0, 0), paint_area)
pygame.display.update(paint_area)

# do your non-rendering game loop computation here
# to reduce CPU usage, call this guy:
time.wait(0)



Quote:
Unfortunately this guy eats about 99% of my cpu at all times.

That doesn't necessarily mean anything. It's running flat out, so it's using its full allocated CPU timeslice. Giving up any remainder of the timeslice is the key to lowering CPU usage, which is done by sleeping - or, in the case of PyGame, calling time.wait(). We pass zero because the time specified is actually a lower bound - your process will sleep at least that many milliseconds. calling Sleep(0) (underlying system API) just releases the rest of your current timeslice.

Share this post


Link to post
Share on other sites
Oluseyi    2112
Quote:
Original post by caleb_yau
Whoa much better and cleaner Oluseyi. I like wait a lot already! Unfortunately that means that sleeping is everybody's secret ... :)

Yes. I also make heavy use of custom events in my PyGame programming, instead of manually juggling time intervals. Another advantage of this approach is that you can enter a function that takes over the event loop, allowing for different behavior - ESCAPE might exit that function/mode rather than the application. (Generally, avoid calling sys.exit(), as it really just raises an exception, SystemExit, that can spill onto the command line.)

Here's an example of the modal nested event loops approach. main() calls menu(), which establishes an event loop and then either exits or calls puzzle() depending on user input. puzzle() sets up its own event loop - note that they all use the same event queue, but take over polling it so that the effect of the same event can vary with game mode. This is easier than trying to shoehorn all that logic into a single loop and select based on game state. In effect, each of the functions menu(), puzzle(), etc is the game state.


def PostQuitEvent():
pygame.event.post(pygame.event.Event(pygame.QUIT))

def main():
screen = pygame.display.set_mode((800, 450))
menu(screen)

def menu(screen):
...

running = True
selected = 0
while running:
for evt in pygame.event.get():
if evt.type == pygame.QUIT:
running = False
elif evt.type == pygame.KEYDOWN:
if evt.key == pygame.K_ESCAPE:
PostQuitEvent()
elif evt.key == pygame.K_RETURN:
if selected == (NUM_MENU_OPTIONS - 1):
PostQuitEvent()
else:
puzzle(screen, 0)
elif evt.key == pygame.K_UP or evt.key == pygame.K_DOWN:
selected += 1
select %= NUM_MENU_OPTIONS
...

def puzzle(screen, p):
...
running = True
while running:
for evt in pygame.event.get():
if evt.type == pygame.QUIT:
running = False
elif evt.type == pygame.KEYDOWN:
if evt.key == pygame.K_ESCAPE:
PostQuitEvent()
...

if __name__ == '__main__':
main()


Share this post


Link to post
Share on other sites
caleb_yau    122
This is definetly going to clean up my game loop a lot, and probably speed it up a little too. What's this supposed to do though?:


if __name__ == '__main__':
main()

if im not mistaken __name__ will be the name of the module, and only will only be '__main__' when its run interactively. Won't this almost never run in that case?

Share this post


Link to post
Share on other sites
Oluseyi    2112
Quote:
Original post by caleb_yau
What's this supposed to do though?:
if __name__ == '__main__':
main()

if im not mistaken __name__ will be the name of the module, and only will only be '__main__' when its run interactively. Won't this almost never run in that case?

Not when it's run interactively. When the module is the executing module, as opposed to be loaded via import. (You can't run a file interactively, after all.) This little test is very useful: it allows you to implement unit tests for a module designed for import, for example, or to import a module written as an application and take advantage of its types in other code.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this