diff --git a/docs/README-migration.md b/docs/README-migration.md index 08b62fb1fa705..2807be1be8d57 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -1962,7 +1962,7 @@ The SDL_WINDOW_SKIP_TASKBAR flag has been replaced by the SDL_WINDOW_UTILITY fla SDL_DisplayMode now includes the pixel density which can be greater than 1.0 for display modes that have a higher pixel size than the mode size. You should use SDL_GetWindowSizeInPixels() to get the actual pixel size of the window back buffer. -The refresh rate in SDL_DisplayMode is now a float. +The refresh rate in SDL_DisplayMode is now a float, as well as being represented as a precise fraction with numerator and denominator. Rather than iterating over display modes using an index, there is a new function SDL_GetFullscreenDisplayModes() to get the list of available fullscreen modes on a display. ```c diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 07c608e89fa39..9a22dbd7d96ad 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -87,13 +87,15 @@ typedef enum SDL_SystemTheme */ typedef struct SDL_DisplayMode { - SDL_DisplayID displayID; /**< the display this mode is associated with */ - SDL_PixelFormat format; /**< pixel format */ - int w; /**< width */ - int h; /**< height */ - float pixel_density; /**< scale converting size to pixels (e.g. a 1920x1080 mode with 2.0 scale would have 3840x2160 pixels) */ - float refresh_rate; /**< refresh rate (or zero for unspecified) */ - void *driverdata; /**< driver-specific data, initialize to 0 */ + SDL_DisplayID displayID; /**< the display this mode is associated with */ + SDL_PixelFormat format; /**< pixel format */ + int w; /**< width */ + int h; /**< height */ + float pixel_density; /**< scale converting size to pixels (e.g. a 1920x1080 mode with 2.0 scale would have 3840x2160 pixels) */ + float refresh_rate; /**< refresh rate (or 0.0f for unspecified) */ + int refresh_rate_numerator; /**< precise refresh rate numerator (or 0 for unspecified) */ + int refresh_rate_denominator; /**< precise refresh rate denominator */ + 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 8d050bdb1b5df..53a59bfb64e9c 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -918,21 +918,20 @@ static void SDL_CalculateSimulatedVSyncInterval(SDL_Renderer *renderer, SDL_Wind { SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); const SDL_DisplayMode *mode; - float refresh_rate; int num, den; if (displayID == 0) { displayID = SDL_GetPrimaryDisplay(); } mode = SDL_GetDesktopDisplayMode(displayID); - if (mode && mode->refresh_rate > 0.0f) { - refresh_rate = mode->refresh_rate; + if (mode && mode->refresh_rate_numerator > 0) { + num = mode->refresh_rate_numerator; + den = mode->refresh_rate_denominator; } else { /* Pick a good default refresh rate */ - refresh_rate = 60.0f; + num = 60; + den = 1; } - num = 100; - den = (int)(100 * refresh_rate); renderer->simulate_vsync_interval_ns = (SDL_NS_PER_SECOND * num) / den; } diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index aa43387a69984..0d676cc339931 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -725,6 +725,16 @@ static void SDL_FinalizeDisplayMode(SDL_DisplayMode *mode) if (mode->pixel_density <= 0.0f) { mode->pixel_density = 1.0f; } + + if (mode->refresh_rate_numerator > 0) { + if (mode->refresh_rate_denominator <= 0) { + mode->refresh_rate_denominator = 1; + } + mode->refresh_rate = ((100 * (Sint64)mode->refresh_rate_numerator) / mode->refresh_rate_denominator) / 100.0f; + } else { + SDL_CalculateFraction(mode->refresh_rate, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); + mode->refresh_rate = (int)(mode->refresh_rate * 100) / 100.0f; + } } SDL_DisplayID SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode) diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 638e82d6b4f55..b3001aa9793b1 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -99,17 +99,17 @@ static int CG_SetError(const char *prefix, CGDisplayErr result) static float GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link) { - double refreshRate = CGDisplayModeGetRefreshRate(vidmode); + float refreshRate = (float)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 = (double)time.timeScale / time.timeValue; + refreshRate = (float)time.timeScale / time.timeValue; } } - return (int)(refreshRate * 100) / 100.0f; + return refreshRate; } static SDL_bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode) diff --git a/src/video/haiku/SDL_bmodes.cc b/src/video/haiku/SDL_bmodes.cc index c44729e4d0083..1201d714bb64c 100644 --- a/src/video/haiku/SDL_bmodes.cc +++ b/src/video/haiku/SDL_bmodes.cc @@ -65,9 +65,10 @@ static SDL_INLINE display_mode * _ExtractBMode(SDL_DisplayMode *mode) { } /* Copied from haiku/trunk/src/preferences/screen/ScreenMode.cpp */ -static float get_refresh_rate(display_mode &mode) { - return float(mode.timing.pixel_clock * 1000) - / float(mode.timing.h_total * mode.timing.v_total); +static void get_refresh_rate(display_mode &mode, int *numerator, int *denominator) +{ + *numerator = (mode.timing.pixel_clock * 1000); + *denominator = (mode.timing.h_total * mode.timing.v_total); } @@ -169,7 +170,7 @@ static void _BDisplayModeToSdlDisplayMode(display_mode *bmode, SDL_DisplayMode * SDL_zerop(mode); mode->w = bmode->virtual_width; mode->h = bmode->virtual_height; - mode->refresh_rate = get_refresh_rate(*bmode); + get_refresh_rate(*bmode, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); #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 5d231595cfa71..a56c39da39f28 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -170,26 +170,22 @@ static int get_driindex(void) return available; } -static float CalculateRefreshRate(drmModeModeInfo *mode) +static void CalculateRefreshRate(drmModeModeInfo *mode, int *numerator, int *denominator) { - unsigned int num, den; - - num = mode->clock * 1000; - den = mode->htotal * mode->vtotal; + *numerator = mode->clock * 1000; + *denominator = mode->htotal * mode->vtotal; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - num *= 2; + *numerator *= 2; } if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { - den *= 2; + *denominator *= 2; } if (mode->vscan > 1) { - den *= mode->vscan; + *denominator *= mode->vscan; } - - return ((100 * (Sint64)num) / den) / 100.0f; } static int KMSDRM_Available(void) @@ -964,7 +960,7 @@ static void KMSDRM_AddDisplay(SDL_VideoDevice *_this, drmModeConnector *connecto display.driverdata = dispdata; display.desktop_mode.w = dispdata->mode.hdisplay; display.desktop_mode.h = dispdata->mode.vdisplay; - display.desktop_mode.refresh_rate = CalculateRefreshRate(&dispdata->mode); + CalculateRefreshRate(&dispdata->mode, &display.desktop_mode.refresh_rate_numerator, &display.desktop_mode.refresh_rate_denominator); display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888; display.desktop_mode.driverdata = modedata; @@ -1279,15 +1275,6 @@ int KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window) */ KMSDRM_GetModeToSet(window, &dispdata->mode); - /* - SDL_zero(current_mode); - current_mode.w = dispdata->mode.hdisplay; - current_mode.h = dispdata->mode.vdisplay; - current_mode.refresh_rate = CalculateRefreshRate(&dispdata->mode); - current_mode.format = SDL_PIXELFORMAT_ARGB8888; - SDL_SetCurrentDisplayMode(display, ¤t_mode); - */ - windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, dispdata->mode.hdisplay, dispdata->mode.vdisplay, surface_fmt, surface_flags); @@ -1433,7 +1420,7 @@ int KMSDRM_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) SDL_zero(mode); mode.w = conn->modes[i].hdisplay; mode.h = conn->modes[i].vdisplay; - mode.refresh_rate = CalculateRefreshRate(&conn->modes[i]); + CalculateRefreshRate(&conn->modes[i], &mode.refresh_rate_numerator, &mode.refresh_rate_denominator); 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 b9e34eb3889b5..1c1f43204a435 100644 --- a/src/video/n3ds/SDL_n3dsvideo.c +++ b/src/video/n3ds/SDL_n3dsvideo.c @@ -194,7 +194,7 @@ static int N3DS_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *displa SDL_zero(mode); mode.w = (displaydata->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 = format_map[i].pixfmt; mode.driverdata = modedata; modedata->fmt = format_map[i].gspfmt; diff --git a/src/video/raspberry/SDL_rpivideo.c b/src/video/raspberry/SDL_rpivideo.c index 222a50fb03753..d9fda949bd9d7 100644 --- a/src/video/raspberry/SDL_rpivideo.c +++ b/src/video/raspberry/SDL_rpivideo.c @@ -51,7 +51,7 @@ static void RPI_Destroy(SDL_VideoDevice *device) SDL_free(device); } -static float RPI_GetRefreshRate() +static void RPI_GetRefreshRate(int *numerator, int *denominator) { TV_DISPLAY_STATE_T tvstate; if (vc_tv_get_display_state(&tvstate) == 0) { @@ -61,12 +61,18 @@ static float RPI_GetRefreshRate() property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; vc_tv_hdmi_get_property(&property); if (property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC) { - return ((100 * tvstate.display.hdmi.frame_rate * 1000) / 1001) / 100.0f; + *numerator = tvstate.display.hdmi.frame_rate * 1000; + *denominator = 1001; } else { - return (float)tvstate.display.hdmi.frame_rate; + *numerator = tvstate.display.hdmi.frame_rate; + *denominator = 1; } + return; } - return 60.0f; /* Failed to get display state, default to 60 */ + + /* Failed to get display state, default to 60 */ + *numerator = 60; + *denominator = 1; } static SDL_VideoDevice *RPI_Create() @@ -158,7 +164,7 @@ static void AddDispManXDisplay(const int display_id) SDL_zero(mode); mode.w = modeinfo.width; mode.h = modeinfo.height; - mode.refresh_rate = RPI_GetRefreshRate(); + RPI_GetRefreshRate(&mode.refresh_rate_numerator, &mode.refresh_rate_denominator); /* 32 bpp for default */ mode.format = SDL_PIXELFORMAT_ABGR8888; diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index 70e99a3590fb1..ba65650b261cf 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -285,7 +285,7 @@ int UIKit_AddDisplay(SDL_bool send_event){ mode.h = (int)size.height; mode.pixel_density = 1; mode.format = SDL_PIXELFORMAT_ABGR8888; - mode.refresh_rate = 60; + mode.refresh_rate = 60.0f; display.natural_orientation = SDL_ORIENTATION_LANDSCAPE; diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index e7515880c602d..62fe87036a497 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -666,7 +666,8 @@ static void AddEmulatedModes(SDL_DisplayData *dispdata, int native_width, int na for (i = 0; i < SDL_arraysize(mode_list); ++i) { SDL_zero(mode); mode.format = dpy->desktop_mode.format; - mode.refresh_rate = dpy->desktop_mode.refresh_rate; + mode.refresh_rate_numerator = dpy->desktop_mode.refresh_rate_numerator; + mode.refresh_rate_denominator = dpy->desktop_mode.refresh_rate_denominator; if (rot_90) { mode.w = mode_list[i].h; @@ -809,7 +810,8 @@ static void display_handle_done(void *data, native_mode.w = driverdata->pixel_width; native_mode.h = driverdata->pixel_height; } - native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */ + native_mode.refresh_rate_numerator = driverdata->refresh; + native_mode.refresh_rate_denominator = 1000; if (driverdata->has_logical_size) { /* If xdg-output is present... */ if (native_mode.w != driverdata->screen_width || native_mode.h != driverdata->screen_height) { @@ -851,7 +853,8 @@ static void display_handle_done(void *data, desktop_mode.pixel_density = 1.0f; } - desktop_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */ + desktop_mode.refresh_rate_numerator = driverdata->refresh; + desktop_mode.refresh_rate_denominator = 1000; if (driverdata->display > 0) { dpy = SDL_GetVideoDisplay(driverdata->display); diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 19bea73f6f54c..a221779e9696c 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -161,16 +161,20 @@ static SDL_DisplayOrientation WIN_GetDisplayOrientation(DEVMODE *mode) } } -static float WIN_GetRefreshRate(DEVMODE *mode) +static void WIN_GetRefreshRate(DEVMODE *mode, int *numerator, int *denominator) { /* We're not currently using DXGI to query display modes, so fake NTSC timings */ switch (mode->dmDisplayFrequency) { case 119: case 59: case 29: - return ((100 * (mode->dmDisplayFrequency + 1) * 1000) / 1001) / 100.0f; + *numerator = (mode->dmDisplayFrequency + 1) * 1000; + *denominator = 1001; + break; default: - return (float)mode->dmDisplayFrequency; + *numerator = mode->dmDisplayFrequency; + *denominator = 1; + break; } } @@ -223,7 +227,7 @@ static SDL_bool WIN_GetDisplayMode(SDL_VideoDevice *_this, HMONITOR hMonitor, LP mode->format = SDL_PIXELFORMAT_UNKNOWN; mode->w = data->DeviceMode.dmPelsWidth; mode->h = data->DeviceMode.dmPelsHeight; - mode->refresh_rate = WIN_GetRefreshRate(&data->DeviceMode); + WIN_GetRefreshRate(&data->DeviceMode, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); /* Fill in the mode information */ WIN_UpdateDisplayMode(_this, deviceName, index, mode); diff --git a/src/video/winrt/SDL_winrtvideo.cpp b/src/video/winrt/SDL_winrtvideo.cpp index 48805d3c73181..3e06e852dce85 100644 --- a/src/video/winrt/SDL_winrtvideo.cpp +++ b/src/video/winrt/SDL_winrtvideo.cpp @@ -253,7 +253,8 @@ 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 = (((100 * dxgiMode->RefreshRate.Numerator) / dxgiMode->RefreshRate.Denominator) / 100.0f); + sdlMode->refresh_rate_numerator = dxgiMode->RefreshRate.Numerator; + sdlMode->refresh_rate_denominator = dxgiMode->RefreshRate.Denominator; sdlMode->format = D3D11_DXGIFormatToSDLPixelFormat(dxgiMode->Format); } @@ -314,9 +315,6 @@ static int WINRT_AddDisplaysForOutput(SDL_VideoDevice *_this, IDXGIAdapter1 *dxg hr = dxgiOutput->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &numModes, NULL); if (FAILED(hr)) { - if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { - // TODO, WinRT: make sure display mode(s) are added when using Terminal Services / Windows Simulator - } WIN_SetErrorFromHRESULT(__FUNCTION__ ", IDXGIOutput::GetDisplayModeList [get mode list size] failed", hr); goto done; } diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 850ba2b0acbfc..62b6c48610272 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -414,9 +414,9 @@ static SDL_bool CheckXRandR(Display *display, int *major, int *minor) #define XRANDR_ROTATION_LEFT (1 << 1) #define XRANDR_ROTATION_RIGHT (1 << 3) -static float CalculateXRandRRefreshRate(const XRRModeInfo *info) +static void CalculateXRandRRefreshRate(const XRRModeInfo *info, int *numerator, int *denominator) { - float vTotal = info->vTotal; + unsigned int vTotal = info->vTotal; if (info->modeFlags & RR_DoubleScan) { /* doublescan doubles the number of lines */ @@ -429,10 +429,13 @@ static float CalculateXRandRRefreshRate(const XRRModeInfo *info) vTotal /= 2; } - if (info->hTotal && vTotal != 0.f) { - return ((100 * (Sint64)info->dotClock) / (info->hTotal * vTotal)) / 100.0f; + if (info->hTotal && vTotal) { + *numerator = info->dotClock; + *denominator = (info->hTotal * vTotal); + } else { + *numerator = 0; + *denominator = 0; } - return 0.0f; } static SDL_bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc crtc, @@ -465,11 +468,11 @@ static SDL_bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRC mode->w = (info->width * scale_w + 0xffff) >> 16; mode->h = (info->height * scale_h + 0xffff) >> 16; } - mode->refresh_rate = CalculateXRandRRefreshRate(info); + CalculateXRandRRefreshRate(info, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); ((SDL_DisplayModeData *)mode->driverdata)->xrandr_mode = modeID; #ifdef X11MODES_DEBUG - printf("XRandR mode %d: %dx%d@%gHz\n", (int)modeID, - mode->screen_w, mode->screen_h, mode->refresh_rate); + printf("XRandR mode %d: %dx%d@%d/%dHz\n", (int)modeID, + mode->screen_w, mode->screen_h, mode->refresh_rate_numerator, mode->refresh_rate_denominator); #endif return SDL_TRUE; }