cocoa_common: report panel refresh rate, not our own display link rate

cocoa_get_refresh_rate() on iOS/tvOS returned the CADisplayLink's
preferredFrameRateRange.preferred.  Since 12c4ff0739 ("apple: better
refresh rate restoration") apple_display_server_init() stamps
settings->floats.video_refresh_rate onto that same display link at
startup, so the getter and the setter now share one storage slot.

The result is a feedback loop.  DEFAULT_REFRESH_RATE is 60 on Apple
targets, so on a fresh config the display link is pulled from the
panel rate the CocoaView set up ([UIScreen maximumFramesPerSecond],
120 on ProMotion) down to 60, and every subsequent read reports 60.
"Set Display-Reported Refresh Rate" can therefore never report
anything the user does not already have configured, and on a
ProMotion iPhone it is stuck at 60 Hz.

All four Apple refresh-rate vtable entries (cocoa_gl_ctx,
cocoa_vk_ctx, metal, dispserv_apple) funnel into this helper, so the
stale value also reaches runloop.c's vsync throttle decision and
RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE.

Query [UIScreen maximumFramesPerSecond] instead.  That is the
iOS/tvOS analogue of the CGDisplayModeGetRefreshRate() call the macOS
branch of this same function already makes: a property of the
display, not of our own timer.  The display link is kept only as the
pre-iOS-10.3 fallback, where maximumFramesPerSecond does not exist
and no ProMotion hardware does either.

The display link keeps honouring the configured rate for actual
frame pacing; only the reporting path changes.
This commit is contained in:
libretroadmin authored and libretroadmin committed 2026-07-27 05:37:30 +02:00
1 parent 79b46b2518
commit 580c738efd
1 file changed
+35 -24
+35 -24
View File
@@ -1161,33 +1161,44 @@ float cocoa_get_refresh_rate(void)
return (rate > 0.0) ? (float)rate : 60.0f;
#endif
#else /* iOS / tvOS */
CADisplayLink *dl = [CocoaView get].displayLink;
if (dl)
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 || __TV_OS_VERSION_MAX_ALLOWED >= 150000
if (@available(iOS 15.0, tvOS 15.0, *))
return dl.preferredFrameRateRange.preferred;
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 || __TV_OS_VERSION_MAX_ALLOWED >= 100000
if (@available(iOS 10.0, tvOS 10.0, *))
return dl.preferredFramesPerSecond;
#endif
/* iOS 6 - 9 / tvOS < 10: only frameInterval exists. It is
* the number of screen refreshes between callbacks, so
* convert to Hz assuming a 60 Hz panel (accurate for every
* pre-iOS-10 device - ProMotion is iPad Pro 2017+). */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
{
NSInteger fi = dl.frameInterval;
return 60.0f / (float)(fi > 0 ? fi : 1);
}
#pragma clang diagnostic pop
}
/* Report what the panel is capable of, NOT what we last asked
* the CADisplayLink to run at.
*
* apple_display_server_init() stamps
* settings->floats.video_refresh_rate onto the display link at
* startup, so querying preferredFrameRateRange here closes a
* feedback loop: "Set Display-Reported Refresh Rate" would just
* echo the value the user already has configured. On a
* ProMotion device with a default config that means a permanent
* 60 Hz readout even though the panel does 120 Hz.
*
* maximumFramesPerSecond is the iOS/tvOS analogue of the
* CGDisplayModeGetRefreshRate() query the macOS branch above
* makes - a property of the display, not of our own timer. */
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100300 || __TV_OS_VERSION_MAX_ALLOWED >= 100200
if (@available(iOS 10.3, tvOS 10.2, *))
return [UIScreen mainScreen].maximumFramesPerSecond;
{
NSInteger max_fps = [[UIScreen mainScreen] maximumFramesPerSecond];
if (max_fps > 0)
return (float)max_fps;
}
#endif
/* iOS 6 - 9 / tvOS < 10: maximumFramesPerSecond does not exist.
* Only CADisplayLink's frameInterval does - the number of screen
* refreshes between callbacks - so convert to Hz assuming a 60 Hz
* panel (accurate for every pre-iOS-10 device; ProMotion is iPad
* Pro 2017 / iOS 10.3+). */
{
CADisplayLink *dl = [[CocoaView get] displayLink];
if (dl)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSInteger fi = [dl frameInterval];
#pragma clang diagnostic pop
return 60.0f / (float)(fi > 0 ? fi : 1);
}
}
return 60.0f;
#endif
}