Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vsync fails to work while SDL window is covered by another opaque window in Mac OS X #1871

Closed
SDLBugzilla opened this issue Feb 11, 2021 · 3 comments
Labels
abandoned Bug has been abandoned for various reasons

Comments

@SDLBugzilla
Copy link
Collaborator

SDLBugzilla commented Feb 11, 2021

This bug report was migrated from our old Bugzilla tracker.

These attachments are available in the static archive:

Reported in version: HG 2.0
Reported for operating system, platform: Mac OS X (All), x86_64

Comments on the original bug report:

On 2015-06-02 10:16:59 +0000, Joshua Bodine wrote:

Created attachment 2169
code that demonstrates issue

When a non-fullscreen window with an OpenGL context (and SDL_GL_SetSwapInterval(1) enabled) is completely covered by another application's opaque window, the framerate is no longer restricted to vsync and the framerate increases dramatically, consuming a lot of CPU. Once any portion of the SDL window is visible again, vsync works properly again. Obstructing the SDL window only partially, or obstructing it entirely with a translucent window, does not cause the framerate to increase.

The attached source file demonstrates this issue.

My operating system is Mac OS X 10.10.3. This behavior was observed in SDL 2.0 development code in mercurial as of today's date.

On 2015-06-02 17:27:18 +0000, Alex Szpakowski wrote:

I believe this is completely out of SDL's control – it's just how the modern OS X window manager works, even when the app requests vsync.

It's also worth mentioning that (on any operating system) vsync requests are just requests. The OS gets the final say in when to actually use it, and users often have the option to completely disable it system-wide (even in OS X.)

It is possible to get notified when such an occlusion event happens, however SDL doesn't currently make use of that to post its own event (SDL_WINDOWEVENT_HIDDEN or similar.) It would probably be useful to add that: https://bugzilla.libsdl.org/show_bug.cgi?id=2656

On 2015-10-03 01:59:31 +0000, Alex Szpakowski wrote:

Mac OS 10.11 seems to have fixed this behavior (on my system at least), for what it's worth.

On 2017-07-08 17:16:47 +0000, Joshua Bodine wrote:

I just checked back in here on this bug status since I am still experiencing it in our program on macOS 10.12.5 and SDL 2.0.5. We have a workaround in place to stop the program from eating 100% CPU when the window is obscured, but without the workaround the problem still exists.

As for the claim that this is standard macOS behavior, I haven't tried this recently, but when I wrote an experimental pure Cocoa application to test this same behavior, the framerate remained consistent when the window was obscured. I therefore believe that it is indeed possible, although I don't know what it would take to replicate it in SDL.

On 2017-07-14 02:58:05 +0000, Alex Szpakowski wrote:

How did your Cocoa app's render loop execute? From my understanding, a lot of Cocoa apps use a CVDisplayLink (or a higher level API which wraps that) in order to get a callback that executes before every display refresh event, and then use that callback to render. That would allow for steady framerates no matter the vsync setting requested in the OpenGL context.

That sort of thing is not feasible to implement in SDL's mac backend code because SDL exposes a polling and push-frame API, rather than a callback-based one.

On 2017-09-01 05:42:58 +0000, Joshua Bodine wrote:

It was a very basic Cocoa app. I created a window with an OpenGL View in the interface editor (or whatever it's called now), put something in the main loop (I think) to set the view to always need redrawing, and then I put some framerate tracking/print out code in the method that was called when the app tried to re-draw the view. It was some time ago, so some of the details escape me, but I do remember that the framerate remained constant when the window was obscured.

The more I think about it, the more I think that using vsync to throttle your entire application is probably not a great idea on our end. Nonetheless, it seems to me that having your rendering function lose vsync when your application's window is obscured would be unexpected behavior.

On 2017-09-01 17:30:03 +0000, Sam Lantinga wrote:

For what it's worth, I tried reproducing this on Mac OS X 10.12.6 and wasn't able to. It seems this may have been fixed in later OS versions.

On 2017-09-01 21:20:46 +0000, Alex Szpakowski wrote:

(In reply to Joshua Bodine from comment # 5)

It was a very basic Cocoa app. I created a window with an OpenGL View in the
interface editor (or whatever it's called now), put something in the main
loop (I think) to set the view to always need redrawing, and then I put some
framerate tracking/print out code in the method that was called when the app
tried to re-draw the view. It was some time ago, so some of the details
escape me, but I do remember that the framerate remained constant when the
window was obscured.

I believe that's a similar situation to what I described above - the Cocoa run loop fires off draw events at a rate slower than a busy loop (possibly tied to the monitor's native refresh rate), even when the OpenGL context doesn't have vsync enabled.

SDL's APIs cannot hook into and expose this behaviour because SDL's external APIs are polling, rather than callback-based.

On 2018-08-06 21:20:23 +0000, Ryan C. Gordon wrote:

Hello, and sorry if you're getting dozens of copies of this message by email.

We are closing out bugs that appear to be abandoned in some form. This can happen for lots of reasons: we couldn't reproduce it, conversation faded out, the bug was noted as fixed in a comment but we forgot to mark it resolved, the report is good but the fix is impractical, we fixed it a long time ago without realizing there was an associated report, etc.

Individually, any of these bugs might have a better resolution (such as WONTFIX or WORKSFORME or INVALID) but we've added a new resolution of ABANDONED to make this easily searchable and make it clear that it's not necessarily unreasonable to revive a given bug report.

So if this bug is still a going concern and you feel it should still be open: please feel free to reopen it! But unless you respond, we'd like to consider these bugs closed, as many of them are several years old and overwhelming our ability to prioritize recent issues.

(please note that hundred of bug reports were sorted through here, so we apologize for any human error. Just reopen the bug in that case!)

Thanks,
--ryan.

@SDLBugzilla SDLBugzilla added abandoned Bug has been abandoned for various reasons bug labels Feb 11, 2021
@bmwalters
Copy link

bmwalters commented Apr 6, 2021

I am able to reproduce this issue on macOS 11.2.3. Running the test application gives:

./main
FPS: 35
FPS: 61
FPS: 61
FPS: 60
FPS: 61
FPS: 61
FPS: 275
FPS: 657
FPS: 656
FPS: 650
FPS: 652
^C⏎  

It looks like CVDisplayLink support was introduced in 13869f1 and later backed out in 04b50f6 due to #3218.

I am currently working around this with an if (macos && !has_keyboard_focus) SDL_Delay(16) in my application loop. However I think this is a "gotcha" which would be nice to fix in SDL itself.

Are the issues when using CVDisplayLink inherent or can it be fixed? I couldn't quite get all the context from #3218. @icculus

@bmwalters
Copy link

bmwalters commented Apr 6, 2021

Looks like GLFW has seen both this issue and the "broken vsync on a specific mojave patch" issue and resolved them using CVDisplayLink. glfw/glfw#680

They then also removed CVDisplayLink glfw/glfw@54e8e0b

and instead committed a workaround glfw/glfw@c3ca880 which usleeps for up to 16ms when the window is occluded 🙃

@icculus
Copy link
Collaborator

icculus commented Apr 6, 2021

Are the issues when using CVDisplayLink inherent or can it be fixed?

It caused problems with multi-monitor setups and just is an enormous amount of code that felt very fragile when it usually just needs a single Cocoa call to say "the OS should handle this for us."

github-merge-queue bot pushed a commit to bevyengine/bevy that referenced this issue Aug 2, 2023
…s not visible (#7611)

Fixes #5856. Fixes #8080. Fixes #9040.

# Objective

We need to limit the update rate of games whose windows are not visible
(minimized or completely occluded). Compositors typically ignore the
VSync settings of windows that aren't visible. That, combined with the
lack of rendering work, results in a scenario where an app becomes
completely CPU-bound and starts updating without limit.

There are currently three update modes.
- `Continuous` updates an app as often as possible.
- `Reactive` updates when new window or device events have appeared, a
timer expires, or a redraw is requested.
- `ReactiveLowPower` is the same as `Reactive` except it ignores device
events (e.g. general mouse movement).

The problem is that the default "game" settings are set to `Contiuous`
even when no windows are visible.

### More Context

- libsdl-org/SDL#1871
- glfw/glfw#680
- godotengine/godot#19741
- godotengine/godot#64708

## Solution

Change the default "unfocused" `UpdateMode` for games to
`ReactiveLowPower` just like desktop apps. This way, even when the
window is occluded, the app still updates at a sensible rate or if
something about the window changes. I chose 20Hz arbitrarily.
Subserial pushed a commit to Subserial/bevy_winit_hook that referenced this issue Jan 24, 2024
…s not visible (#7611)

Fixes #5856. Fixes #8080. Fixes #9040.

# Objective

We need to limit the update rate of games whose windows are not visible
(minimized or completely occluded). Compositors typically ignore the
VSync settings of windows that aren't visible. That, combined with the
lack of rendering work, results in a scenario where an app becomes
completely CPU-bound and starts updating without limit.

There are currently three update modes.
- `Continuous` updates an app as often as possible.
- `Reactive` updates when new window or device events have appeared, a
timer expires, or a redraw is requested.
- `ReactiveLowPower` is the same as `Reactive` except it ignores device
events (e.g. general mouse movement).

The problem is that the default "game" settings are set to `Contiuous`
even when no windows are visible.

### More Context

- libsdl-org/SDL#1871
- glfw/glfw#680
- godotengine/godot#19741
- godotengine/godot#64708

## Solution

Change the default "unfocused" `UpdateMode` for games to
`ReactiveLowPower` just like desktop apps. This way, even when the
window is occluded, the app still updates at a sensible rate or if
something about the window changes. I chose 20Hz arbitrarily.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
abandoned Bug has been abandoned for various reasons
Projects
None yet
Development

No branches or pull requests

3 participants