Scaled resolutions on retina screens on macOS


#1

A few months ago I started getting reports of our game (BZFlag) failing to launch on certain mac computers. When I investigated it, I found that our code kept trying to create a fullscreen window at a certain resolution, but another resolution would be returned instead, so it was stuck in an endless cycle. As I dug deeper, what I discovered is that under certain conditions (retina display, with a non-standard scaling option under System Preferences -> Displays), SDL_GetCurrentDisplayMode() returns a resolution that isn’t actually supported by the display. What follows is that we cannot pass that resolution to SDL_CreateWindow() and get what we asked for.

I wrote a test application that queries SDL for the current display mode and every other possible display mode using SDL_GetNumDisplayModes() and SDL_GetDisplayMode(), and attempts to create a fullscreen window with SDL_CreateWindow() using each resolution. I then ran the test application on an Apple MacBook Pro retina 13" at all four scaling settings, with the following results:

skye:Desktop josh$ "Larger Text" skye:Desktop josh$ ./a.out Current Resolution 1024 x 640 FAIL, got 1024 x 768 Resolution 2560 x 1600 PASS Resolution 2048 x 1280 PASS Resolution 1650 x 1050 PASS Resolution 1440 x 900 PASS Resolution 1280 x 800 PASS Resolution 1152 x 720 PASS Resolution 1024 x 768 PASS Resolution 840 x 524 PASS Resolution 800 x 600 PASS Resolution 640 x 480 PASS skye:Desktop josh$ "Default" skye:Desktop josh$ ./a.out Current Resolution 1280 x 800 PASS Resolution 2560 x 1600 PASS Resolution 2048 x 1280 PASS Resolution 1650 x 1050 PASS Resolution 1440 x 900 PASS Resolution 1280 x 800 PASS Resolution 1152 x 720 PASS Resolution 1024 x 768 PASS Resolution 840 x 524 PASS Resolution 800 x 600 PASS Resolution 640 x 480 PASS skye:Desktop josh$ "In Between" skye:Desktop josh$ ./a.out Current Resolution 1440 x 900 PASS Resolution 2560 x 1600 PASS Resolution 2048 x 1280 PASS Resolution 1650 x 1050 PASS Resolution 1440 x 900 PASS Resolution 1280 x 800 PASS Resolution 1152 x 720 PASS Resolution 1024 x 768 PASS Resolution 840 x 524 PASS Resolution 800 x 600 PASS Resolution 640 x 480 PASS skye:Desktop josh$ "More Space" skye:Desktop josh$ ./a.out Current Resolution 1680 x 1050 FAIL, got 2048 x 1280 Resolution 2560 x 1600 PASS Resolution 2048 x 1280 PASS Resolution 1650 x 1050 PASS Resolution 1440 x 900 PASS Resolution 1280 x 800 PASS Resolution 1152 x 720 PASS Resolution 1024 x 768 PASS Resolution 840 x 524 PASS Resolution 800 x 600 PASS Resolution 640 x 480 PASS

So it seems that what is being returned by SDL_GetCurrentDisplayMode() under certain scaling modes is not a resolution that the display can actually be put into. I poked around in SDL’s code for this function, and as far as I can tell it pulls this information straight from the operating system.

So my question is, is this the expected behavior for SDL_GetCurrentDisplayMode()? Is there a better way to get the true resolution currently in use when a retina display is in one of those scaling modes? Or if this is not the expected behavior, is the problem in SDL or more likely in the macOS operating system code? For reference, I demonstrated this issue on macOS Sierra 10.12.5 and SDL 2.0.5.


#2

Hi,
There might be some helpful info in MacBook Pro with Retina display - strange behaviour

Specifically, it’s not clear that passing the fullscreen resolution to SDL_CreateWindow() is correct - the docs at https://wiki.libsdl.org/SDL_CreateWindow specifically state that the width/height params are ignored if you call it with SDL_WINDOW_FULLSCREEN (even though they are not ignored in practice).

It might be worth seeing if you can reproduce the problem by using SDL_SetCurrentDisplayMode instead of SDL_CreateWindow() .

I have a retina display Mac as well so would be interested in looking at this, mind posting the code for your test program?


#3

Sure, the source code is below. I compiled it using g++ --std=c++11 -F /Library/Frameworks -framework SDL2 displaytest.cpp

I couldn’t find a function SDL_SetCurrentDisplayMode(). Did you mean I should use SDL_SetWindowDisplayMode() after creating the window some other way?

displaytest.cpp (1.8 KB)


#4

Thanks, I’ll try it on my system (15" rMBP, 2880x1800). Yeah, sorry I meant SDL_SetWindowDisplayMode:
https://wiki.libsdl.org/SDL_SetWindowDisplayMode


#5

Was able to reproduce and found a fix. The problem seems to have been code in SDL_cocoamodes.m using CGDisplayModeGetWidth which returns points instead of pixels when a scaled retina resolution is in use. That’s why you’re getting

Current Resolution 1024 x 640

for the “Larger Text” setting. The fix was just switching to CGDisplayModeGetPixelWidth/Height. I submitted a patch for this in the bugtracker:
https://bugzilla.libsdl.org/show_bug.cgi?id=3700


#6

I’ve pushed what I believe to be a proper fix: https://hg.libsdl.org/SDL/rev/1e26564c7288

Let me know if it works for you!


#7

I appreciate the effort to fix this issue. I personally don’t own a retina mac (I had to borrow one for my testing), so if someone with better access to such a machine could help test the fix, that would be much appreciated. As long as SDL_GetCurrentDisplayMode() returns a resolution that the screen can actually be put into, that will solve the problem on our end.


#8

I ran @Joshua_Bodine’s test program on current hg on all 5 scaling modes on my macbook pro, and everything reports “PASS” now.