mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 02:56:36 -04:00
f34f91cc25
Add a player-facing internal resolution on top of the raised GL ceiling. A preset is a target height; the runtime renders at the integer scale S = ceil(target / reference_lines), reference 240 by default: 720p 3x, 1080p 5x (area-resolved to 1080), 1440p 6x, 4K 9x, 5K 12x, 8K 18x. The backend then clamps S to what it can allocate. - runtime/include/internal_resolution.h: the preset table, stable ids, the value encoding shared with recomp-ui (0 unset, 1 native, -1 match display, N target lines) and the pure resolver. - config: [video] internal_resolution (a preset id or a number of lines) and resolution_reference_lines in game.toml; internal_resolution in settings.toml, written as a stable id beside supersampling = min(S, 4) for older runtimes. Precedence: game default < player's legacy factor < player's preset. settings.toml window_width now accepts up to 7680. - main.cpp: resolve the preset before gpu_init; Match display re-resolves from the game window's monitor (SDL3 mode height x pixel density) before the GL context allocates. The launcher gets the vocabulary (with a synthesized "Nx (L lines)" entry for a legacy factor no preset matches) behind RECOMP_LAUNCHER_HAS_INTERNAL_RESOLUTION, on first boot and on the netplay soft-return. - The OpenGL game window gets SDL_WINDOW_HIGH_PIXEL_DENSITY (SDL2: ALLOW_HIGHDPI) only when the player chose more than native, so the drawable is in pixels on Retina; native keeps the window as it was. - debug server: video_info (preset, requested and effective scale, GL limit and clamp, hr surface, window points vs pixels), screenshot_hires reads the GL hr FBO at full size, screenshot_wide_hires captures the native-wide band at internal resolution. - docs: config_schema (keys, precedence), NETPLAY (a per-peer presentation setting), TCP_COMMANDS (index regenerated), ENHANCEMENTS IR1. Default-off: with no internal_resolution anywhere the scale, the window flags and the present path are unchanged. Tests: internal_resolution_test (presets x reference 240/480 x backend maxima, legacy mapping round trip), video_enhancement_settings_test (game and settings keys, stable-id round trip, legacy key still written), gl_internal_scale_guards (HiDPI only when opted in, launcher fields behind the capability macro). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
273 lines
8.1 KiB
C
273 lines
8.1 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#endif
|