I am stuck on a screen tearing problem for about a week now.
Here is my problem: I would like to make a program that can display a sequence of PNG pictures really fast (at a rate of 30 fps or more). To do this I use the pygame library, especially pygame.display.blit and pygame.display.flip.
Here is an example of the code (with a homemade delay function):
import time
import pygame
screen=pygame.display.set_mode(size, pygame.FULLSCREEN)
nbPicturesToLoad=12
pictures=range(nbPicturesToLoad)
for i in range(nbPicturesToLoad):
pictures[i]=pygame.image.load(pictureName).convert() //Here pictureName depends on the value of i
(...)
for i in range(nbPicturesToLoad):
timer1=time.time()
screen.blit(pictures[i], (0,0))
pygame.display.flip()
timer2=time.time()
timer=timer2-timer1
while(timer<0.03333):
timer2=time.time()
timer=timer2-timer1
The timer allows me to have the same refresh rate on the screen.
My problem is that one can see with their own eyes that there is a screen tearing problem (https://en.wikipedia.org/wiki/Screen_tearing).
On the pygame documentation they recommend to use other flags for the screen init (pygame.HWSURFACE and pygame.DOUBLEBUF, http://www.pygame.org/docs/ref/display, y.set_mode). Apparently these could help to correct the screen tearing problems. But when I use these flags I see no screen tearing correction.
I've seen somewhere that pygame can't handle vsync on X11, which would explain why I see no difference with the HWSURFACE and DOUBLEBUF flags.
Has anyone ever encountered this vsync issue on the Raspberry Pi? Is there a parameter to put in the "config.txt" file?
Does anyone have a way to get real vsync information to avoid these screen tearing effects?
I've seen that pyglet could handle vsync in the created windows, but for unknown reasons I can't display images in the pyglet window and I'm really not sure if it would fix the problem, especially if a Raspberry can't handle vsync right now.
Try using the Pygame clock to handle timing, it is much easier to implement and understand and it may solve the issue.
shown below:
import pygame as py
py.init()
WINDOW = (400,400)
screen = py.display.set_mode(WINDOW)
nbPicturesToLoad=12
pictures = range(nbPicturesToLoad)
for i in range(nbPicturesToLoad):
pictures[i]=py.image.load(pictureName).convert()
FPS = 30
clock = py.time.Clock() # Create a clock object
done = False
while not done:
for event in py.event.get():
if event.type == py.QUIT:
done = True
for i in range(nbPicturesToLoad):
screen.fill((255,255,255))
screen.blit(pictures[i],(0,0))
py.display.flip()
clock.tick(FPS) # use pygame.time.Clock to wait 1/30th of a second
py.quit()
However, since this is a screen tearing problem the solution may be to stop using Pygame altogether. How to avoid tearing with pygame on Linux/X11
This question seems to cover what you may be looking for.
Good luck.
Related
When the pygame window closes, I want to immediately stop execution; that's the end of the program. I have looked up and down the internet and every problem I come across is something like "Did you try pygame.quit()" or "You never break out of your infinite loop". That isn't my problem. Here's my code:
#!/usr/bin/env python3
import pygame
from sys import exit as sysExit
pygame.init()
screen = pygame.display.set_mode((512,512))
screen.fill((255,255,255))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.update()
pygame.display.quit()
pygame.quit()
exit(0)
sysExit(0)
#None of these work, code under them is unreachable but the process hangs
As the comment and question title say, this process hangs. I honestly don't what else I could do to tell python I'm done, and I've tried every combination of these quit functions but nothing works. The window will close, but the program never ends. In my searching I've found people have issues with something called "IDLE" which I do not have.
Here's what I do have (my system): Linux Mint Cinnamon 18, 32-bit (i686), using python version 3.5.2 (no IDE, just running from terminal)
EDIT - Apparently there's some confusion about what I'm talking about when I say the program "hangs", so here's some screenshots of the issue in action. This first one shows you that the above code is, in fact, usable to reproduce the problem.
Now all I do is run this script, and it just gives me a blank window like you'd expect.
Now I click the "x" button on the window, and the window disappears, but my terminal is left like this. The screen capture won't show you the blinking cursor, but you'll notice there's no new prompt.
I've so far been resorting to doing this to get the thing to stop:
throw it in the background, force kill the job (SIGTERM gets ignored btw) and foreground it again and now I have a prompt.
check pygame.init() -> (numpass, numfails), then loading each module manually (yes, each module has an init() and quit() method). I would suspect the mixed or music module to be the problem (due to os/driver/hardware issues).
The pygame irc channel helped me figure this out. Specifically, this answer comes from user DR0ID_ who walked me through some debugging I should've thought of in the first place.
First of all, once I'm done with this answer I'll edit the question, but I have to apologize for leaving something out initially: I'm doing all this on a Virtualbox VM. By walking through the different modules initialized by pygame.init(), I discovered that the one that actually hangs it is pygame.mixer. No word yet from the pygame devs, but the working theory is that this is related to Virtualbox sound drivers and/or their interface with the host drivers. Basically, if you're on a virtual machine and don't need to play sounds, the workaround is to only initialize the modules you need. Since in my example I only need pygame.display, the solution is easy:
#!/usr/bin/env python3
import pygame
pygame.display.init()
screen = pygame.display.set_mode((512, 512))
screen.fill((255, 255, 255))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.quit()
exit()
I am just on my first steps using pygame on python 3.5 on mac osx 10.11.1. I think i have properly installed pygame because when i run
import pygame
it accepts it. I am running some tests on basic pygame use and i have a problem with pygame.display.set_mode
This is the code:
import pygame
pygame.init()
screen = pygame.display
screen.set_mode((720,480))
it runs ok without any bugs but the pygame screen that opens (different from the IDLE screen) it freezes. The cursor becomes this spinning rainbow thing.
Excuse me if this is a really dumb question but i'm really new at this and i have been searching all day and can't find something similar.
Thank you for your time
What you need to write is an event loop and a quit event, as the user cannot close the window.
For the event loop I would do something like:
running = True
while running: # This would start the event loop
for event in pygame.event.get():
if event.type == pygame.QUIT: # This would be a quit event.
running = False # So the user can close the program
screen.fill(0,0,0) # This fills the screen with black colour.
pygame.display.flip() # This "flips" the display so that it shows something
pygame.quit()
I hope this helps! The event loop is just something that keeps the program running, without it the code would look quite confusing so it's best to have one.
I recently startet getting into pyglet and rabbyt from pygame, but I have hit something of a brick wall.
I created a basic example where one Sprite (of the type found in pyglet.sprite.Sprite) is displayed at 60 frames per second. The problem is that this simple program is using up 50% of the CPU time somehow. I repeated the experiment with the sprite type found in the rabbyt library with the same result.
I decided to render 1000, then 10 000 sprites at 60 frames per second, and to my surprise the CPU usage stays at 50%. The only thing is that moving or animating a sprite results in slight stuttering.
Lastly, I tried running at 360 frames per second. Same result, 50% usage.
Here is the sample code:
import pyglet
import rabbyt
def on_draw(dt):
window.clear()
spr.render()
global window
window = pyglet.window.Window(800, 600)
spr = rabbyt.Sprite('ship.png')
spr.x = 100
spr.y = 100
pyglet.clock.schedule_interval(on_draw, 1.0/60.0)
if __name__ == '__main__':
pyglet.app.run()
I am using a Core 2 Duo with an ATI HD 3500 card.
Any advice/ideas are appreciated.
Be aware that the default pyglet event handler will fire an 'on_draw' event every time it clears the event queue.
http://www.pyglet.org/doc/programming_guide/the_application_event_loop.html
The pyglet application event loop dispatches window events (such as for mouse and keyboard input) as they occur and dispatches the on_draw event to each window after every iteration through the loop.
This means that any event can trigger a redraw.
So if you're moving the mouse about or doing anything that fires events, you will get massive slow down as it begins to trigger render calls.
This also caused problems because I was doing my own render calls, so I would get the two buffers fighting which created a 'ghost' effect on the screen. Took me a while to realise this was the cause.
I monkey patched the event loop to not do this.
https://github.com/adamlwgriffiths/PyGLy/blob/master/pygly/monkey_patch.py
Be aware that this patched event loop will no longer render on it's own, you must manually flip the buffers or trigger an 'on_draw' event.
It might be the case that, although you've hooked in at 60fps, but the internal render loop is ticking at the maximum possible rate.
I dislike code that takes away control, hence my patch lets me decide when render events occur.
Hmm.. You might want to know the fps at which the game runs, if it helps:
cldis = pyglet.clock.ClockDisplay()
Then add this to your on_draw function:
cldis.draw()
it draws the current fps at the bottomleft corner of the screen in a semi-transparent color.
I know that in Pygame there is the built-in called "Clock". You can put a limit on how many times the game loops per second using the tick method. In my example I have put a limit of 30 FPS. This prevents your CPU being on constant demand.
clock = pygame.time.Clock()
While 1:
clock.tick(30) # Puts a limit of 30 frames per second on the loop
In pyglet there appears to be something similar:
pyglet.clock.schedule_interval(on_draw, 1.0/60.0)
clock.set_fps_limit(60)
Hope that helps!
edit: documentation on fps limit: http://pyglet.org/doc/api/pyglet.clock-module.html#set_fps_limit
Is there anyway to use pygame to get input from the console, rather than having to display a separate window to get input? I'm using pygame to track how long the keys on the keyboard are pressed.
The following code doesn't work (this is just a minimal example, it doesn't actually keep track of time elapsed):
pygame.init()
while 1:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
print event.key, 'pressed'
It doesn't look like any pygame event is being raised. If I add
screen = pygame.display.set_mode((640, 480))
After pygame.init()
then the event is raised, but I have that ghastly window I don't want to deal with.
To explain why I don't want the window, I envision this application being a command-line utility, so I can't have that. Is there any functional reason preventing pygame from running on the command-line?
Thanks!
EDIT: I speculated that the problem was pygame.init(), and that I only needed to initialize the key and event modules. According to http://www.pygame.org/docs/tut/ImportInit.html I should have been able to call pygame.key.init()pygame.event.init() but it didn't work.
Pygame is designed for making (graphical) games, so it only captures key presses when there is a window displayed. As Ignacio said in his answer, reading from the command line and from another window are very different things.
If you want to create a command line application, try curses:
http://docs.python.org/library/curses.html
Unfortunately, it only works on Linux and Mac OS X.
Console input comes in via stdin, which pygame is not prepared to handle. There is no reliable way to get press/release events via stdin since it's dependent on the terminal sending it keypresses.
If you just make the window really small using
screen = pygame.display.set_mode((1, 1))
you can't see it. So you are clicked into the window but you don't notice.
If you click anywhere of course it stops working. You have to click on the pygame window icon for it to work again.
If you just plain don't want a window of any kind at all, you can use PyHook. If you just want a console application, get user input with the built-in Python command "raw_input(...)".
Try pygame.display.iconify().
This will hide the pygame screen, and you will still be able to detect keypresses.
I'm currently working on a game engine written in pygame and I wanted to add OpenGL support.
I wrote a test to see how to make pygame and OpenGL work together, and when it's running in windowed mode, it runs between 150 and 200 fps. When I run it full screen (all I did was add the FULLSCREEN flag when I set up the window), it drops down to 60 fps. I added a lot more drawing functions to see if it was just a huge performance drop, but it always ran at 60 fps.
Is there something extra I need to do to tell OpenGL that it's running fullscreen or is this a limitation of OpenGL?
(I am running in Windows XP)
As frou pointed out, this would be due to Pygame waiting for the vertical retrace when you update the screen by calling display.flip(). As the Pygame display documentation notes, if you set the display mode using the HWSURFACE or the DOUBLEBUF flags, display.flip() will wait for the vertical retrace before swapping buffers.
To be honest, I don't see any good reason (aside from benchmarking) to try to achieve a frame rate that's faster than the screen's refresh rate. You (and the people playing your game) won't be able to notice any difference in speed or performance, since the display can only draw 60 fps anyways. Plus, if you don't sync with the vertical retrace, there's a good chance that you'll get screen tearing.
Is this a V-Sync issue? Something about the config or your environment may be limiting maximum frame rate to your monitor's refresh rate.
If you are not changing your clock.tick() when you change between full screen and windowed mode this is almost certainly a vsync issue. If you are on an LCD then it's 100% certain.
Unfortunately v-sync can be handled in many places including SDL, Pyopengl, your display server and your video drivers. If you are using windows you can adjust the vsync toggle in the nvidia control panel to test, and there's more than likely something in nvidia-settings for linux as well. I'd guess other manufacturers drivers have similar settings but that's a guess.