Workaround for bug 4822 - Broken visual output in full screen mode with OS X 10.15
authorSam Lantinga <slouken@libsdl.org>
Tue, 11 Feb 2020 10:21:31 -0800
changeset 1350725c5f567de44
parent 13506 d5109d6a9b98
child 13508 a6d3d330dedc
Workaround for bug 4822 - Broken visual output in full screen mode with OS X 10.15

sjordan

We did some investigations into a different direction which I would like to share. As mentioned previously the scaling setting in the preferences play an important role for our problem and they also hint towards an issue with point/pixel scaling factors.

We found an interesting correlation between our fail case and the behavior of [nsWindow.screen backingScaleFactor]. It turns out that whenever we encounter the fail case the scale factor is zero when we print it quickly after calling SDL_CreateWindow. After some time the value changes to a non-zero value. In the success case the scaling factor is nonzero 'immediately'. Note that we don't use that factor. We also find that the window backingScaleFactor does not show the strange behavior even in the fail case.

We have also attempted to find out whether any event triggers the transition from zero to non-zero. We found the transition happening when we call SDL_PollEvent. We can even force this to happen by explicitly adding a SDL_PollEvent at an early stage, but it will only happen if a certain amount of time elapsed, so we need to add some sleep before the call to trigger the transition at an earlier stage. All that seems to imply that the transition happens async and that SDL_PollEvent merely causes the system to update its internal state at that time.

We have also verified that the scaling setting in the preferences does NOT directly correlate to the scaling factor behavior. We find that a particular scaling setting can lead to a fail case for one resolution and a success case for another resolution. This shows that the scaling setting alone does not determine whether the problem will appear or not.

We have also verified on another Mac with 10.14 that the scaling factor is always non-zero and we always have the success case.

I have no idea how to interpret this initial-zero behavior and haven't found any usable information on the screen backing scale factor. It seems as 10.15 does some stuff more async than before and maybe the problem could be caused by unfortunate timings. I would be very interested to hear your opinion about that.

...

Finally we found the cause of all our problems: it's the origin hack in Cocoa_SetWindowFullscreen:

/* Hack to fix origin on Mac OS X 10.4 */
NSRect screenRect = [[nswindow screen] frame];
if (screenRect.size.height >= 1.0f) {
rect.origin.y += (screenRect.size.height - rect.size.height);
}

If we comment this one out our game and testdraw2 do behave correctly.

It turns out that if a window is not fully contained in the screen, it's screen property becomes zero and therefore we saw a zero when printing the backing scale factor (although it's not clear why it became nonzero later).

We suggest to add a runtime check which skips this code for 10.15 (or possibly earlier if you happen to know that the hack is not needed for certain older versions).

More info: consider the line

NSRect screenRect = [[nswindow screen] frame];

in Cocoa_SetWindowFullscreen. We found that this rect has the dimensions of the desktop
on our OS X 10.15 setup. This is true both for the success case and the fail case. It seems as the success case is actually a fail case in disguise.

On the other Mac with OS X 10.14 the same rect has the dimension of the newly created screen. This is what I would expect, because at that time the window has already been created successfully and there should be a newly created screen associated to the window.

What are the cases in which the whole origin conversion code for the fullscreen case is supposed to have a non-trivial result?

Today we found that if we print the dimensions of [nswindow screen] later, then we find them to be correct. So the conclusion seems to be that OS X 10.15 does indeed do the window/screen setup more async than before and that the origin correction code uses the [nswindow screen] at a time where the window/screen setup isn't finalized yet.
src/video/cocoa/SDL_cocoawindow.m
     1.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Tue Feb 11 10:08:22 2020 -0800
     1.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Tue Feb 11 10:21:31 2020 -0800
     1.3 @@ -1793,10 +1793,15 @@
     1.4          rect.size.height = bounds.h;
     1.5          ConvertNSRect([nswindow screen], fullscreen, &rect);
     1.6  
     1.7 -        /* Hack to fix origin on Mac OS X 10.4 */
     1.8 -        NSRect screenRect = [[nswindow screen] frame];
     1.9 -        if (screenRect.size.height >= 1.0f) {
    1.10 -            rect.origin.y += (screenRect.size.height - rect.size.height);
    1.11 +        /* Hack to fix origin on Mac OS X 10.4
    1.12 +           This is no longer needed as of Mac OS X 10.15, according to bug 4822.
    1.13 +         */
    1.14 +        NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
    1.15 +        if ((version.majorVersion == 10) && (version.minorVersion < 15)) {
    1.16 +            NSRect screenRect = [[nswindow screen] frame];
    1.17 +            if (screenRect.size.height >= 1.0f) {
    1.18 +                rect.origin.y += (screenRect.size.height - rect.size.height);
    1.19 +            }
    1.20          }
    1.21  
    1.22          [nswindow setStyleMask:NSWindowStyleMaskBorderless];