mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
2b7f1db676
Variable refresh (G-Sync/FreeSync, ProMotion): present each frame when it is ready (swap interval 0). Smooth motion targets the panel's maximum refresh at the current size (psx_sdl_display_max_refresh) and never exceeds it. PSX_HOST_REFRESH_HZ / PSX_HOST_REFRESH_MAX_HZ simulate a display for headless tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
305 lines
9.3 KiB
C
305 lines
9.3 KiB
C
#pragma once
|
|
|
|
/*
|
|
* Runtime-facing SDL compatibility seam.
|
|
*
|
|
* PSXRecomp uses SDL3 by default, while PSX_SDL_BACKEND=SDL2 keeps the
|
|
* previous implementation available for comparison and older platforms.
|
|
*/
|
|
#if defined(PSX_SDL3)
|
|
|
|
#define SDL_ENABLE_OLD_NAMES
|
|
#define SDL_FUNCTION_POINTER_IS_VOID_POINTER
|
|
#include <SDL3/SDL.h>
|
|
|
|
#ifndef SDL_WINDOW_SHOWN
|
|
#define SDL_WINDOW_SHOWN 0
|
|
#endif
|
|
#ifndef SDL_WINDOW_FULLSCREEN_DESKTOP
|
|
#define SDL_WINDOW_FULLSCREEN_DESKTOP SDL_WINDOW_FULLSCREEN
|
|
#endif
|
|
#ifndef SDL_RENDERER_ACCELERATED
|
|
#define SDL_RENDERER_ACCELERATED 0x00000001u
|
|
#endif
|
|
#ifndef SDL_RENDERER_PRESENTVSYNC
|
|
#define SDL_RENDERER_PRESENTVSYNC 0x00000002u
|
|
#endif
|
|
#ifndef SDL_RENDERER_SOFTWARE
|
|
#define SDL_RENDERER_SOFTWARE 0x00000004u
|
|
#endif
|
|
#ifndef SDL_MUTEX_TIMEDOUT
|
|
#define SDL_MUTEX_TIMEDOUT 1
|
|
#endif
|
|
#ifndef SDL_HINT_RENDER_SCALE_QUALITY
|
|
#define SDL_HINT_RENDER_SCALE_QUALITY "SDL_RENDER_SCALE_QUALITY"
|
|
#endif
|
|
|
|
static inline int psx_sdl_init(SDL_InitFlags flags)
|
|
{
|
|
return SDL_Init(flags) ? 0 : -1;
|
|
}
|
|
|
|
static inline int psx_sdl_init_subsystem(SDL_InitFlags flags)
|
|
{
|
|
return SDL_InitSubSystem(flags) ? 0 : -1;
|
|
}
|
|
|
|
static inline SDL_Window *psx_sdl_create_window(
|
|
const char *title, int x, int y, int w, int h, Uint64 flags)
|
|
{
|
|
(void)x;
|
|
(void)y;
|
|
return SDL_CreateWindow(title, w, h, (SDL_WindowFlags)flags);
|
|
}
|
|
|
|
static inline SDL_Renderer *psx_sdl_create_renderer(
|
|
SDL_Window *window, int driver_index, Uint32 flags)
|
|
{
|
|
(void)driver_index;
|
|
SDL_Renderer *renderer = SDL_CreateRenderer(window, NULL);
|
|
if (renderer && (flags & SDL_RENDERER_PRESENTVSYNC)) {
|
|
(void)SDL_SetRenderVSync(renderer, 1);
|
|
}
|
|
return renderer;
|
|
}
|
|
|
|
static inline int psx_sdl_render_set_logical_size(
|
|
SDL_Renderer *renderer, int w, int h)
|
|
{
|
|
return SDL_SetRenderLogicalPresentation(
|
|
renderer, w, h, SDL_LOGICAL_PRESENTATION_LETTERBOX)
|
|
? 0
|
|
: -1;
|
|
}
|
|
|
|
static inline int psx_sdl_render_copy(
|
|
SDL_Renderer *renderer, SDL_Texture *texture,
|
|
const SDL_Rect *src, const SDL_Rect *dst)
|
|
{
|
|
SDL_FRect srcf;
|
|
SDL_FRect dstf;
|
|
const SDL_FRect *srcp = NULL;
|
|
const SDL_FRect *dstp = NULL;
|
|
if (src) {
|
|
srcf.x = (float)src->x;
|
|
srcf.y = (float)src->y;
|
|
srcf.w = (float)src->w;
|
|
srcf.h = (float)src->h;
|
|
srcp = &srcf;
|
|
}
|
|
if (dst) {
|
|
dstf.x = (float)dst->x;
|
|
dstf.y = (float)dst->y;
|
|
dstf.w = (float)dst->w;
|
|
dstf.h = (float)dst->h;
|
|
dstp = &dstf;
|
|
}
|
|
return SDL_RenderTexture(renderer, texture, srcp, dstp) ? 0 : -1;
|
|
}
|
|
|
|
static inline int psx_sdl_render_set_vsync(
|
|
SDL_Renderer *renderer, int enabled)
|
|
{
|
|
return SDL_SetRenderVSync(renderer, enabled) ? 0 : -1;
|
|
}
|
|
|
|
static inline int psx_sdl_gl_make_current(
|
|
SDL_Window *window, SDL_GLContext context)
|
|
{
|
|
return SDL_GL_MakeCurrent(window, context) ? 0 : -1;
|
|
}
|
|
|
|
static inline int psx_sdl_gl_set_swap_interval(int interval)
|
|
{
|
|
return SDL_GL_SetSwapInterval(interval) ? 0 : -1;
|
|
}
|
|
|
|
static inline void psx_sdl_gl_get_drawable_size(
|
|
SDL_Window *window, int *w, int *h)
|
|
{
|
|
(void)SDL_GetWindowSizeInPixels(window, w, h);
|
|
}
|
|
|
|
static inline int psx_sdl_get_display_usable_bounds(
|
|
SDL_DisplayID display, SDL_Rect *bounds)
|
|
{
|
|
if (!display) display = SDL_GetPrimaryDisplay();
|
|
return SDL_GetDisplayUsableBounds(display, bounds) ? 0 : -1;
|
|
}
|
|
|
|
static inline int psx_sdl_get_current_display_mode(
|
|
SDL_DisplayID display, SDL_DisplayMode *mode)
|
|
{
|
|
if (!display) display = SDL_GetPrimaryDisplay();
|
|
const SDL_DisplayMode *current = SDL_GetCurrentDisplayMode(display);
|
|
if (!current) return -1;
|
|
*mode = *current;
|
|
return 0;
|
|
}
|
|
|
|
/* The highest refresh the display offers at the current mode's size (a VRR
|
|
* or ProMotion panel's maximum); 0 when unknown. */
|
|
static inline double psx_sdl_display_max_refresh(SDL_DisplayID display)
|
|
{
|
|
if (!display) display = SDL_GetPrimaryDisplay();
|
|
const SDL_DisplayMode *cur = SDL_GetCurrentDisplayMode(display);
|
|
double best = cur ? (double)cur->refresh_rate : 0.0;
|
|
int count = 0;
|
|
SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &count);
|
|
if (modes) {
|
|
for (int i = 0; i < count; ++i)
|
|
if (modes[i] && cur && modes[i]->w == cur->w && modes[i]->h == cur->h &&
|
|
(double)modes[i]->refresh_rate > best)
|
|
best = (double)modes[i]->refresh_rate;
|
|
SDL_free(modes);
|
|
}
|
|
return best;
|
|
}
|
|
|
|
/* Opt-in high-pixel-density game window. Without it, SDL3 on a Retina Mac
|
|
* gives the GL context a drawable in POINTS (half the panel resolution) and
|
|
* the compositor stretches it, so any internal resolution above that is
|
|
* averaged away. Only set when the player chose an internal resolution. */
|
|
#define PSX_SDL_WINDOW_HIGH_DENSITY SDL_WINDOW_HIGH_PIXEL_DENSITY
|
|
|
|
/* Pixel height of the display a window is on (the primary display when the
|
|
* window is NULL), for "Match display". 0 when unknown. */
|
|
static inline int psx_sdl_display_pixel_height(SDL_Window *window)
|
|
{
|
|
SDL_DisplayID display = window ? SDL_GetDisplayForWindow(window) : 0;
|
|
if (!display) display = SDL_GetPrimaryDisplay();
|
|
if (!display) return 0;
|
|
const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display);
|
|
if (!mode || mode->h <= 0) return 0;
|
|
float density = mode->pixel_density > 0.0f ? mode->pixel_density : 1.0f;
|
|
return (int)((float)mode->h * density + 0.5f);
|
|
}
|
|
|
|
static inline const Uint8 *psx_sdl_get_keyboard_state(int *count)
|
|
{
|
|
return (const Uint8 *)(const void *)SDL_GetKeyboardState(count);
|
|
}
|
|
|
|
static inline SDL_JoystickID psx_sdl_joystick_id_for_index(int index)
|
|
{
|
|
int count = 0;
|
|
SDL_JoystickID result = 0;
|
|
SDL_JoystickID *ids = SDL_GetJoysticks(&count);
|
|
if (ids && index >= 0 && index < count) result = ids[index];
|
|
SDL_free(ids);
|
|
return result;
|
|
}
|
|
|
|
static inline int psx_sdl_num_joysticks(void)
|
|
{
|
|
int count = 0;
|
|
SDL_JoystickID *ids = SDL_GetJoysticks(&count);
|
|
SDL_free(ids);
|
|
return count;
|
|
}
|
|
|
|
static inline bool psx_sdl_is_game_controller(int index)
|
|
{
|
|
SDL_JoystickID id = psx_sdl_joystick_id_for_index(index);
|
|
return id && SDL_IsGamepad(id);
|
|
}
|
|
|
|
static inline SDL_Gamepad *psx_sdl_game_controller_open(int index)
|
|
{
|
|
SDL_JoystickID id = psx_sdl_joystick_id_for_index(index);
|
|
return id ? SDL_OpenGamepad(id) : NULL;
|
|
}
|
|
|
|
static inline SDL_GUID psx_sdl_joystick_get_device_guid(int index)
|
|
{
|
|
return SDL_GetJoystickGUIDForID(psx_sdl_joystick_id_for_index(index));
|
|
}
|
|
|
|
static inline SDL_JoystickID psx_sdl_joystick_get_device_instance_id(int index)
|
|
{
|
|
return psx_sdl_joystick_id_for_index(index);
|
|
}
|
|
|
|
static inline void psx_sdl_joystick_get_guid_string(
|
|
SDL_GUID guid, char *buffer, int buffer_size)
|
|
{
|
|
SDL_GUIDToString(guid, buffer, buffer_size);
|
|
}
|
|
|
|
static inline int psx_sdl_cond_wait_timeout(
|
|
SDL_Condition *condition, SDL_Mutex *mutex, Sint32 timeout_ms)
|
|
{
|
|
return SDL_WaitConditionTimeout(condition, mutex, timeout_ms)
|
|
? 0
|
|
: SDL_MUTEX_TIMEDOUT;
|
|
}
|
|
|
|
#undef SDL_Init
|
|
#define SDL_Init psx_sdl_init
|
|
#undef SDL_InitSubSystem
|
|
#define SDL_InitSubSystem psx_sdl_init_subsystem
|
|
#undef SDL_CreateWindow
|
|
#define SDL_CreateWindow psx_sdl_create_window
|
|
#undef SDL_CreateRenderer
|
|
#define SDL_CreateRenderer psx_sdl_create_renderer
|
|
#undef SDL_RenderSetLogicalSize
|
|
#define SDL_RenderSetLogicalSize psx_sdl_render_set_logical_size
|
|
#undef SDL_RenderCopy
|
|
#define SDL_RenderCopy psx_sdl_render_copy
|
|
#undef SDL_RenderSetVSync
|
|
#define SDL_RenderSetVSync psx_sdl_render_set_vsync
|
|
#undef SDL_GL_MakeCurrent
|
|
#define SDL_GL_MakeCurrent psx_sdl_gl_make_current
|
|
#undef SDL_GL_SetSwapInterval
|
|
#define SDL_GL_SetSwapInterval psx_sdl_gl_set_swap_interval
|
|
#define SDL_GL_GetDrawableSize psx_sdl_gl_get_drawable_size
|
|
#undef SDL_GetDisplayUsableBounds
|
|
#define SDL_GetDisplayUsableBounds psx_sdl_get_display_usable_bounds
|
|
#undef SDL_GetCurrentDisplayMode
|
|
#define SDL_GetCurrentDisplayMode psx_sdl_get_current_display_mode
|
|
#undef SDL_GetKeyboardState
|
|
#define SDL_GetKeyboardState psx_sdl_get_keyboard_state
|
|
#define SDL_NumJoysticks psx_sdl_num_joysticks
|
|
#undef SDL_IsGameController
|
|
#define SDL_IsGameController psx_sdl_is_game_controller
|
|
#undef SDL_GameControllerOpen
|
|
#define SDL_GameControllerOpen psx_sdl_game_controller_open
|
|
#undef SDL_JoystickGetDeviceGUID
|
|
#define SDL_JoystickGetDeviceGUID psx_sdl_joystick_get_device_guid
|
|
#undef SDL_JoystickGetDeviceInstanceID
|
|
#define SDL_JoystickGetDeviceInstanceID psx_sdl_joystick_get_device_instance_id
|
|
#undef SDL_JoystickGetGUIDString
|
|
#define SDL_JoystickGetGUIDString psx_sdl_joystick_get_guid_string
|
|
#undef SDL_CondWaitTimeout
|
|
#define SDL_CondWaitTimeout psx_sdl_cond_wait_timeout
|
|
|
|
#else
|
|
|
|
#include <SDL.h>
|
|
|
|
#define PSX_SDL_WINDOW_HIGH_DENSITY SDL_WINDOW_ALLOW_HIGHDPI
|
|
|
|
static inline int psx_sdl_display_pixel_height(SDL_Window *window)
|
|
{
|
|
int index = window ? SDL_GetWindowDisplayIndex(window) : 0;
|
|
SDL_DisplayMode mode;
|
|
if (index < 0) index = 0;
|
|
if (SDL_GetCurrentDisplayMode(index, &mode) != 0 || mode.h <= 0) return 0;
|
|
return mode.h;
|
|
}
|
|
|
|
static inline double psx_sdl_display_max_refresh(int display)
|
|
{
|
|
SDL_DisplayMode cur, m;
|
|
if (display < 0) display = 0;
|
|
if (SDL_GetCurrentDisplayMode(display, &cur) != 0) return 0.0;
|
|
double best = (double)cur.refresh_rate;
|
|
const int n = SDL_GetNumDisplayModes(display);
|
|
for (int i = 0; i < n; ++i)
|
|
if (SDL_GetDisplayMode(display, i, &m) == 0 && m.w == cur.w && m.h == cur.h &&
|
|
(double)m.refresh_rate > best)
|
|
best = (double)m.refresh_rate;
|
|
return best;
|
|
}
|
|
#endif
|