sokol
sokol copied to clipboard
sokol_app: dpi_scale seems to be off on iPhone 13 mini
Hi, this is more of a question than an issue, thanks in advance! I noticed that I was getting a weird horizontal resolution for an iPhone 13 mini from sapp_width() - it returns 2338 while the screen is actually 2340 pixels as far as I know. As a side effect of this, there's an ugly vertical line of noise on the side of the screen because those pixels aren't cleared.
I traced this back to UIScreen.mainScreen.nativeScale
returning a float value of 2.88000011
. UIScreen.mainScreen.bounds
returns 812x375. When I type 812 * 2.88000011
into my trusty calculator, the result is 2338.56008932
.
So I thought, maybe I have a configuration issue in Xcode somewhere as usual, maybe the Storyboard? That would mean this is out of sokol_app's control and I have to fix my project.
However, UIScreen.mainScreen.nativeBounds
returns a CGRect with the correct resolution: 2340x1080.
Aren't those values supposed to be linked / derived from each other? Or is there maybe a rounding error involved, because the correct scaling value of 2.881773399014778
is unusually ~~baloney~~ odd?
At least, if I hardcode the framebuffer resolution to 2340x1080 then the noisy line disappears. But I would be really interested in the root cause of this. Thanks again!
I just noticed this commit, in which rounding was added to the computation of the framebuffer size. This results in a framebuffer resolution of 2339x1080 for the iPhone 13 mini in landscape mode, a 50% reduction in terms of relative error :) However, it's still off by one pixel...
To fix my use case, I now changed _sapp_ios_update_dimensions
to calculate framebuffer size like this:
_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) {
CGRect screen_rect = UIScreen.mainScreen.bounds;
// mainScreen.bounds respects device orientation, while nativeBounds is always portrait
const bool is_portrait = screen_rect.size.width < screen_rect.size.height;
CGRect native_bounds = UIScreen.mainScreen.nativeBounds;
_sapp.framebuffer_width = is_portrait ? (int)native_bounds.size.width : (int)native_bounds.size.height;
_sapp.framebuffer_height = is_portrait ? (int)native_bounds.size.height : (int)native_bounds.size.width;
_sapp.window_width = (int)roundf(screen_rect.size.width);
_sapp.window_height = (int)roundf(screen_rect.size.height);
...
This gives the correct resolution without any rounding necessary. I left the roundf() for window_width/height, although I'm not sure if it's needed. However, there's a caveat, this branch will no longer be entered: https://github.com/floooh/sokol/blob/efc3a2b3d89d1d6371e130dd2d1046c976a4a9cc/sokol_app.h#L4128-L4141
The dimensions are now always exactly the drawable size. I'm not sure if this has side effects in case the orientation changes, since my app is landscape only.
Thanks for the investigation! I wasn't aware that there are iOS devices with 'odd' scaling factors like that.
I need to investigate why there's this precision problem when multiplying mainScreen.bounds with mainScreen.nativeScale, because IIRC I've been through quite a few iterations in that area (for instance computing dpi_scale from .bounds and .nativeBounds), and all those iterations had one problem or another, so I'd prefer keeping the current code but fixing the precision problem.
It's definitely weird that the nativeScale isn't exact, but apparently rounded down to two decimal places. There could be a reason they're doing that, but I couldn't find anything useful via google. And I agree, especially since the dpi_scale is used for converting input positions to pixel positions, it should definitely be based on what iOS uses as the internal scaling value.
Can be reproduced in the Simulator:
-
sokol-samples % ./fips set config sapp-metal-ios-xcode-debug; ./fips gen
- Open sapp-metal-ios-xcode-debug/sokol-samples.xcodeproj
- Right-click sokol-samples project -> New File... -> iOS -> Launch Screen -> Next -> Select target triangle-sapp -> Create
- Left-click sokol-samples project, left-click target triangle-sapp -> General -> Launch Screen File -> enter "Launch Screen"
- Open triangle-sapp.c, add
.high_dpi = true,
in sokol_main - Debug triangle-sapp on iOS Simulator iPhone 13 mini
- Set a breakpoint in sg_begin_default_pass: width is 2339, but should be 2340
You need to create the launch screen storyboard to get actual fullscreen on iOS 15 (?). At least that's the only way I know that works.