SDL: The refresh rate in SDL_DisplayMode is now a float

From e3c1749f5b15d83ad84d38e856a5d7351cae491d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 2 Jan 2023 15:47:19 -0800
Subject: [PATCH] The refresh rate in SDL_DisplayMode is now a float

---
 docs/README-migration.md                   | 21 ++++++----------
 include/SDL3/SDL_test_common.h             |  2 +-
 include/SDL3/SDL_video.h                   |  2 +-
 src/render/SDL_render.c                    | 15 ++++++------
 src/render/direct3d/SDL_render_d3d.c       |  4 ++--
 src/test/SDL_test_common.c                 | 16 ++++++-------
 src/video/SDL_video.c                      |  4 ++--
 src/video/android/SDL_androidvideo.c       |  4 ++--
 src/video/cocoa/SDL_cocoamodes.m           | 13 +++++-----
 src/video/dummy/SDL_nullvideo.c            |  2 +-
 src/video/emscripten/SDL_emscriptenvideo.c |  2 +-
 src/video/haiku/SDL_bmodes.cc              |  2 +-
 src/video/kmsdrm/SDL_kmsdrmvideo.c         | 28 +++++++++++++++++++---
 src/video/n3ds/SDL_n3dsvideo.c             |  2 +-
 src/video/ngage/SDL_ngagevideo.cpp         |  2 +-
 src/video/offscreen/SDL_offscreenvideo.c   |  2 +-
 src/video/ps2/SDL_ps2video.c               |  2 +-
 src/video/psp/SDL_pspvideo.c               |  2 +-
 src/video/raspberry/SDL_rpivideo.c         | 10 +++++---
 src/video/riscos/SDL_riscosmodes.c         |  2 +-
 src/video/uikit/SDL_uikitmodes.m           | 10 ++++----
 src/video/vita/SDL_vitavideo.c             |  2 +-
 src/video/vivante/SDL_vivantevideo.c       |  2 +-
 src/video/wayland/SDL_waylandopengles.c    |  2 +-
 src/video/wayland/SDL_waylandvideo.c       |  4 ++--
 src/video/windows/SDL_windowsmodes.c       |  2 +-
 src/video/windows/SDL_windowsvideo.c       |  2 +-
 src/video/winrt/SDL_winrtvideo.cpp         |  6 ++---
 src/video/x11/SDL_x11modes.c               |  7 ++++--
 test/testautomation_video.c                |  8 +++----
 test/testdisplayinfo.c                     |  2 +-
 test/testwm2.c                             |  2 +-
 32 files changed, 105 insertions(+), 81 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 52c3adfd23d2..385b16a7e22c 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -834,28 +834,21 @@ SDL_GetRevisionNumber() has been removed from the API, it always returned 0 in S
 
 ## SDL_video.h
 
-SDL_SetWindowBrightness and SDL_SetWindowGammaRamp have been removed from the API, because they interact poorly with modern operating systems and aren't able to limit their effects to the SDL window.
-
-Programs which have access to shaders can implement more robust versions of those functions using custom shader code rendered as a post-process effect.
-
-'SDL_GL_GetSwapInterval()' takes 'interval' as an output parameter and returns -1 on error.
-
-Removed 'SDL_GL_CONTEXT_EGL' from OpenGL configuration attributes
-You can instead use 'SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);'
-
-'SDL_GL_SwapWindow()' returns an error code.
-
 SDL_VideoInit() and SDL_VideoQuit() have been removed. Instead you can call SDL_InitSubSytem() and SDL_QuitSubSytem() with SDL_INIT_VIDEO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_VIDEO_DRIVER hint.
 
-'SDL_WINDOW_SHOW' flag has been removed. It's activated by default, and can be unactivated by using SDL_WINDOW_HIDDEN
-
-
+'SDL_WINDOW_SHOW' flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag.
 
+The refresh rate in SDL_DisplayMode is now a float.
 
+SDL_SetWindowBrightness and SDL_SetWindowGammaRamp have been removed from the API, because they interact poorly with modern operating systems and aren't able to limit their effects to the SDL window.
 
+Programs which have access to shaders can implement more robust versions of those functions using custom shader code rendered as a post-process effect.
 
+Removed SDL_GL_CONTEXT_EGL from OpenGL configuration attributes. You can instead use `SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);`
 
+SDL_GL_SwapWindow() returns 0 if the function succeeds or a negative error code if there was an error.
 
+SDL_GL_GetSwapInterval() takes the interval as an output parameter and returns 0 if the function succeeds or a negative error code if there was an error.
 
 The following functions have been renamed:
 * SDL_GetPointDisplayIndex() => SDL_GetDisplayIndexForPoint()
diff --git a/include/SDL3/SDL_test_common.h b/include/SDL3/SDL_test_common.h
index 1375d85d1cad..1fa963f517af 100644
--- a/include/SDL3/SDL_test_common.h
+++ b/include/SDL3/SDL_test_common.h
@@ -78,7 +78,7 @@ typedef struct
     int logical_h;
     float scale;
     int depth;
-    int refresh_rate;
+    float refresh_rate;
     int num_windows;
     SDL_Window **windows;
 
diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index aef9928563d9..e7799a8f1cdd 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -55,7 +55,7 @@ typedef struct
     Uint32 format;              /**< pixel format */
     int w;                      /**< width, in screen coordinates */
     int h;                      /**< height, in screen coordinates */
-    int refresh_rate;           /**< refresh rate (or zero for unspecified) */
+    float refresh_rate;         /**< refresh rate (or zero for unspecified) */
     void *driverdata;           /**< driver-specific data, initialize to 0 */
 } SDL_DisplayMode;
 
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 81edcf32499a..8b9c9809b238 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -915,22 +915,23 @@ static SDL_RenderLineMethod SDL_GetRenderLineMethod()
 
 static void SDL_CalculateSimulatedVSyncInterval(SDL_Renderer *renderer, SDL_Window *window)
 {
-    /* FIXME: SDL refresh rate API should return numerator/denominator */
-    int refresh_rate = 0;
     int display_index = SDL_GetWindowDisplayIndex(window);
     SDL_DisplayMode mode;
+    float refresh_rate;
+    int num, den;
 
     if (display_index < 0) {
         display_index = 0;
     }
-    if (SDL_GetDesktopDisplayMode(display_index, &mode) == 0) {
+    if (SDL_GetDesktopDisplayMode(display_index, &mode) == 0 && mode.refresh_rate > 0.0f) {
         refresh_rate = mode.refresh_rate;
-    }
-    if (!refresh_rate) {
+    } else {
         /* Pick a good default refresh rate */
-        refresh_rate = 60;
+        refresh_rate = 60.0f;
     }
-    renderer->simulate_vsync_interval_ns = (SDL_NS_PER_SECOND / refresh_rate);
+    num = 100;
+    den = (int)(100 * refresh_rate);
+    renderer->simulate_vsync_interval_ns = (SDL_NS_PER_SECOND * num) / den;
 }
 #endif /* !SDL_RENDER_DISABLED */
 
diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index 12179ff8d7b3..80b2b70cecb0 100644
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -300,7 +300,7 @@ static int D3D_ActivateRenderer(SDL_Renderer *renderer)
             SDL_GetWindowDisplayMode(window, &fullscreen_mode);
             data->pparams.Windowed = FALSE;
             data->pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format);
-            data->pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate;
+            data->pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode.refresh_rate);
         } else {
             data->pparams.Windowed = TRUE;
             data->pparams.BackBufferFormat = D3DFMT_UNKNOWN;
@@ -1627,7 +1627,7 @@ D3D_CreateRenderer(SDL_Window *window, Uint32 flags)
     if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
         pparams.Windowed = FALSE;
         pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format);
-        pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate;
+        pparams.FullScreen_RefreshRateInHz = (UINT)SDL_ceilf(fullscreen_mode.refresh_rate);
     } else {
         pparams.Windowed = TRUE;
         pparams.BackBufferFormat = D3DFMT_UNKNOWN;
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index a55174027f3b..681e5f8fb6f9 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -430,7 +430,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
         if (!argv[index]) {
             return -1;
         }
-        state->refresh_rate = SDL_atoi(argv[index]);
+        state->refresh_rate = (float)SDL_atof(argv[index]);
         return 2;
     }
     if (SDL_strcasecmp(argv[index], "--vsync") == 0) {
@@ -1137,7 +1137,7 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
                 SDL_GetDesktopDisplayMode(i, &mode);
                 SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask, &Gmask,
                                            &Bmask, &Amask);
-                SDL_Log("  Current mode: %dx%d@%dHz, %d bits-per-pixel (%s)\n",
+                SDL_Log("  Current mode: %dx%d@%gHz, %d bits-per-pixel (%s)\n",
                         mode.w, mode.h, mode.refresh_rate, bpp,
                         SDL_GetPixelFormatName(mode.format));
                 if (Rmask || Gmask || Bmask) {
@@ -1159,7 +1159,7 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
                         SDL_GetDisplayMode(i, j, &mode);
                         SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask,
                                                    &Gmask, &Bmask, &Amask);
-                        SDL_Log("    Mode %d: %dx%d@%dHz, %d bits-per-pixel (%s)\n",
+                        SDL_Log("    Mode %d: %dx%d@%gHz, %d bits-per-pixel (%s)\n",
                                 j, mode.w, mode.h, mode.refresh_rate, bpp,
                                 SDL_GetPixelFormatName(mode.format));
                         if (Rmask || Gmask || Bmask) {
@@ -2252,7 +2252,7 @@ void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, in
     textY += lineHeight;
 
     if (0 == SDL_GetWindowDisplayMode(window, &mode)) {
-        (void)SDL_snprintf(text, sizeof text, "SDL_GetWindowDisplayMode: %dx%d@%dHz (%s)",
+        (void)SDL_snprintf(text, sizeof text, "SDL_GetWindowDisplayMode: %dx%d@%gHz (%s)",
                            mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0, textY, text);
         textY += lineHeight;
@@ -2282,15 +2282,15 @@ void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, in
     }
 
     if (0 == SDL_GetCurrentDisplayMode(windowDisplayIndex, &mode)) {
-        (void)SDL_snprintf(text, sizeof text, "SDL_GetCurrentDisplayMode: %dx%d@%d",
-                           mode.w, mode.h, mode.refresh_rate);
+        (void)SDL_snprintf(text, sizeof text, "SDL_GetCurrentDisplayMode: %dx%d@%gHz (%s)",
+                           mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0, textY, text);
         textY += lineHeight;
     }
 
     if (0 == SDL_GetDesktopDisplayMode(windowDisplayIndex, &mode)) {
-        (void)SDL_snprintf(text, sizeof text, "SDL_GetDesktopDisplayMode: %dx%d@%d",
-                           mode.w, mode.h, mode.refresh_rate);
+        (void)SDL_snprintf(text, sizeof text, "SDL_GetDesktopDisplayMode: %dx%d@%gHz (%s)",
+                           mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0, textY, text);
         textY += lineHeight;
     }
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 3bec551693c1..2ac6c7ccad61 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -373,7 +373,7 @@ static int SDLCALL cmpmodes(const void *A, const void *B)
     } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) {
         return SDL_PIXELLAYOUT(b->format) - SDL_PIXELLAYOUT(a->format);
     } else if (a->refresh_rate != b->refresh_rate) {
-        return b->refresh_rate - a->refresh_rate;
+        return (int)(b->refresh_rate * 100) - (int)(a->refresh_rate * 100);
     }
     return 0;
 }
@@ -891,7 +891,7 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
                                                             SDL_DisplayMode *closest)
 {
     Uint32 target_format;
-    int target_refresh_rate;
+    float target_refresh_rate;
     int i;
     SDL_DisplayMode *current, *match;
 
diff --git a/src/video/android/SDL_androidvideo.c b/src/video/android/SDL_androidvideo.c
index e95f00678ec9..e61408213c38 100644
--- a/src/video/android/SDL_androidvideo.c
+++ b/src/video/android/SDL_androidvideo.c
@@ -61,7 +61,7 @@ int Android_SurfaceHeight = 0;
 static int Android_DeviceWidth = 0;
 static int Android_DeviceHeight = 0;
 static Uint32 Android_ScreenFormat = SDL_PIXELFORMAT_RGB565; /* Default SurfaceView format, in case this is queried before being filled */
-static int Android_ScreenRate = 0;
+static float Android_ScreenRate = 0.0f;
 SDL_sem *Android_PauseSem = NULL;
 SDL_sem *Android_ResumeSem = NULL;
 SDL_mutex *Android_ActivityMutex = NULL;
@@ -217,7 +217,7 @@ void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int device
     Android_SurfaceHeight = surfaceHeight;
     Android_DeviceWidth = deviceWidth;
     Android_DeviceHeight = deviceHeight;
-    Android_ScreenRate = (int)rate;
+    Android_ScreenRate = rate;
 }
 
 static Uint32 format_to_pixelFormat(int format)
diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m
index f382320ef5cd..ab7c4871e36e 100644
--- a/src/video/cocoa/SDL_cocoamodes.m
+++ b/src/video/cocoa/SDL_cocoamodes.m
@@ -83,19 +83,19 @@ static int CG_SetError(const char *prefix, CGDisplayErr result)
     return SDL_SetError("%s: %s", prefix, error);
 }
 
-static int GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link)
+static float GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link)
 {
-    int refreshRate = (int)(CGDisplayModeGetRefreshRate(vidmode) + 0.5);
+    double refreshRate = CGDisplayModeGetRefreshRate(vidmode);
 
     /* CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays). */
     if (refreshRate == 0 && link != NULL) {
         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
         if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
-            refreshRate = (int)((time.timeScale / (double)time.timeValue) + 0.5);
+            refreshRate = (double)time.timeScale / time.timeValue;
         }
     }
 
-    return refreshRate;
+    return (int)(refreshRate * 100) / 100.0f;
 }
 
 static SDL_bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode)
@@ -146,7 +146,7 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
     int width = (int)CGDisplayModeGetWidth(vidmode);
     int height = (int)CGDisplayModeGetHeight(vidmode);
     uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
-    int refreshrate = GetDisplayModeRefreshRate(vidmode, link);
+    float refreshrate = GetDisplayModeRefreshRate(vidmode, link);
     Uint32 format = GetDisplayModePixelFormat(vidmode);
     bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0;
     CFMutableArrayRef modes;
@@ -179,7 +179,8 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
         int i;
 
         for (i = 0; i < modescount; i++) {
-            int otherW, otherH, otherpixelW, otherpixelH, otherrefresh;
+            int otherW, otherH, otherpixelW, otherpixelH;
+            float otherrefresh;
             Uint32 otherformat;
             bool otherGUI;
             CGDisplayModeRef othermode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modelist, i);
diff --git a/src/video/dummy/SDL_nullvideo.c b/src/video/dummy/SDL_nullvideo.c
index 0ba5e8923924..1aeb746aec05 100644
--- a/src/video/dummy/SDL_nullvideo.c
+++ b/src/video/dummy/SDL_nullvideo.c
@@ -146,7 +146,7 @@ int DUMMY_VideoInit(_THIS)
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.w = 1024;
     mode.h = 768;
-    mode.refresh_rate = 0;
+    mode.refresh_rate = 0.0f;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
diff --git a/src/video/emscripten/SDL_emscriptenvideo.c b/src/video/emscripten/SDL_emscriptenvideo.c
index 9ad38dbf7d15..15fe76b62f05 100644
--- a/src/video/emscripten/SDL_emscriptenvideo.c
+++ b/src/video/emscripten/SDL_emscriptenvideo.c
@@ -130,7 +130,7 @@ int Emscripten_VideoInit(_THIS)
     mode.format = SDL_PIXELFORMAT_RGB888;
     emscripten_get_screen_size(&mode.w, &mode.h);
 
-    mode.refresh_rate = 0;
+    mode.refresh_rate = 0.0f;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
diff --git a/src/video/haiku/SDL_bmodes.cc b/src/video/haiku/SDL_bmodes.cc
index 3f395e29096c..402351cbea6b 100644
--- a/src/video/haiku/SDL_bmodes.cc
+++ b/src/video/haiku/SDL_bmodes.cc
@@ -169,7 +169,7 @@ static void _BDisplayModeToSdlDisplayMode(display_mode *bmode,
         SDL_DisplayMode *mode) {
     mode->w = bmode->virtual_width;
     mode->h = bmode->virtual_height;
-    mode->refresh_rate = (int)get_refresh_rate(*bmode);
+    mode->refresh_rate = get_refresh_rate(*bmode);
 
 #if WRAP_BMODE
     SDL_DisplayModeData *data = (SDL_DisplayModeData*)SDL_calloc(1,
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 527e1b8e615b..09d9354cdd8e 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -170,6 +170,28 @@ static int get_driindex(void)
     return available;
 }
 
+static float CalculateRefreshRate(drmModeModeInfo *mode)
+{
+    unsigned int num, den;
+
+    num = mode->clock * 1000;
+    den = mode->htotal * mode->vtotal;
+
+    if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+        num *= 2;
+    }
+
+    if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+        den *= 2;
+    }
+
+    if (mode->vscan > 1) {
+        den *= mode->vscan;
+    }
+
+    return ((100 * num) / den) / 100.0f;
+}
+
 static int KMSDRM_Available(void)
 {
 #ifdef __OpenBSD__
@@ -852,7 +874,7 @@ static void KMSDRM_AddDisplay(_THIS, drmModeConnector *connector, drmModeRes *re
     display.driverdata = dispdata;
     display.desktop_mode.w = dispdata->mode.hdisplay;
     display.desktop_mode.h = dispdata->mode.vdisplay;
-    display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
+    display.desktop_mode.refresh_rate = CalculateRefreshRate(&dispdata->mode);
     display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
     display.desktop_mode.driverdata = modedata;
     display.current_mode = display.desktop_mode;
@@ -1163,7 +1185,7 @@ int KMSDRM_CreateSurfaces(_THIS, SDL_Window *window)
 
     display->current_mode.w = dispdata->mode.hdisplay;
     display->current_mode.h = dispdata->mode.vdisplay;
-    display->current_mode.refresh_rate = dispdata->mode.vrefresh;
+    display->current_mode.refresh_rate = CalculateRefreshRate(&dispdata->mode);
     display->current_mode.format = SDL_PIXELFORMAT_ARGB8888;
 
     windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev,
@@ -1276,7 +1298,7 @@ void KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
 
         mode.w = conn->modes[i].hdisplay;
         mode.h = conn->modes[i].vdisplay;
-        mode.refresh_rate = conn->modes[i].vrefresh;
+        mode.refresh_rate = CalculateRefreshRate(&conn->modes[i]);
         mode.format = SDL_PIXELFORMAT_ARGB8888;
         mode.driverdata = modedata;
 
diff --git a/src/video/n3ds/SDL_n3dsvideo.c b/src/video/n3ds/SDL_n3dsvideo.c
index 627c0ddb95ac..20842e75efbd 100644
--- a/src/video/n3ds/SDL_n3dsvideo.c
+++ b/src/video/n3ds/SDL_n3dsvideo.c
@@ -120,7 +120,7 @@ AddN3DSDisplay(gfxScreen_t screen)
 
     mode.w = (screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM;
     mode.h = GSP_SCREEN_WIDTH;
-    mode.refresh_rate = 60;
+    mode.refresh_rate = 60.0f;
     mode.format = FRAMEBUFFER_FORMAT;
     mode.driverdata = NULL;
 
diff --git a/src/video/ngage/SDL_ngagevideo.cpp b/src/video/ngage/SDL_ngagevideo.cpp
index 0eff10fcc0db..79368f10cef6 100644
--- a/src/video/ngage/SDL_ngagevideo.cpp
+++ b/src/video/ngage/SDL_ngagevideo.cpp
@@ -151,7 +151,7 @@ int NGAGE_VideoInit(_THIS)
     mode.format = SDL_PIXELFORMAT_RGB444;
     mode.w = 176;
     mode.h = 208;
-    mode.refresh_rate = 0;
+    mode.refresh_rate = 0.0f;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
diff --git a/src/video/offscreen/SDL_offscreenvideo.c b/src/video/offscreen/SDL_offscreenvideo.c
index 158b314cb713..4ca0840d6d56 100644
--- a/src/video/offscreen/SDL_offscreenvideo.c
+++ b/src/video/offscreen/SDL_offscreenvideo.c
@@ -104,7 +104,7 @@ int OFFSCREEN_VideoInit(_THIS)
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.w = 1024;
     mode.h = 768;
-    mode.refresh_rate = 0;
+    mode.refresh_rate = 0.0f;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
diff --git a/src/video/ps2/SDL_ps2video.c b/src/video/ps2/SDL_ps2video.c
index 61d905a36734..f428304eb1d4 100644
--- a/src/video/ps2/SDL_ps2video.c
+++ b/src/video/ps2/SDL_ps2video.c
@@ -72,7 +72,7 @@ static int PS2_VideoInit(_THIS)
 
     current_mode.w = 640;
     current_mode.h = 480;
-    current_mode.refresh_rate = 60;
+    current_mode.refresh_rate = 60.0f;
 
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
diff --git a/src/video/psp/SDL_pspvideo.c b/src/video/psp/SDL_pspvideo.c
index 410e01f12338..808f86370112 100644
--- a/src/video/psp/SDL_pspvideo.c
+++ b/src/video/psp/SDL_pspvideo.c
@@ -144,7 +144,7 @@ int PSP_VideoInit(_THIS)
     current_mode.w = 480;
     current_mode.h = 272;
 
-    current_mode.refresh_rate = 60;
+    current_mode.refresh_rate = 60.0f;
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
     current_mode.driverdata = NULL;
diff --git a/src/video/raspberry/SDL_rpivideo.c b/src/video/raspberry/SDL_rpivideo.c
index ad47ebcced38..b68981137f18 100644
--- a/src/video/raspberry/SDL_rpivideo.c
+++ b/src/video/raspberry/SDL_rpivideo.c
@@ -53,7 +53,7 @@ static void RPI_Destroy(SDL_VideoDevice *device)
     SDL_free(device);
 }
 
-static int RPI_GetRefreshRate()
+static float RPI_GetRefreshRate()
 {
     TV_DISPLAY_STATE_T tvstate;
     if (vc_tv_get_display_state(&tvstate) == 0) {
@@ -62,9 +62,13 @@ static int RPI_GetRefreshRate()
         HDMI_PROPERTY_PARAM_T property;
         property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
         vc_tv_hdmi_get_property(&property);
-        return property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f / 1001.0f) : tvstate.display.hdmi.frame_rate;
+        if (property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) {
+            return ((100 * tvstate.display.hdmi.frame_rate * 1000) / 1001) / 100.0f;
+        } else {
+            return (float)tvstate.display.hdmi.frame_rate;
+        }
     }
-    return 60; /* Failed to get display state, default to 60 */
+    return 60.0f; /* Failed to get display state, default to 60 */
 }
 
 static SDL_VideoDevice *RPI_Create()
diff --git a/src/video/riscos/SDL_riscosmodes.c b/src/video/riscos/SDL_riscosmodes.c
index 4feed227474b..de267d52de60 100644
--- a/src/video/riscos/SDL_riscosmodes.c
+++ b/src/video/riscos/SDL_riscosmodes.c
@@ -139,7 +139,7 @@ static SDL_bool read_mode_block(int *block, SDL_DisplayMode *mode, SDL_bool exte
     mode->w = xres;
     mode->h = yres;
     mode->format = RISCOS_ModeToPixelFormat(ncolour, modeflags, log2bpp);
-    mode->refresh_rate = rate;
+    mode->refresh_rate = (float)rate;
 
     return SDL_TRUE;
 }
diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m
index 39e7808d8a2c..bf81c66bb985 100644
--- a/src/video/uikit/SDL_uikitmodes.m
+++ b/src/video/uikit/SDL_uikitmodes.m
@@ -249,14 +249,14 @@ static void UIKit_FreeDisplayModeData(SDL_DisplayMode *mode)
     }
 }
 
-static NSUInteger UIKit_GetDisplayModeRefreshRate(UIScreen *uiscreen)
+static float UIKit_GetDisplayModeRefreshRate(UIScreen *uiscreen)
 {
 #ifdef __IPHONE_10_3
     if ([uiscreen respondsToSelector:@selector(maximumFramesPerSecond)]) {
-        return uiscreen.maximumFramesPerSecond;
+        return (float)uiscreen.maximumFramesPerSecond;
     }
 #endif
-    return 0;
+    return 0.0f;
 }
 
 static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h,
@@ -270,7 +270,7 @@ static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h,
     }
 
     mode.format = SDL_PIXELFORMAT_ABGR8888;
-    mode.refresh_rate = (int)UIKit_GetDisplayModeRefreshRate(uiscreen);
+    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
     mode.w = w;
     mode.h = h;
 
@@ -315,7 +315,7 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
     }
 
     mode.format = SDL_PIXELFORMAT_ABGR8888;
-    mode.refresh_rate = (int)UIKit_GetDisplayModeRefreshRate(uiscreen);
+    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
     mode.w = (int)size.width;
     mode.h = (int)size.height;
 
diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c
index 40aabae71e84..30e0b9fbd765 100644
--- a/src/video/vita/SDL_vitavideo.c
+++ b/src/video/vita/SDL_vitavideo.c
@@ -209,7 +209,7 @@ int VITA_VideoInit(_THIS)
     }
 #endif
 
-    current_mode.refresh_rate = 60;
+    current_mode.refresh_rate = 60.0f;
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
 
diff --git a/src/video/vivante/SDL_vivantevideo.c b/src/video/vivante/SDL_vivantevideo.c
index e3bceba42705..6979eec5b9be 100644
--- a/src/video/vivante/SDL_vivantevideo.c
+++ b/src/video/vivante/SDL_vivantevideo.c
@@ -156,7 +156,7 @@ static int VIVANTE_AddVideoDisplays(_THIS)
         break;
     }
     /* FIXME: How do we query refresh rate? */
-    current_mode.refresh_rate = 60;
+    current_mode.refresh_rate = 60.0f;
 
     SDL_zero(display);
     display.name = VIVANTE_GetDisplayName(_this);
diff --git a/src/video/wayland/SDL_waylandopengles.c b/src/video/wayland/SDL_waylandopengles.c
index 2a32ed339fa1..4f3071449bc1 100644
--- a/src/video/wayland/SDL_waylandopengles.c
+++ b/src/video/wayland/SDL_waylandopengles.c
@@ -125,7 +125,7 @@ int Wayland_GLES_SwapWindow(_THIS, SDL_Window *window)
         struct wl_display *display = videodata->display;
         SDL_VideoDisplay *sdldisplay = SDL_GetDisplayForWindow(window);
         /* ~10 frames (or 1 sec), so we'll progress even if throttled to zero. */
-        const Uint64 max_wait = SDL_GetTicksNS() + (sdldisplay->current_mode.refresh_rate ? ((SDL_NS_PER_SECOND * 10) / sdldisplay->current_mode.refresh_rate) : SDL_NS_PER_SECOND);
+        const Uint64 max_wait = SDL_GetTicksNS() + (sdldisplay->current_mode.refresh_rate ? ((SDL_NS_PER_SECOND * 10 * 100) / (int)(sdldisplay->current_mode.refresh_rate * 100)) : SDL_NS_PER_SECOND);
         while (SDL_AtomicGet(&data->swap_interval_ready) == 0) {
             Uint64 now;
 
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index de4e402eef50..c7d33d538dd4 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -570,7 +570,7 @@ static void display_handle_done(void *data,
         native_mode.w = driverdata->native_width;
         native_mode.h = driverdata->native_height;
     }
-    native_mode.refresh_rate = (int)SDL_round(driverdata->refresh / 1000.0); /* mHz to Hz */
+    native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
     native_mode.driverdata = driverdata->output;
 
     /* The scaled desktop mode */
@@ -592,7 +592,7 @@ static void display_handle_done(void *data,
         desktop_mode.w = driverdata->height;
         desktop_mode.h = driverdata->width;
     }
-    desktop_mode.refresh_rate = (int)SDL_round(driverdata->refresh / 1000.0); /* mHz to Hz */
+    desktop_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
     desktop_mode.driverdata = driverdata->output;
 
     /*
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 122e1175a16c..27771533bb18 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -181,7 +181,7 @@ static SDL_bool WIN_GetDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_D
     mode->format = SDL_PIXELFORMAT_UNKNOWN;
     mode->w = data->DeviceMode.dmPelsWidth;
     mode->h = data->DeviceMode.dmPelsHeight;
-    mode->refresh_rate = data->DeviceMode.dmDisplayFrequency;
+    mode->refresh_rate = (float)data->DeviceMode.dmDisplayFrequency;
 
     /* Fill in the mode information */
     WIN_UpdateDisplayMode(_this, deviceName, index, mode);
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 9cdf8d3bdd4c..8fbe3e4f1c31 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -430,7 +430,7 @@ int WIN_VideoInit(_THIS)
 
         SDL_zero(current_mode);
         D3D12_XBOX_GetResolution(&current_mode.w, &current_mode.h);
-        current_mode.refresh_rate = 60;
+        current_mode.refresh_rate = 60.0f;
         current_mode.format = SDL_PIXELFORMAT_ARGB8888;
 
         SDL_zero(display);
diff --git a/src/video/winrt/SDL_winrtvideo.cpp b/src/video/winrt/SDL_winrtvideo.cpp
index 5f345c58a358..e5cef4b6d311 100644
--- a/src/video/winrt/SDL_winrtvideo.cpp
+++ b/src/video/winrt/SDL_winrtvideo.cpp
@@ -256,7 +256,7 @@ static void WINRT_DXGIModeToSDLDisplayMode(const DXGI_MODE_DESC *dxgiMode, SDL_D
     SDL_zerop(sdlMode);
     sdlMode->w = dxgiMode->Width;
     sdlMode->h = dxgiMode->Height;
-    sdlMode->refresh_rate = dxgiMode->RefreshRate.Numerator / dxgiMode->RefreshRate.Denominator;
+    sdlMode->refresh_rate = (((100 * dxgiMode->RefreshRate.Numerator) / dxgiMode->RefreshRate.Denominator) / 100.0f);
     sdlMode->format = D3D11_DXGIFormatToSDLPixelFormat(dxgiMode->Format);
 }
 
@@ -307,7 +307,7 @@ static int WINRT_AddDisplaysForOutput(_THIS, IDXGIAdapter1 *dxgiAdapter1, int ou
         mode.w = (dxgiOutputDesc.DesktopCoordinates.right - dxgiOutputDesc.DesktopCoordinates.left);
         mode.h = (dxgiOutputDesc.DesktopCoordinates.bottom - dxgiOutputDesc.DesktopCoordinates.top);
         mode.format = DXGI_FORMAT_B8G8R8A8_UNORM;
-        mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
+        mode.refresh_rate = 0.0f; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
         display.desktop_mode = mode;
         display.current_mode = mode;
         if (!SDL_AddDisplayMode(&display, &mode)) {
@@ -427,7 +427,7 @@ static int WINRT_AddDisplaysForAdapter(_THIS, IDXGIFactory2 *dxgiFactory2, int a
 #endif
 
                 mode.format = DXGI_FORMAT_B8G8R8A8_UNORM;
-                mode.refresh_rate = 0; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
+                mode.refresh_rate = 0.0f; /* Display mode is unknown, so just fill in zero, as specified by SDL's header files */
                 display.desktop_mode = mode;
                 display.current_mode = mode;
                 bool error = SDL_AddDisplayMode(&display, &mode) < 0 ||
diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c
index 883d298f4d78..790f3fcd4046 100644
--- a/src/video/x11/SDL_x11modes.c
+++ b/src/video/x11/SDL_x11modes.c
@@ -192,9 +192,12 @@ static SDL_bool CheckXRandR(Display *display, int *

(Patch may be truncated, please check the link at the top of this post.)