4

SDL2: vsync document and test existing behaviour · Issue #735 · pygame/pygame ·...

 2 years ago
source link: https://github.com/pygame/pygame/issues/735
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Copy link

Member

illume commented Jan 14, 2019

edited by MyreMylar

At the moment pygame+sdl2 vsync defaults to off, but we should change it to default to on to be backwards compatible with pygame+sdl1.

https://wiki.libsdl.org/SDL_HINT_RENDER_VSYNC

Also, we should perhaps expose a display.set_mode(vsync=True) option.

Related Docs: https://www.pygame.org/docs/ref/display.html#pygame.display.set_mode

Copy link

Contributor

husano896 commented Jan 15, 2019

How will vsync flag work in pygame+sdl1?

Copy link

Member

Author

illume commented Jan 15, 2019

Good question. Maybe it will just do the current behavior, and it will be ignored. I'm not sure if SDL supports vsync in a way where it can be enabled or not.

Copy link

Contributor

husano896 commented Jan 15, 2019

Previously in SDL1, OpenGL contexts are vsync'd by SDL_GL_SWAP_CONTROL

and surfaces are vsync'd by HWSURFACE | DOUBLEBUF + clock.tick().

But windowed surfaces can only use clock.tick(), which may cause screen jitters.

Probably related? #304

Copy link

Member

dlon commented Feb 5, 2019

The hint has no effect for me. Perhaps it only works for SDL_Renderer.

Copy link

Member

Author

illume commented Oct 3, 2019

Talking about this some more, I agree it should probably be left "as is" for pygame 2. Since this is a new feature, it should be moved to pygame 2.1+ (if we do it at all).

illume

removed this from the 2.0 milestone

Oct 3, 2019

illume

added this to the 2.1 milestone

Oct 3, 2019

Copy link

Contributor

MyreMylar commented Oct 19, 2019

My question; is there a way to enable vsynch at all in Pygame 2?

The pygame 1.9 method is no longer available.

Copy link

Member

Author

illume commented Oct 19, 2019

Sorry, which method was that?

Copy link

Contributor

MyreMylar commented Oct 20, 2019

In Pygame 1 you can enable v-sync (on windows) by:

  1. Setting: os.environ['SDL_VIDEODRIVER'] = 'directx' before you call pygame.init()
  2. Setting these flags: pygame.FULLSCREEN | pygame.DOUBLEBUF when you call pygame.display.set_mode()
  3. Don't restrict the frame rate with clock.tick(FPS).

On my computer this locks the frame rate to 60 FPS and if you scroll sprites or surfaces around they will move smoothly with no hitching. As far as I know this is the only way to enable v-sync/smooth graphic movement in pygame 1 so it's not ideal (doesn't work in windowed mode, no idea what the situation is on mac or linux) but it's better than nothing.

Step 1 is the one we can no longer do in pygame 2 as 'directx' is no longer an option for the SDL_VIDEODRIVER environment variable.

I got this method from issue #304 talking about the pygame clock. I'm not sure if the clock can be made any better, discussion elsewhere seems to indicate the hitching that we get without v-sync is likely to be an SDL wide issue (see: https://forums.libsdl.org/viewtopic.php?p=51727). However, to me, having an option to enable v-sync in some manner in pygame 2 seems a necessary feature.

In short; we have v-sync available in pygame 1 (albeit in a slightly crappy manner, but its there on the biggest platform for games) so we should have it in pygame 2, or if not that some other method (not sure there actually is one) that lets us have properly smooth scrolling graphics.

Copy link

Contributor

MyreMylar commented Oct 20, 2019

Perhaps the best way to add it would be just by adding an extra flag 'VSYNC' to display.set_mode(), much like the 'SCALED' one that was already added? I mean people are already going to have to deal with that difference between pygame 1 and pygame 2 when making cross pygame version applications.

I'm currently dealing with version specific differences with code like:
if int(pygame.__version__.split('.')[0]) == 1

But the best way to reduce people needing/wanting pygame 1 compatibility code is to make sure pygame 2 has all the capabilities of pygame 1 + more. And that includes some kind of v sync.

Copy link

Contributor

mcpalmer1980 commented Oct 20, 2019

I never noticed any hitching on Windows or Linux and I don't use v-sync(can't in Linux Mate for some reason). I use the clock.tick(30) on all my games and base movement on that framerate. Some might say that 30fps is too low, but if 24 is enough for film, than 30 is enough for a solo indie platformer. The first version of Flyboy ran at 18.2 fps on DOS because it was the only reliable clock available to me without a liscence for a DOS extender, since matching the 60Hz v-sync was unattainable on 16-bit processor.

That said, I see no reason why v-sync should not be an option in 2.0, even if the request cannot be honored by the host OS / window manager.

illume

removed this from the 2.1 milestone

Oct 26, 2019

illume

added this to the 2.0 milestone

Oct 26, 2019

Copy link

Member

Author

illume commented Oct 26, 2019

edited
  • SDL_HINT_RENDER_VSYNC is apparently 1 by default. I wonder if using SCALED flag pygame.set_mode(..., flags=SCALED) turns vsync on with MacOS? NO
  • PYGAME_VSYNC=1 python3 turns VSYNC on with Mac. Does it work with windows?
  • SDL_RENDERER_PRESENTVSYNC https://wiki.libsdl.org/SDL_RendererFlags

Copy link

Contributor

robertpfeiffer commented Oct 29, 2019

I don't think vsync should default to on, and we'd need an API to get the vsync FPS to make it useful.

Copy link

Contributor

exla1 commented Mar 8, 2020

SDL_HINT_RENDER_VSYNC is apparently 1 by default. I wonder if using SCALED flag pygame.set_mode(..., flags=SCALED) turns vsync on with MacOS? NO

I've been playing with SCALED and the new sdl2 stuff on mac os and the "NO" you said at the end confuses me because, on my end, using SCALED does turn on VSYNC by default.

window = pygame.display.set_mode((960, 540), pygame.SCALED) gives me a metal renderer by default (great!) with vsync.
This is on mac os 10.15.3 with Python 3.8.2 and Pygame 2.0.0.dev6.

The hint has no effect for me. Perhaps it only works for SDL_Renderer.

Same for me. Since it's on by default, I can't disable it. Changing the default renderer driver using SDL_HINT_RENDER_DRIVER also doesn't work (but it does using the new Renderer class).

On mac os, renderer or not, I always have smooth movements and I can't have a higher FPS than my monitor refresh rate. It dosen't seem to be the case on other platforms.
I tried it on Linux (Raspberry Pi with Raspbian) and I get the opposite effect: I can't turn vsync on with SCALED or the new Renderers. There is also a lot of hitching.

Copy link

Contributor

robertpfeiffer commented Mar 9, 2020

Oh no. I worked on this and didn't update here. I talked to some SDL devs and reported this in the Discord. I should write it here too:

VSync cannot be made to work reliably cross-platform with SDL2

  1. This is entirely dependent on your driver configuration
  2. There is no way to talk to your driver directly with SDL2
  3. You cannot query the driver to find whether VSync is actually supported or currently enabled
  4. This goes for both OpenGL (pygame.OPENGL) and SDL_Renderer (pygame.SCALED)

On Linux, I need to enable VSync support for my NVidia drivers in the xorg.conf. If it's disabled, SDL_Renderer will happily request VSync and set the VSync flag. There is no way to query whether VSync is actually happening. SDL_Renderer just knows VSync is a thing the driver supports, and might prefer Metal to OpenGL if the OpenGL on mac is known to SDL2 not to support VSync.

This is a one-way street. It's only possible to query whether there's an API available to set the VSync flags. The SDL dev told me to just set the flag to be sure.

It gets worse. It looks like VSync on MacOS defaults to blocking double buffering. One frame is rendered, the second frame is rendered, and pygame.display.flip() will block until the buffers are swapped and the first frame is actually displayed on the screen. This means the third call to pygame.display.flip() (invoking either SDL_RenderPresent or SDL_GL_SwapWindow) will block until the first frame has been displayed. This means that on a 60FPS monitor, you can use VSync to actually limit your frame rate to 60 FPS - on a Mac.

It depends entirely on your drivers. The driver could use VSync or GSync internally, SDL2 supports that, and the driver could use triple buffering. In the triple buffering case, there are two back buffers, so a pygame program would happily churn out frames into the both back buffers at 1000 FPS without ever blocking. VSync doesn't limit the rate of your game loop, but it prevents tearing. That's why the SDL2 people said it can't hurt to request VSync. If it doesn't prevent tearing for some reason, the user will just disable it in the driver.

But at the same time, there is no way to ensure pygame.display.flip() is always non-bocking. If your game logic is on the same thread as your rendering - and we're still working with the CPython GIL - then we have a huge problem. You cannot rely on VSync as a substitute for clock.tick() but you cannot rely on anything else either. It might prevent tearing.

I'll keep you updated. Maybe in the future there will be an API to query this. But for now, in order to ensure the old behaviour cross-platform, VSync MUST default to off.

I could put a test case that requests VSync in in the examples directory, but that cannot to my knowledge be automated. You have to run it on your machine, and eyeball if you see any tearing on the screen. That can be easily detected if you just draw a pattern of vertical moving lines. Framerate limiting can be detected programmatically, but even that is finnicky. Maybe the test case runs too slow, and I don't think it can do anything sensible in a headless test environment without a dedicated graphics card.

I propose to close this as WONTFIX or similar, especially for PyGame 2.0. Even if we can enable VSync by default, which we can, we can't guarantee it will do the right thing, and it might harm backward compatibility with old PyGame 1.X games.

Copy link

Contributor

MyreMylar commented May 16, 2020

edited

sounds like the main things left to do here are:

  • Test the methods of setting VSYNC in windows from this comment on windows.
  • document the behaviour of VSYNC being a request with no guarantees.

MyreMylar

changed the title SDL2: vsync default to on

SDL2: vsync document and test existing behaviour

May 16, 2020

Copy link

Contributor

MyreMylar commented May 17, 2020

Looks like in pygame 2.0.0.dev8 with SDL 2.0.12 adding:

os.environ['PYGAME_VSYNC'] = "1"

before calling display.set_mode() and adding pygame.SCALED to the set_mode flags will turn on vsync on windows. Seems to do so in windowed and fullscreen modes. Nothing else seems to have any effect.

Copy link

Contributor

MyreMylar commented May 17, 2020

I added the vsync enabling method above that seems to work on both mac and windows to the docs in the linked pull request.

Copy link

Member

Author

illume commented May 30, 2020

There's a vsync keyword now... thanks to @robertpfeiffer and @MyreMylar.

I'd like to continue on with this stuff after the next release:

  • investigate using vsync in 'software' mode.
  • display.set_mode vsync tests

Copy link

Contributor

Farbfetzen commented Nov 8, 2020

Hello, I'd like to make a request for enabling vsync without the SCALED flag. Would this be possible? Because my problem with the SCALED flag is that I have no control over the size of the window. Or if there is no way of using vsync without SCALED, make it possible to set the size of the resulting scaled window myself?

Copy link

Contributor

robertpfeiffer commented Nov 8, 2020

@BastiHz
For technical reasons and because of the nature of vsync, vsync only works when using the GPU. In PyGame, this means using OpenGL with pygame.OPENGL, using the SDL2 renderer (with OpenGL, DirectX, or Metal backends) via pygame._sdl2, or using pygame.SCALED, which uses the SDL2 renderer internally to render the display surface.

It would be possible to create another mode for pygame.display.set_mode that always uses the GPU, but doesn't do any automatic scaling, but for backwards compatibility reasons, we can't use a GPU by default.

Copy link

Contributor

Farbfetzen commented Nov 8, 2020

Ok, thank you.

It would be possible to create another mode for pygame.display.set_mode that always uses the GPU, but doesn't do any automatic scaling

That would be awesome. Maybe in a future version where backwards compatibility is not an issue anymore.

illume

removed this from the 2.0 milestone

Aug 21, 2021

illume

added this to the 2.1 milestone

Aug 21, 2021

ankith26

removed this from the 2.1 milestone

Nov 14, 2021

ankith26

added this to the 2.2 milestone

Nov 14, 2021


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK