mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
928d1a0810 | ||
|
|
6919bc9cd7 | ||
|
|
a8c5b1815e | ||
|
|
cf6aab7ad2 | ||
|
|
f0cce58206 | ||
|
|
07ac0685e2 | ||
|
|
e3e1d87705 | ||
|
|
05a483cbe6 | ||
|
|
2c5cde4125 | ||
|
|
5f8bbcd1b6 | ||
|
|
e81d65d51b | ||
|
|
193133d616 | ||
|
|
d2cf7a113b | ||
|
|
4e07070e86 | ||
|
|
4562153839 |
No files matched your search
@@ -1671,6 +1671,14 @@ ifeq ($(HAVE_SDL3), 1)
|
||||
input/drivers/sdl3_input.o \
|
||||
gfx/drivers/sdl3_gfx.o \
|
||||
gfx/common/sdl3_common.o
|
||||
|
||||
ifneq ($(filter 1,$(HAVE_OPENGL) $(HAVE_OPENGL1) $(HAVE_OPENGL_CORE) $(HAVE_OPENGLES)),)
|
||||
OBJ += gfx/drivers_context/sdl3_gl_ctx.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_VULKAN), 1)
|
||||
OBJ += gfx/drivers_context/sdl3_vk_ctx.o
|
||||
endif
|
||||
else ifeq ($(HAVE_SDL2), 1)
|
||||
HAVE_SDL_COMMON = 1
|
||||
OBJ += gfx/drivers/sdl2_gfx.o \
|
||||
|
||||
+363
-10
@@ -27,20 +27,73 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "sdl3_common.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../input/input_driver.h"
|
||||
#include "../../retroarch.h"
|
||||
|
||||
/* SDL3 dropped SDL_GetWindowWMInfo / SDL_SysWMinfo in favour of the
|
||||
* window properties API, so the native display/window handles are read
|
||||
* from SDL_GetWindowProperties() instead.
|
||||
*
|
||||
* The display type is derived from SDL's active video driver name, so
|
||||
* no platform #ifdefs are needed at the call site; each case below
|
||||
* compiles away when the corresponding backend isn't built in, making
|
||||
* unsupported types safe no-ops. */
|
||||
/* Keeps track of the window position to allow video_window_save_positions. */
|
||||
static void sdl3_window_save_position(SDL_Window *win)
|
||||
{
|
||||
int x, y, w, h;
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (!win || !settings || !settings->bools.video_window_save_positions)
|
||||
return;
|
||||
|
||||
if (SDL_GetWindowFlags(win) & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))
|
||||
return;
|
||||
|
||||
if (!SDL_GetWindowPosition(win, &x, &y) || !SDL_GetWindowSize(win, &w, &h)
|
||||
|| w <= 0 || h <= 0)
|
||||
return;
|
||||
|
||||
settings->uints.window_position_x = (unsigned)x;
|
||||
settings->uints.window_position_y = (unsigned)y;
|
||||
settings->uints.window_position_width = (unsigned)w;
|
||||
settings->uints.window_position_height = (unsigned)h;
|
||||
}
|
||||
|
||||
void sdl3_pump_window_events(bool *quit, bool *resize)
|
||||
{
|
||||
SDL_Event event;
|
||||
|
||||
SDL_PumpEvents();
|
||||
|
||||
/* Only consume quit and window events here. The SDL3 input driver
|
||||
* handles the keyboard/mouse events within the same queue. */
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_QUIT, SDL_EVENT_QUIT) > 0)
|
||||
*quit = true;
|
||||
|
||||
/* Check if the display size was changed. */
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST) > 0)
|
||||
{
|
||||
if (event.type == SDL_EVENT_WINDOW_RESIZED || event.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED)
|
||||
*resize = true;
|
||||
|
||||
if (event.type == SDL_EVENT_WINDOW_RESIZED || event.type == SDL_EVENT_WINDOW_MOVED)
|
||||
sdl3_window_save_position(SDL_GetWindowFromID(event.window.windowID));
|
||||
}
|
||||
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_DISPLAY_FIRST, SDL_EVENT_DISPLAY_LAST) > 0)
|
||||
{
|
||||
if (event.type == SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED)
|
||||
*resize = true;
|
||||
}
|
||||
|
||||
/* Clear out the input queue if we're not using the
|
||||
* SDL driver. */
|
||||
if (input_state_get_ptr()->current_driver != &input_sdl3)
|
||||
{
|
||||
SDL_FlushEvents(SDL_EVENT_KEY_DOWN, SDL_EVENT_MOUSE_REMOVED);
|
||||
SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED);
|
||||
SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS);
|
||||
}
|
||||
}
|
||||
|
||||
void sdl3_set_handles(SDL_Window *window)
|
||||
{
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
const char *sdl_driver = SDL_GetCurrentVideoDriver();
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
const char *sdl_driver = SDL_GetCurrentVideoDriver();
|
||||
enum rarch_display_type display_type = RARCH_DISPLAY_NONE;
|
||||
|
||||
if (!props)
|
||||
@@ -110,3 +163,303 @@ void sdl3_set_handles(SDL_Window *window)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Moves the window to the provided 1-based monitor. If 0
|
||||
* is provided, leave as is. */
|
||||
static void sdl3_window_move_to_monitor(SDL_Window *win,
|
||||
unsigned monitor_index)
|
||||
{
|
||||
int count;
|
||||
SDL_DisplayID *displays;
|
||||
SDL_DisplayID display = 0;
|
||||
|
||||
if (!win || monitor_index == 0)
|
||||
return;
|
||||
|
||||
displays = SDL_GetDisplays(&count);
|
||||
if (displays && (int)monitor_index <= count)
|
||||
display = displays[monitor_index - 1];
|
||||
SDL_free(displays);
|
||||
|
||||
/* Out-of-range index (e.g. the configured monitor was unplugged):
|
||||
* leave the window where the system placed it. */
|
||||
if (display == 0)
|
||||
return;
|
||||
|
||||
SDL_SetWindowPosition(win,
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
SDL_SyncWindow(win);
|
||||
}
|
||||
|
||||
/* Creates the SDL3 window. */
|
||||
static SDL_Window *sdl3_window_create(unsigned width, unsigned height,
|
||||
bool fullscreen, SDL_WindowFlags backend_flags)
|
||||
{
|
||||
SDL_Window *win;
|
||||
settings_t *settings = config_get_ptr();
|
||||
SDL_WindowFlags flags = backend_flags
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
|
||||
if (fullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
|
||||
if (!settings->bools.video_window_show_decorations)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
if (!(win = SDL_CreateWindow("RetroArch", width, height, flags)))
|
||||
return NULL;
|
||||
|
||||
/* Set either the window position, or the active monitor. This is ignored
|
||||
* in Wayland where windows are not manually positioned. */
|
||||
if (settings->bools.video_window_save_positions
|
||||
&& !fullscreen
|
||||
&& settings->uints.window_position_width
|
||||
&& settings->uints.window_position_height
|
||||
&& !string_is_equal(SDL_GetCurrentVideoDriver(), "wayland"))
|
||||
{
|
||||
SDL_SetWindowPosition(win,
|
||||
(int)settings->uints.window_position_x,
|
||||
(int)settings->uints.window_position_y);
|
||||
SDL_SyncWindow(win);
|
||||
}
|
||||
else
|
||||
sdl3_window_move_to_monitor(win, settings->uints.video_monitor_index);
|
||||
|
||||
/* SDL_EVENT_TEXT_INPUT is emitted for windows that opted in for
|
||||
* it. The SDL3 input driver handles those events for menu
|
||||
* text entry and core keyboard callbacks. */
|
||||
SDL_StartTextInput(win);
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
/* Applies the fullscreen state. */
|
||||
static void sdl3_window_apply_fullscreen(SDL_Window *win,
|
||||
unsigned width, unsigned height, bool fullscreen)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool exclusive = fullscreen
|
||||
&& !settings->bools.video_windowed_fullscreen
|
||||
&& SDL_GetClosestFullscreenDisplayMode(
|
||||
SDL_GetDisplayForWindow(win),
|
||||
width, height,
|
||||
settings->floats.video_refresh_rate, false, &mode);
|
||||
|
||||
SDL_SetWindowFullscreenMode(win, exclusive ? &mode : NULL);
|
||||
SDL_SetWindowFullscreen(win, fullscreen);
|
||||
|
||||
if (fullscreen)
|
||||
SDL_HideCursor();
|
||||
else
|
||||
{
|
||||
/* Leaving fullscreen is asynchronous, and size requests
|
||||
* are dropped while still fullscreen, so we wait until
|
||||
* it has completed. */
|
||||
SDL_SyncWindow(win);
|
||||
SDL_SetWindowSize(win, width, height);
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
bool sdl3_window_set_video_mode(SDL_Window **win,
|
||||
unsigned width, unsigned height, bool fullscreen,
|
||||
SDL_WindowFlags backend_flags)
|
||||
{
|
||||
if (*win)
|
||||
SDL_SetWindowBordered(*win, config_get_ptr()->bools.video_window_show_decorations);
|
||||
else if (!(*win = sdl3_window_create(width, height, fullscreen, backend_flags)))
|
||||
return false;
|
||||
|
||||
sdl3_window_apply_fullscreen(*win, width, height, fullscreen);
|
||||
|
||||
sdl3_set_handles(*win);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sdl3_window_get_video_size(SDL_Window *win,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
const SDL_DisplayMode *mode;
|
||||
|
||||
if (win)
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSizeInPixels(win, &w, &h);
|
||||
*width = w;
|
||||
*height = h;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The window doesn't exist yet, so report the desktop size. */
|
||||
mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
|
||||
if (mode)
|
||||
{
|
||||
*width = mode->w;
|
||||
*height = mode->h;
|
||||
}
|
||||
}
|
||||
|
||||
float sdl3_window_get_refresh_rate(SDL_Window *win)
|
||||
{
|
||||
const SDL_DisplayMode *mode;
|
||||
|
||||
if (!win)
|
||||
return 0.0f;
|
||||
|
||||
mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(win));
|
||||
return mode ? mode->refresh_rate : 0.0f;
|
||||
}
|
||||
|
||||
void sdl3_window_update_title(SDL_Window *win)
|
||||
{
|
||||
char title[128];
|
||||
|
||||
if (!win)
|
||||
return;
|
||||
|
||||
title[0] = '\0';
|
||||
video_driver_get_window_title(title, sizeof(title));
|
||||
|
||||
if (title[0])
|
||||
SDL_SetWindowTitle(win, title);
|
||||
}
|
||||
|
||||
bool sdl3_window_has_focus(SDL_Window *win)
|
||||
{
|
||||
if (!win)
|
||||
return false;
|
||||
/* This only checks for keyboard focus, as SDL_WINDOW_MOUSE_FOCUS will
|
||||
* pause when the mouse leaves the window too. */
|
||||
return (SDL_GetWindowFlags(win) & SDL_WINDOW_INPUT_FOCUS) != 0;
|
||||
}
|
||||
|
||||
bool sdl3_suppress_screensaver(void *data, bool enable)
|
||||
{
|
||||
return enable ? SDL_DisableScreenSaver() : SDL_EnableScreenSaver();
|
||||
}
|
||||
|
||||
/* Returns the configured input driver if it works alongside an SDL3
|
||||
* window, or NULL if the SDL3 input driver should be used instead.
|
||||
*
|
||||
* The udev, linuxraw, raw and dinput input drivers read input devices
|
||||
* directly, so they will function inside an SDL3 window. Drivers tied
|
||||
* to a windowing system like x, wayland, or cocoa however, should end
|
||||
* up using the SDL3 input driver. */
|
||||
static input_driver_t *sdl3_passthrough_input(const char *ident)
|
||||
{
|
||||
#ifdef HAVE_UDEV
|
||||
if (string_is_equal(ident, "udev"))
|
||||
return &input_udev;
|
||||
#endif
|
||||
#if defined(__linux__) && !defined(ANDROID)
|
||||
if (string_is_equal(ident, "linuxraw"))
|
||||
return &input_linuxraw;
|
||||
#endif
|
||||
#if defined(_WIN32) && !defined(_XBOX) && _WIN32_WINNT >= 0x0501 && !defined(__WINRT__)
|
||||
#ifdef HAVE_WINRAWINPUT
|
||||
if (string_is_equal(ident, "raw"))
|
||||
return &input_winraw;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_DINPUT
|
||||
if (string_is_equal(ident, "dinput"))
|
||||
return &input_dinput;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sdl3_input_driver(const char *joypad_name,
|
||||
input_driver_t **input, void **input_data)
|
||||
{
|
||||
input_driver_t *passthrough = sdl3_passthrough_input(config_get_ptr()->arrays.input_driver);
|
||||
|
||||
if (passthrough)
|
||||
{
|
||||
if ((*input_data = input_driver_init_wrap(passthrough, joypad_name)))
|
||||
{
|
||||
*input = passthrough;
|
||||
return;
|
||||
}
|
||||
/* The configured input driver failed, so fallback to SDL3. */
|
||||
}
|
||||
|
||||
/* Use the SDL3 input driver, taking advantage of the window's event queue. */
|
||||
*input_data = input_driver_init_wrap(&input_sdl3, joypad_name);
|
||||
*input = *input_data ? &input_sdl3 : NULL;
|
||||
}
|
||||
|
||||
void sdl3_ctx_input_driver(void *data,
|
||||
const char *name,
|
||||
input_driver_t **input, void **input_data)
|
||||
{
|
||||
sdl3_input_driver(name, input, input_data);
|
||||
}
|
||||
|
||||
bool sdl3_ctx_enabled(const char *ctx_ident)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
|
||||
/* Determine the user's desired video driver. When a core requests
|
||||
* gl/glcore/vulkan, the user's original selection is stashed in
|
||||
* cached_driver_id. Use that if it's available. */
|
||||
const char *video_ident = video_st->cached_driver_id[0]
|
||||
? video_st->cached_driver_id
|
||||
: settings->arrays.video_driver;
|
||||
|
||||
/* Keep the hardware rendering within the SDL3 window. When SDL3 isn't
|
||||
* selected, will pass off to the next native context instead. */
|
||||
return string_is_equal(video_ident, "sdl3")
|
||||
|| string_is_equal(settings->arrays.video_context_driver, ctx_ident);
|
||||
}
|
||||
|
||||
/* Shared ctx callbacks only need the SDL_Window*, which
|
||||
* every SDL3 driver data struct keeps as its first member. */
|
||||
static SDL_Window *sdl3_ctx_window(void *data)
|
||||
{
|
||||
return data ? *(SDL_Window**)data : NULL;
|
||||
}
|
||||
|
||||
void sdl3_ctx_get_video_size(void *data, unsigned *width, unsigned *height)
|
||||
{
|
||||
if (data)
|
||||
sdl3_window_get_video_size(sdl3_ctx_window(data), width, height);
|
||||
}
|
||||
|
||||
float sdl3_ctx_get_refresh_rate(void *data)
|
||||
{
|
||||
return sdl3_window_get_refresh_rate(sdl3_ctx_window(data));
|
||||
}
|
||||
|
||||
void sdl3_ctx_update_title(void *data)
|
||||
{
|
||||
sdl3_window_update_title(sdl3_ctx_window(data));
|
||||
}
|
||||
|
||||
bool sdl3_ctx_has_focus(void *data)
|
||||
{
|
||||
return sdl3_window_has_focus(sdl3_ctx_window(data));
|
||||
}
|
||||
|
||||
void sdl3_ctx_check_window(void *data, bool *quit, bool *resize,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
SDL_Window *win = sdl3_ctx_window(data);
|
||||
|
||||
sdl3_pump_window_events(quit, resize);
|
||||
|
||||
if (*resize && win)
|
||||
sdl3_window_get_video_size(win, width, height);
|
||||
}
|
||||
|
||||
void sdl3_show_mouse(void *data, bool state)
|
||||
{
|
||||
if (state)
|
||||
SDL_ShowCursor();
|
||||
else
|
||||
SDL_HideCursor();
|
||||
}
|
||||
@@ -44,6 +44,8 @@ typedef struct sdl3_tex
|
||||
|
||||
typedef struct _sdl3_video
|
||||
{
|
||||
SDL_Window *window; /* Must be first because it's shared across
|
||||
* sdl3_ctx_* callbacks. */
|
||||
double rotation;
|
||||
|
||||
struct video_viewport vp;
|
||||
@@ -52,12 +54,68 @@ typedef struct _sdl3_video
|
||||
sdl3_tex_t frame; /* ptr alignment */
|
||||
sdl3_tex_t menu; /* ptr alignment */
|
||||
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
|
||||
uint8_t flags;
|
||||
} sdl3_video_t;
|
||||
|
||||
/* Sets the window's native display/window handles (X11, Wayland,
|
||||
* Win32, Cocoa, KMS) to the video driver state via SDL's window
|
||||
* properties API, so subsystems that require the raw handles
|
||||
* can get access to them. */
|
||||
void sdl3_set_handles(SDL_Window *window);
|
||||
|
||||
/* Processes the SDL event queue, and cleans up the ones that
|
||||
* are handled elsewhere. For example: quit / window / display
|
||||
* events, etc. The keyboard/mouse events are left in the queue
|
||||
* for the SDL input driver to handle itself. */
|
||||
void sdl3_pump_window_events(bool *quit, bool *resize);
|
||||
|
||||
/* Creates or resizes the window, or toggles fullscreen. */
|
||||
bool sdl3_window_set_video_mode(SDL_Window **win,
|
||||
unsigned width, unsigned height, bool fullscreen,
|
||||
SDL_WindowFlags backend_flags);
|
||||
|
||||
/* Retrieves the window size in pixels, or the desktop mode when the
|
||||
* window doesn't exist yet. */
|
||||
void sdl3_window_get_video_size(SDL_Window *win,
|
||||
unsigned *width, unsigned *height);
|
||||
|
||||
/* Get the refresh rate of the display the window is on, in Hz.
|
||||
* Returns 0.0f when there is no window or when we can't tell. */
|
||||
float sdl3_window_get_refresh_rate(SDL_Window *win);
|
||||
|
||||
/* Updates the window title to what is expected. */
|
||||
void sdl3_window_update_title(SDL_Window *win);
|
||||
|
||||
/* True when the window has keyboard input focus. */
|
||||
bool sdl3_window_has_focus(SDL_Window *win);
|
||||
|
||||
/* Toggles screensaver inhibition. */
|
||||
bool sdl3_suppress_screensaver(void *data, bool enable);
|
||||
|
||||
/* Initializes the input driver paired with an SDL3 window. */
|
||||
void sdl3_input_driver(const char *joypad_name,
|
||||
input_driver_t **input, void **input_data);
|
||||
|
||||
/* gfx_ctx_driver_t input_driver callback wrapping sdl3_input_driver. */
|
||||
void sdl3_ctx_input_driver(void *data, const char *name,
|
||||
input_driver_t **input, void **input_data);
|
||||
|
||||
/* Determines whether or not an SDL3 context driver should be
|
||||
* initialized. Will return true when the user is using the
|
||||
* sdl3 video driver, or when ctx_ident is explicitly configured
|
||||
* as the context driver. */
|
||||
bool sdl3_ctx_enabled(const char *ctx_ident);
|
||||
|
||||
void sdl3_ctx_get_video_size(void *data, unsigned *width, unsigned *height);
|
||||
float sdl3_ctx_get_refresh_rate(void *data);
|
||||
void sdl3_ctx_update_title(void *data);
|
||||
bool sdl3_ctx_has_focus(void *data);
|
||||
void sdl3_ctx_check_window(void *data, bool *quit, bool *resize,
|
||||
unsigned *width, unsigned *height);
|
||||
|
||||
/* Shows or hides the mouse cursor. */
|
||||
void sdl3_show_mouse(void *data, bool state);
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,13 @@
|
||||
#include "vksym.h"
|
||||
#include <libretro_vulkan.h>
|
||||
|
||||
#ifdef HAVE_SDL3
|
||||
/* Must come after the Vulkan headers so SDL_vulkan.h picks up the
|
||||
* real Vk* types instead of forward-declaring its own. */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#endif
|
||||
|
||||
#include "../../verbosity.h"
|
||||
#include "../../configuration.h"
|
||||
|
||||
@@ -1071,12 +1078,13 @@ static VkInstance vulkan_context_create_instance_wrapper(void *opaque, const VkI
|
||||
gfx_ctx_vulkan_data_t *vk = (gfx_ctx_vulkan_data_t *)opaque;
|
||||
VkInstanceCreateInfo info = *create_info;
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
const char **instance_extensions = (const char**)malloc((info.enabledExtensionCount + 3
|
||||
+ ARRAY_SIZE(vulkan_optional_device_extensions)) * sizeof(const char *));
|
||||
const char **instance_layers = (const char**)malloc((info.enabledLayerCount + 1) * sizeof(const char *));
|
||||
|
||||
const char *required_extensions[3];
|
||||
/* Room for VK_KHR_surface, WSI, SDL3, and the debug extension. */
|
||||
const char *required_extensions[16];
|
||||
uint32_t required_extension_count = 0;
|
||||
const char **instance_extensions = (const char**)malloc((info.enabledExtensionCount
|
||||
+ ARRAY_SIZE(required_extensions)
|
||||
+ ARRAY_SIZE(vulkan_optional_instance_extensions)) * sizeof(const char *));
|
||||
const char **instance_layers = (const char**)malloc((info.enabledLayerCount + 1) * sizeof(const char *));
|
||||
|
||||
/* Both mallocs must have succeeded before the memcpy / field
|
||||
* assignments below dereference the buffers. The 'end' label
|
||||
@@ -1129,6 +1137,24 @@ static VkInstance vulkan_context_create_instance_wrapper(void *opaque, const VkI
|
||||
case VULKAN_WSI_MVK_IOS:
|
||||
required_extensions[required_extension_count++] = "VK_EXT_metal_surface";
|
||||
break;
|
||||
#ifdef HAVE_SDL3
|
||||
case VULKAN_WSI_SDL3:
|
||||
{
|
||||
Uint32 sdl_ext_count = 0;
|
||||
const char * const *sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_ext_count);
|
||||
for (i = 0; sdl_extensions && i < sdl_ext_count; i++)
|
||||
{
|
||||
/* VK_KHR_surface already added above. */
|
||||
if (string_is_equal(sdl_extensions[i], "VK_KHR_surface"))
|
||||
continue;
|
||||
if (required_extension_count < ARRAY_SIZE(required_extensions))
|
||||
required_extensions[required_extension_count++] = sdl_extensions[i];
|
||||
else
|
||||
RARCH_WARN("[Vulkan] Dropping SDL3 instance extension \"%s\": list full.\n", sdl_extensions[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case VULKAN_WSI_NONE:
|
||||
default:
|
||||
break;
|
||||
@@ -1136,7 +1162,10 @@ static VkInstance vulkan_context_create_instance_wrapper(void *opaque, const VkI
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
instance_layers[info.enabledLayerCount++] = "VK_LAYER_KHRONOS_validation";
|
||||
required_extensions[required_extension_count++] = "VK_EXT_debug_utils";
|
||||
if (required_extension_count < ARRAY_SIZE(required_extensions))
|
||||
required_extensions[required_extension_count++] = "VK_EXT_debug_utils";
|
||||
else
|
||||
RARCH_WARN("[Vulkan] Dropping VK_EXT_debug_utils: extension list full.\n");
|
||||
#endif
|
||||
|
||||
if (!(vulkan_find_instance_extensions(
|
||||
@@ -1788,6 +1817,18 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
|
||||
!= VK_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case VULKAN_WSI_SDL3:
|
||||
#ifdef HAVE_SDL3
|
||||
/* The SDL3 context driver passes its SDL_Window through the
|
||||
* 'surface' parameter; SDL picks the platform surface path. */
|
||||
if (!SDL_Vulkan_CreateSurface((SDL_Window*)surface, vk->context.instance, NULL, &vk->vk_surface))
|
||||
{
|
||||
RARCH_ERR("[Vulkan] Failed to create SDL3 surface: %s.\n",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case VULKAN_WSI_NONE:
|
||||
@@ -2625,7 +2666,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
|
||||
/* TODO/FIXME:
|
||||
* Weird shenanigans necessary for Apple otherwise the following happens:
|
||||
* The menu sometimes refuses to display, but still responds to input.
|
||||
* The menu sometimes refuses to display, but still responds to input.
|
||||
* This happens about 1/5 times on macOS but 100% in the quick menu on iOS
|
||||
*/
|
||||
#ifdef __APPLE__
|
||||
|
||||
@@ -153,6 +153,7 @@ enum vulkan_wsi_type
|
||||
VULKAN_WSI_DISPLAY,
|
||||
VULKAN_WSI_MVK_MACOS,
|
||||
VULKAN_WSI_MVK_IOS,
|
||||
VULKAN_WSI_SDL3,
|
||||
};
|
||||
|
||||
enum vulkan_context_flags
|
||||
|
||||
+20
-93
@@ -26,10 +26,6 @@
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_X11
|
||||
#include "../common/x11_common.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../common/win32_common.h"
|
||||
#endif
|
||||
@@ -225,13 +221,8 @@ static void *sdl3_gfx_init(const video_info_t *video,
|
||||
input_driver_t **input, void **input_data)
|
||||
{
|
||||
int i;
|
||||
SDL_WindowFlags flags;
|
||||
sdl3_video_t *vid = NULL;
|
||||
|
||||
#ifdef HAVE_X11
|
||||
XInitThreads();
|
||||
#endif
|
||||
|
||||
/* Initialize the video system. */
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
|
||||
return NULL;
|
||||
@@ -250,16 +241,12 @@ static void *sdl3_gfx_init(const video_info_t *video,
|
||||
for (i = 0; i < SDL_GetNumRenderDrivers(); ++i)
|
||||
RARCH_LOG("[SDL3] \t%s\n", SDL_GetRenderDriver(i));
|
||||
|
||||
if (video->fullscreen)
|
||||
flags = SDL_WINDOW_FULLSCREEN;
|
||||
else
|
||||
{
|
||||
if (!video->fullscreen)
|
||||
RARCH_LOG("[SDL3] Creating window @ %ux%u.\n", video->width, video->height);
|
||||
flags = SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
|
||||
vid->window = SDL_CreateWindow("", video->width, video->height, flags);
|
||||
if (!vid->window)
|
||||
/* No backend flag: SDL_CreateRenderer picks the render driver. */
|
||||
if (!sdl3_window_set_video_mode(&vid->window,
|
||||
video->width, video->height, video->fullscreen, 0))
|
||||
{
|
||||
RARCH_ERR("[SDL3] Failed to init SDL window: %s.\n", SDL_GetError());
|
||||
goto error;
|
||||
@@ -268,25 +255,12 @@ static void *sdl3_gfx_init(const video_info_t *video,
|
||||
vid->video = *video;
|
||||
vid->flags |= SDL3_FLAG_SHOULD_RESIZE;
|
||||
|
||||
/* SDL3 only emits SDL_EVENT_TEXT_INPUT for windows that opted in;
|
||||
* the SDL3 input driver forwards those events for menu text entry
|
||||
* and core keyboard callbacks. Desktop platforms don't pop an
|
||||
* on-screen keyboard for this. */
|
||||
SDL_StartTextInput(vid->window);
|
||||
|
||||
if (video->fullscreen)
|
||||
SDL_HideCursor();
|
||||
|
||||
if (!sdl3_init_renderer(vid))
|
||||
goto error;
|
||||
|
||||
sdl3_set_handles(vid->window);
|
||||
|
||||
sdl3_refresh_viewport(vid);
|
||||
|
||||
/* The frontend selects the input driver separately. */
|
||||
*input = NULL;
|
||||
*input_data = NULL;
|
||||
sdl3_input_driver(config_get_ptr()->arrays.input_joypad_driver, input, input_data);
|
||||
|
||||
return vid;
|
||||
|
||||
@@ -297,28 +271,15 @@ error:
|
||||
|
||||
static void sdl3_check_window(sdl3_video_t *vid)
|
||||
{
|
||||
SDL_Event event;
|
||||
bool quit = false;
|
||||
bool resize = false;
|
||||
|
||||
SDL_PumpEvents();
|
||||
sdl3_pump_window_events(&quit, &resize);
|
||||
|
||||
/* Only consume quit + window events here; the SDL3 input driver
|
||||
* drains keyboard/mouse events from the same queue. */
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_QUIT, SDL_EVENT_QUIT) > 0)
|
||||
if (quit)
|
||||
vid->flags |= SDL3_FLAG_QUITTING;
|
||||
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST) > 0)
|
||||
{
|
||||
if (event.type == SDL_EVENT_WINDOW_RESIZED || event.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED)
|
||||
vid->flags |= SDL3_FLAG_SHOULD_RESIZE;
|
||||
}
|
||||
|
||||
/* Handle the window scale event here. */
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT,
|
||||
SDL_EVENT_DISPLAY_FIRST, SDL_EVENT_DISPLAY_LAST) > 0)
|
||||
{
|
||||
if (event.type == SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED)
|
||||
vid->flags |= SDL3_FLAG_SHOULD_RESIZE;
|
||||
}
|
||||
if (resize)
|
||||
vid->flags |= SDL3_FLAG_SHOULD_RESIZE;
|
||||
}
|
||||
|
||||
/* Menu, statistics and widget passes compute their coordinates
|
||||
@@ -507,7 +468,6 @@ static bool sdl3_gfx_frame(void *data, const void *frame, unsigned width,
|
||||
unsigned height, uint64_t frame_count,
|
||||
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
||||
{
|
||||
char title[128];
|
||||
sdl3_video_t *vid = (sdl3_video_t*)data;
|
||||
|
||||
if (vid->flags & SDL3_FLAG_SHOULD_RESIZE)
|
||||
@@ -529,10 +489,7 @@ static bool sdl3_gfx_frame(void *data, const void *frame, unsigned width,
|
||||
|
||||
SDL_RenderPresent(vid->renderer);
|
||||
|
||||
title[0] = '\0';
|
||||
video_driver_get_window_title(title, sizeof(title));
|
||||
if (title[0])
|
||||
SDL_SetWindowTitle(vid->window, title);
|
||||
sdl3_window_update_title(vid->window);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -573,18 +530,6 @@ static bool sdl3_gfx_alive(void *data)
|
||||
return !(vid->flags & SDL3_FLAG_QUITTING);
|
||||
}
|
||||
|
||||
static bool sdl3_gfx_focus(void *data)
|
||||
{
|
||||
sdl3_video_t *vid = (sdl3_video_t*)data;
|
||||
SDL_WindowFlags flags = (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS);
|
||||
return (SDL_GetWindowFlags(vid->window) & flags) == flags;
|
||||
}
|
||||
|
||||
static bool sdl3_gfx_suspend_screensaver(void *data, bool enable)
|
||||
{
|
||||
return enable ? SDL_DisableScreenSaver() : SDL_EnableScreenSaver();
|
||||
}
|
||||
|
||||
static bool sdl3_gfx_has_windowed(void *data)
|
||||
{
|
||||
/* kmsdrm backend has no windowing. Everything is an exclusive
|
||||
@@ -647,18 +592,6 @@ static bool sdl3_gfx_read_viewport(void *data, uint8_t *buffer, bool is_idle)
|
||||
return sdl3_capture_viewport(vid, buffer);
|
||||
}
|
||||
|
||||
static float sdl3_get_refresh_rate(void *data)
|
||||
{
|
||||
sdl3_video_t *vid = (sdl3_video_t*)data;
|
||||
const SDL_DisplayMode *mode;
|
||||
|
||||
if (!vid || !vid->window)
|
||||
return 0.0f;
|
||||
|
||||
mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(vid->window));
|
||||
return mode ? mode->refresh_rate : 0.0f;
|
||||
}
|
||||
|
||||
static void sdl3_poke_set_filtering(void *data, unsigned index, bool smooth, bool ctx_scaling)
|
||||
{
|
||||
sdl3_video_t *vid = (sdl3_video_t*)data;
|
||||
@@ -706,14 +639,6 @@ static void sdl3_poke_texture_enable(void *data, bool enable, bool full_screen)
|
||||
vid->menu.active = enable;
|
||||
}
|
||||
|
||||
static void sdl3_show_mouse(void *data, bool state)
|
||||
{
|
||||
if (state)
|
||||
SDL_ShowCursor();
|
||||
else
|
||||
SDL_HideCursor();
|
||||
}
|
||||
|
||||
static void sdl3_grab_mouse_toggle(void *data)
|
||||
{
|
||||
sdl3_video_t *vid = (sdl3_video_t*)data;
|
||||
@@ -897,10 +822,12 @@ static void gfx_display_sdl3_scissor_begin(void *data,
|
||||
if (!vid)
|
||||
return;
|
||||
|
||||
/* gfx_display passes scissor rects in GL convention (origin
|
||||
* bottom-left). SDL_SetRenderClipRect is top-left, so flip. */
|
||||
/* gfx_display passes scissor rects with a top-left origin. The
|
||||
* vulkan driver uses y directly and only GL flips (its scissor
|
||||
* origin is bottom-left). SDL_SetRenderClipRect is top-left too,
|
||||
* so we can pass the values directly to the rectangle. */
|
||||
rect.x = x;
|
||||
rect.y = (int)video_height - y - (int)height;
|
||||
rect.y = y;
|
||||
rect.w = (int)width;
|
||||
rect.h = (int)height;
|
||||
|
||||
@@ -1638,7 +1565,7 @@ static video_poke_interface_t sdl3_video_poke_interface = {
|
||||
sdl3_load_texture,
|
||||
sdl3_unload_texture,
|
||||
NULL, /* set_video_mode */
|
||||
sdl3_get_refresh_rate,
|
||||
sdl3_ctx_get_refresh_rate,
|
||||
sdl3_poke_set_filtering,
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
@@ -1672,8 +1599,8 @@ video_driver_t video_sdl3 = {
|
||||
sdl3_gfx_frame,
|
||||
sdl3_gfx_set_nonblock_state,
|
||||
sdl3_gfx_alive,
|
||||
sdl3_gfx_focus,
|
||||
sdl3_gfx_suspend_screensaver,
|
||||
sdl3_ctx_has_focus,
|
||||
sdl3_suppress_screensaver,
|
||||
sdl3_gfx_has_windowed,
|
||||
NULL, /* set_shader - all call sites treat this as optional */
|
||||
sdl3_gfx_free,
|
||||
|
||||
+12
-6
@@ -2688,7 +2688,7 @@ static void vulkan_font_render_msg(
|
||||
* instead of flushing the entire allocation.
|
||||
* Aligns offset down and size up to nonCoherentAtomSize
|
||||
* as required by the spec (§12.1). */
|
||||
VkDeviceSize flush_offset = (VkDeviceSize)font->dirty_y_min
|
||||
VkDeviceSize flush_offset = (VkDeviceSize)font->dirty_y_min
|
||||
* staging_tex->stride
|
||||
+ (VkDeviceSize)font->dirty_x_min * bpp;
|
||||
VkDeviceSize flush_end = (VkDeviceSize)(font->dirty_y_max > 0
|
||||
@@ -3103,6 +3103,12 @@ static struct vk_buffer_chain vulkan_buffer_chain_init(
|
||||
}
|
||||
|
||||
static const gfx_ctx_driver_t *gfx_ctx_vk_drivers[] = {
|
||||
/* SDL3 video driver is higher on the list so that when a Vulkan context
|
||||
* is desired while using SDL3, it's used. The sdl3_ctx_enabled() function
|
||||
* ensures it's only selected when SDL3 is actively in use. */
|
||||
#if defined(HAVE_SDL3)
|
||||
&gfx_ctx_sdl3_vk,
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
&gfx_ctx_cocoavk,
|
||||
#endif
|
||||
@@ -5072,9 +5078,9 @@ static void vulkan_set_signal_semaphore(void *handle, VkSemaphore semaphore)
|
||||
/* Drop every reference the frontend holds to core-owned GPU objects.
|
||||
*
|
||||
* Called immediately before core_reset(). Vulkan-context cores
|
||||
* typically destroy and recreate their renderer inside retro_reset(),
|
||||
* which invalidates the VkImage/VkImageView whose handles are cached
|
||||
* in vk->hw.image, plus any per-frame semaphores still referenced
|
||||
* typically destroy and recreate their renderer inside retro_reset(),
|
||||
* which invalidates the VkImage/VkImageView whose handles are cached
|
||||
* in vk->hw.image, plus any per-frame semaphores still referenced
|
||||
* via vk->hw.semaphores[] and vk->hw.signal_semaphore.
|
||||
*
|
||||
* vkDeviceWaitIdle ensures the previous frame's submit, which may
|
||||
@@ -7265,7 +7271,7 @@ static bool vulkan_frame(void *data, const void *frame,
|
||||
((vk->flags & VK_FLAG_MENU_ENABLE) || (vk->flags & VK_FLAG_OVERLAY_ENABLE)
|
||||
|| message_visible
|
||||
|| statistics_show
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
|| widgets_visible
|
||||
#endif
|
||||
) &&
|
||||
@@ -7380,7 +7386,7 @@ static bool vulkan_frame(void *data, const void *frame,
|
||||
((vk->flags & VK_FLAG_MENU_ENABLE) || (vk->flags & VK_FLAG_OVERLAY_ENABLE)
|
||||
|| message_visible
|
||||
|| statistics_show
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
|| widgets_visible
|
||||
#endif
|
||||
) &&
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2026 - Rob Loach
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* SDL3 OpenGL context driver. While the SDL3 video driver itself is
|
||||
* software rendered via SDL_Renderer, this context driver allows
|
||||
* the gl/gl1/glcore video drivers to render within the SDL3 window. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#include "../../gfx/video_defines.h"
|
||||
#include "../../gfx/video_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "../common/sdl3_common.h"
|
||||
|
||||
typedef struct gfx_ctx_sdl3_data
|
||||
{
|
||||
SDL_Window *win; /* Must be first because it's shared across
|
||||
* sdl3_ctx_* callbacks. */
|
||||
SDL_GLContext ctx;
|
||||
SDL_GLContext shared_ctx;
|
||||
bool core_hw_context_enable;
|
||||
bool adaptive_vsync;
|
||||
} gfx_ctx_sdl3_data_t;
|
||||
|
||||
/* bind_api runs before init (see video_context_driver_init), so there
|
||||
* is no driver instance to store these in yet. */
|
||||
static enum gfx_ctx_api sdl3_gl_api = GFX_CTX_OPENGL_API;
|
||||
static unsigned sdl3_gl_major = 0;
|
||||
static unsigned sdl3_gl_minor = 0;
|
||||
|
||||
/* Cached GL contexts. Allow using the GL context across video
|
||||
* rebuilds. */
|
||||
static SDL_GLContext sdl3_gl_cached_ctx = NULL;
|
||||
static SDL_GLContext sdl3_gl_cached_shared_ctx = NULL;
|
||||
|
||||
/* Core-profile context: explicitly requested, or GL 3.1+. */
|
||||
static bool sdl3_gl_core_profile(gfx_ctx_sdl3_data_t *sdl)
|
||||
{
|
||||
if (sdl && sdl->core_hw_context_enable)
|
||||
return true;
|
||||
return sdl3_gl_major > 3 || (sdl3_gl_major == 3 && sdl3_gl_minor >= 1);
|
||||
}
|
||||
|
||||
static void sdl3_ctx_destroy(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
|
||||
if (!sdl)
|
||||
return;
|
||||
|
||||
if (sdl->ctx && (video_st->flags & VIDEO_FLAG_CACHE_CONTEXT))
|
||||
{
|
||||
/* hw_render.cache_context reinit: keep the context alive for
|
||||
* the next set_video_mode instead of destroying it. */
|
||||
SDL_GL_MakeCurrent(sdl->win, NULL);
|
||||
sdl3_gl_cached_ctx = sdl->ctx;
|
||||
sdl3_gl_cached_shared_ctx = sdl->shared_ctx;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sdl->ctx)
|
||||
SDL_GL_DestroyContext(sdl->ctx);
|
||||
|
||||
if (sdl->shared_ctx)
|
||||
SDL_GL_DestroyContext(sdl->shared_ctx);
|
||||
}
|
||||
|
||||
if (sdl->win)
|
||||
{
|
||||
SDL_StopTextInput(sdl->win);
|
||||
SDL_DestroyWindow(sdl->win);
|
||||
}
|
||||
|
||||
/* SDL3 refcounts subsystems; this balances the SDL_InitSubSystem
|
||||
* in sdl3_ctx_init. While a cached context is stashed, keep our
|
||||
* reference: letting the refcount hit zero would tear the context
|
||||
* down with the subsystem. The next init adopts it. */
|
||||
if (!sdl3_gl_cached_ctx)
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
|
||||
free(sdl);
|
||||
}
|
||||
|
||||
static void *sdl3_ctx_init(void *video_driver)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl;
|
||||
|
||||
if (!sdl3_ctx_enabled("gl_sdl3"))
|
||||
return NULL;
|
||||
|
||||
sdl = (gfx_ctx_sdl3_data_t*)
|
||||
calloc(1, sizeof(gfx_ctx_sdl3_data_t));
|
||||
|
||||
if (!sdl)
|
||||
return NULL;
|
||||
|
||||
/* When a cached context is stashed, the previous instance kept its
|
||||
* video-subsystem reference to protect it - adopt that reference
|
||||
* instead of initializing again. */
|
||||
if (!sdl3_gl_cached_ctx && !SDL_InitSubSystem(SDL_INIT_VIDEO))
|
||||
{
|
||||
RARCH_WARN("[SDL3 GL] Failed to initialize SDL video subsystem: %s.\n", SDL_GetError());
|
||||
free(sdl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RARCH_LOG("[SDL3 GL] SDL %d.%d.%d gfx context driver initialized.\n",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION);
|
||||
|
||||
return sdl;
|
||||
}
|
||||
|
||||
static enum gfx_ctx_api sdl3_ctx_get_api(void *data) { return sdl3_gl_api; }
|
||||
|
||||
static bool sdl3_ctx_bind_api(void *data,
|
||||
enum gfx_ctx_api api, unsigned major,
|
||||
unsigned minor)
|
||||
{
|
||||
if (api != GFX_CTX_OPENGL_API && api != GFX_CTX_OPENGL_ES_API)
|
||||
return false;
|
||||
|
||||
sdl3_gl_api = api;
|
||||
sdl3_gl_major = major;
|
||||
sdl3_gl_minor = minor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sdl3_ctx_swap_interval(void *data, int interval)
|
||||
{
|
||||
/* Adaptive vsync and multi-frame intervals aren't supported
|
||||
* everywhere. Fallback to 1-frame interval rather than reverting
|
||||
* to the previous setting. */
|
||||
if (!SDL_GL_SetSwapInterval(interval) && interval != 0)
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
|
||||
static bool sdl3_ctx_set_video_mode(void *data,
|
||||
unsigned width, unsigned height,
|
||||
bool fullscreen)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
|
||||
if (!sdl)
|
||||
return false;
|
||||
|
||||
/* GL attributes must be set before the window is created. */
|
||||
if (!sdl->win)
|
||||
{
|
||||
if (sdl3_gl_api == GFX_CTX_OPENGL_ES_API)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
else if (sdl3_gl_core_profile(sdl))
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
|
||||
if (sdl3_gl_major > 0)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, sdl3_gl_major);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, sdl3_gl_minor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sdl3_window_set_video_mode(&sdl->win, width, height, fullscreen, SDL_WINDOW_OPENGL))
|
||||
goto error;
|
||||
|
||||
/* Hold onto the context across video reinit (hw_render.cache_context). */
|
||||
if (!sdl->ctx && sdl3_gl_cached_ctx)
|
||||
{
|
||||
sdl->ctx = sdl3_gl_cached_ctx;
|
||||
sdl->shared_ctx = sdl3_gl_cached_shared_ctx;
|
||||
sdl3_gl_cached_ctx = NULL;
|
||||
sdl3_gl_cached_shared_ctx = NULL;
|
||||
}
|
||||
|
||||
if (sdl->ctx)
|
||||
{
|
||||
/* Bind the context to the new window. */
|
||||
if (SDL_GL_MakeCurrent(sdl->win, sdl->ctx))
|
||||
{
|
||||
video_driver_cache_context_ack_set();
|
||||
RARCH_LOG("[SDL3 GL] Using cached GL context.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Stale cached context: drop it and create a fresh one
|
||||
* below. The ack stays unset, so the core receives a
|
||||
* context_reset and rebuilds its GL state. */
|
||||
RARCH_WARN("[SDL3 GL] Failed to bind cached GL context, creating anew: %s.\n",
|
||||
SDL_GetError());
|
||||
SDL_GL_DestroyContext(sdl->ctx);
|
||||
if (sdl->shared_ctx)
|
||||
SDL_GL_DestroyContext(sdl->shared_ctx);
|
||||
sdl->ctx = NULL;
|
||||
sdl->shared_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sdl->ctx && !(sdl->ctx = SDL_GL_CreateContext(sdl->win)))
|
||||
goto error;
|
||||
|
||||
/* Check whether or not adaptive vsync is supported. Probed on the
|
||||
* cached path too: this instance is freshly alloc'd on reinit, so
|
||||
* the flag would otherwise be lost with the old instance. */
|
||||
sdl->adaptive_vsync = SDL_GL_SetSwapInterval(-1);
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
RARCH_WARN("[SDL3 GL] Failed to set video mode: %s.\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sdl3_ctx_swap_buffers(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
if (sdl && sdl->win)
|
||||
SDL_GL_SwapWindow(sdl->win);
|
||||
}
|
||||
|
||||
static gfx_ctx_proc_t sdl3_ctx_get_proc_address(const char *name)
|
||||
{
|
||||
return (gfx_ctx_proc_t)SDL_GL_GetProcAddress(name);
|
||||
}
|
||||
|
||||
static uint32_t sdl3_ctx_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
|
||||
if (sdl && sdl->adaptive_vsync)
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
|
||||
|
||||
if (sdl3_gl_core_profile(sdl))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
|
||||
|
||||
/* Shader usage depends on the video driver:
|
||||
* - glcore uses slang
|
||||
* - gl uses GLSLS
|
||||
* - gl1 is fixed-function, without shaders */
|
||||
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
|
||||
if (string_is_equal(video_driver_get_ident(), "glcore"))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
|
||||
#endif
|
||||
#ifdef HAVE_GLSL
|
||||
if (string_is_equal(video_driver_get_ident(), "gl"))
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void sdl3_ctx_set_flags(void *data, uint32_t flags)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
if (!sdl)
|
||||
return;
|
||||
if (BIT32_GET(flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
|
||||
sdl->core_hw_context_enable = true;
|
||||
}
|
||||
|
||||
static void sdl3_ctx_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
gfx_ctx_sdl3_data_t *sdl = (gfx_ctx_sdl3_data_t*)data;
|
||||
|
||||
if (!sdl || !sdl->win || !sdl->ctx)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
if (!sdl->shared_ctx)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
SDL_GL_MakeCurrent(sdl->win, sdl->ctx);
|
||||
sdl->shared_ctx = SDL_GL_CreateContext(sdl->win);
|
||||
/* Reset the attribute so later SDL_GL_CreateContext calls
|
||||
* (video reinit) don't silently inherit sharing. */
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0);
|
||||
if (!sdl->shared_ctx)
|
||||
{
|
||||
RARCH_ERR("[SDL3 GL] Failed to create shared GL context: %s.\n",
|
||||
SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
SDL_GL_MakeCurrent(sdl->win, sdl->shared_ctx);
|
||||
}
|
||||
else
|
||||
SDL_GL_MakeCurrent(sdl->win, sdl->ctx);
|
||||
}
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_sdl3_gl =
|
||||
{
|
||||
sdl3_ctx_init,
|
||||
sdl3_ctx_destroy,
|
||||
sdl3_ctx_get_api,
|
||||
sdl3_ctx_bind_api,
|
||||
sdl3_ctx_swap_interval,
|
||||
sdl3_ctx_set_video_mode,
|
||||
sdl3_ctx_get_video_size,
|
||||
sdl3_ctx_get_refresh_rate,
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_metrics */
|
||||
NULL, /* translate_aspect */
|
||||
sdl3_ctx_update_title,
|
||||
sdl3_ctx_check_window,
|
||||
NULL, /* set_resize */
|
||||
sdl3_ctx_has_focus,
|
||||
sdl3_suppress_screensaver,
|
||||
true, /* has_windowed */
|
||||
sdl3_ctx_swap_buffers,
|
||||
sdl3_ctx_input_driver,
|
||||
sdl3_ctx_get_proc_address,
|
||||
NULL, /* image_buffer_init */
|
||||
NULL, /* image_buffer_write */
|
||||
sdl3_show_mouse,
|
||||
"gl_sdl3",
|
||||
sdl3_ctx_get_flags,
|
||||
sdl3_ctx_set_flags,
|
||||
sdl3_ctx_bind_hw_render,
|
||||
NULL, /* get_context_data */
|
||||
NULL, /* make_current */
|
||||
NULL, /* create_surface */
|
||||
NULL /* destroy_surface */
|
||||
};
|
||||
@@ -0,0 +1,301 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2026 - Rob Loach
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* SDL3 Vulkan context driver. While the SDL3 video driver itself is
|
||||
* software rendered via SDL_Renderer, this context driver allows
|
||||
* the vulkan video driver to render within the SDL3 window. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include <retro_timers.h>
|
||||
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
|
||||
#include "../common/sdl3_common.h"
|
||||
#include "../common/vulkan_common.h"
|
||||
|
||||
typedef struct gfx_ctx_sdl3_vk_data
|
||||
{
|
||||
SDL_Window *win; /* Must be first because it's shared across
|
||||
* sdl3_ctx_* callbacks. */
|
||||
int interval;
|
||||
gfx_ctx_vulkan_data_t vk;
|
||||
} gfx_ctx_sdl3_vk_data_t;
|
||||
|
||||
static void sdl3_vk_ctx_destroy(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
|
||||
if (!sdl)
|
||||
return;
|
||||
|
||||
vulkan_context_destroy(&sdl->vk, sdl->win != NULL);
|
||||
|
||||
#if defined(HAVE_THREADS)
|
||||
if (sdl->vk.context.queue_lock)
|
||||
slock_free(sdl->vk.context.queue_lock);
|
||||
#endif
|
||||
|
||||
if (sdl->win)
|
||||
{
|
||||
SDL_StopTextInput(sdl->win);
|
||||
SDL_DestroyWindow(sdl->win);
|
||||
}
|
||||
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
/* Balances the SDL_InitSubSystem in init (SDL3 refcounts). */
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
|
||||
free(sdl);
|
||||
}
|
||||
|
||||
static void *sdl3_vk_ctx_init(void *video_driver)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl;
|
||||
|
||||
if (!sdl3_ctx_enabled("vk_sdl3"))
|
||||
return NULL;
|
||||
|
||||
sdl = (gfx_ctx_sdl3_vk_data_t*)
|
||||
calloc(1, sizeof(gfx_ctx_sdl3_vk_data_t));
|
||||
|
||||
if (!sdl)
|
||||
return NULL;
|
||||
|
||||
/* SDL's X11 backend calls XInitThreads itself, so no Xlib setup
|
||||
* is needed here. */
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
|
||||
{
|
||||
RARCH_WARN("[SDL3 Vulkan] Failed to initialize SDL video subsystem: %s.\n",
|
||||
SDL_GetError());
|
||||
free(sdl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load SDL's Vulkan support up front: instance creation queries
|
||||
* SDL_Vulkan_GetInstanceExtensions before any window exists. */
|
||||
if (!SDL_Vulkan_LoadLibrary(NULL))
|
||||
{
|
||||
RARCH_WARN("[SDL3 Vulkan] Failed to load Vulkan library: %s.\n",
|
||||
SDL_GetError());
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!vulkan_context_init(&sdl->vk, VULKAN_WSI_SDL3))
|
||||
{
|
||||
SDL_Vulkan_UnloadLibrary();
|
||||
goto error;
|
||||
}
|
||||
|
||||
RARCH_LOG("[SDL3 Vulkan] SDL %d.%d.%d gfx context driver initialized.\n",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION);
|
||||
|
||||
return sdl;
|
||||
|
||||
error:
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
free(sdl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sdl3_vk_ctx_swap_interval(void *data, int interval)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
|
||||
if (sdl->interval != interval)
|
||||
{
|
||||
sdl->interval = interval;
|
||||
if (sdl->vk.swapchain)
|
||||
sdl->vk.flags |= VK_DATA_FLAG_NEED_NEW_SWAPCHAIN;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl3_vk_ctx_swap_buffers(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
|
||||
if (sdl->vk.context.flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN)
|
||||
{
|
||||
sdl->vk.context.flags &= ~VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN;
|
||||
if (sdl->vk.swapchain == VK_NULL_HANDLE)
|
||||
{
|
||||
retro_sleep(10);
|
||||
}
|
||||
else
|
||||
vulkan_present(&sdl->vk, sdl->vk.context.current_swapchain_index);
|
||||
}
|
||||
vulkan_acquire_next_image(&sdl->vk);
|
||||
}
|
||||
|
||||
static void sdl3_vk_ctx_check_window(void *data, bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
|
||||
/* A pending swapchain rebuild counts as a resize; the shared
|
||||
* handler pumps events and fetches the new size. */
|
||||
if (sdl->vk.flags & VK_DATA_FLAG_NEED_NEW_SWAPCHAIN)
|
||||
*resize = true;
|
||||
|
||||
sdl3_ctx_check_window(data, quit, resize, width, height);
|
||||
}
|
||||
|
||||
static bool sdl3_vk_ctx_set_resize(void *data,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
|
||||
if (!sdl)
|
||||
return false;
|
||||
|
||||
if (!vulkan_create_swapchain(&sdl->vk, width, height, sdl->interval))
|
||||
{
|
||||
RARCH_ERR("[SDL3 Vulkan] Failed to update swapchain.\n");
|
||||
sdl->vk.swapchain = VK_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sdl->vk.flags & VK_DATA_FLAG_CREATED_NEW_SWAPCHAIN)
|
||||
vulkan_acquire_next_image(&sdl->vk);
|
||||
sdl->vk.context.flags |= VK_CTX_FLAG_INVALID_SWAPCHAIN;
|
||||
sdl->vk.flags &= ~VK_DATA_FLAG_NEED_NEW_SWAPCHAIN;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sdl3_vk_ctx_set_video_mode(void *data,
|
||||
unsigned width, unsigned height,
|
||||
bool fullscreen)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
unsigned win_width = 0;
|
||||
unsigned win_height = 0;
|
||||
|
||||
if (!sdl)
|
||||
return false;
|
||||
|
||||
if (!sdl3_window_set_video_mode(&sdl->win, width, height, fullscreen,
|
||||
SDL_WINDOW_VULKAN))
|
||||
goto error;
|
||||
|
||||
sdl3_window_get_video_size(sdl->win, &win_width, &win_height);
|
||||
|
||||
if (!vulkan_surface_create(&sdl->vk, VULKAN_WSI_SDL3,
|
||||
NULL, sdl->win,
|
||||
win_width, win_height, sdl->interval))
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
/* Avoids destroying SDL here since vulkan_init() treats a failure
|
||||
* and will call vulkan_free() itself. This is the same for x_vk,
|
||||
* cocoa, and android. */
|
||||
RARCH_WARN("[SDL3 Vulkan] Failed to set video mode: %s.\n",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum gfx_ctx_api sdl3_vk_ctx_get_api(void *data)
|
||||
{
|
||||
return GFX_CTX_VULKAN_API;
|
||||
}
|
||||
|
||||
static bool sdl3_vk_ctx_bind_api(void *data, enum gfx_ctx_api api,
|
||||
unsigned major, unsigned minor) { return (api == GFX_CTX_VULKAN_API); }
|
||||
|
||||
/* Vulkan HW-render cores share the frontend's VkDevice through
|
||||
* context negotiation - there is no context to switch. */
|
||||
static void sdl3_vk_ctx_bind_hw_render(void *data, bool enable) { }
|
||||
|
||||
static void *sdl3_vk_ctx_get_context_data(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
return &sdl->vk.context;
|
||||
}
|
||||
|
||||
static uint32_t sdl3_vk_ctx_get_flags(void *data)
|
||||
{
|
||||
gfx_ctx_sdl3_vk_data_t *sdl = (gfx_ctx_sdl3_vk_data_t*)data;
|
||||
uint32_t flags = 0;
|
||||
uint8_t present_mode_count = 16;
|
||||
uint8_t i = 0;
|
||||
|
||||
/* Check for FIFO_RELAXED_KHR capability. */
|
||||
if (sdl)
|
||||
{
|
||||
for (i = 0; i < present_mode_count; i++)
|
||||
{
|
||||
if (sdl->vk.context.present_modes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
|
||||
{
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void sdl3_vk_ctx_set_flags(void *data, uint32_t flags) { }
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_sdl3_vk = {
|
||||
sdl3_vk_ctx_init,
|
||||
sdl3_vk_ctx_destroy,
|
||||
sdl3_vk_ctx_get_api,
|
||||
sdl3_vk_ctx_bind_api,
|
||||
sdl3_vk_ctx_swap_interval,
|
||||
sdl3_vk_ctx_set_video_mode,
|
||||
sdl3_ctx_get_video_size,
|
||||
sdl3_ctx_get_refresh_rate,
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_metrics */
|
||||
NULL, /* translate_aspect */
|
||||
sdl3_ctx_update_title,
|
||||
sdl3_vk_ctx_check_window,
|
||||
sdl3_vk_ctx_set_resize,
|
||||
sdl3_ctx_has_focus,
|
||||
sdl3_suppress_screensaver,
|
||||
true, /* has_windowed */
|
||||
sdl3_vk_ctx_swap_buffers,
|
||||
sdl3_ctx_input_driver,
|
||||
NULL, /* get_proc_address */
|
||||
NULL, /* image_buffer_init */
|
||||
NULL, /* image_buffer_write */
|
||||
sdl3_show_mouse,
|
||||
"vk_sdl3",
|
||||
sdl3_vk_ctx_get_flags,
|
||||
sdl3_vk_ctx_set_flags,
|
||||
sdl3_vk_ctx_bind_hw_render,
|
||||
sdl3_vk_ctx_get_context_data,
|
||||
NULL, /* make_current */
|
||||
NULL, /* create_surface */
|
||||
NULL /* destroy_surface */
|
||||
};
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include "../msg_hash.h"
|
||||
#include "../verbosity.h"
|
||||
#include "font_driver.h"
|
||||
#include "video_thread_wrapper.h"
|
||||
|
||||
@@ -1021,7 +1022,14 @@ void font_driver_init_osd(
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
|
||||
if (video_st->osd_font && video_st->osd_font_owner != video_data)
|
||||
{
|
||||
font_data_t *stale = (font_data_t*)video_st->osd_font;
|
||||
|
||||
RARCH_WARN("[Font] Discarding stale OSD font from a previous video driver instance.\n");
|
||||
stale->renderer = NULL;
|
||||
stale->renderer_data = NULL;
|
||||
font_driver_free_osd();
|
||||
}
|
||||
|
||||
/* threading_hint is false: both callers - video_driver_init_internal()
|
||||
* and the threaded wrapper's CMD_INIT - already run on the thread that
|
||||
|
||||
+12
-9
@@ -163,6 +163,12 @@ static const gfx_ctx_driver_t *gfx_ctx_gl_drivers[] = {
|
||||
#if defined(HAVE_OPENDINGUX_FBDEV)
|
||||
&gfx_ctx_opendingux_fbdev,
|
||||
#endif
|
||||
/* SDL3 video driver is higher on the list so that when an OpenGL context
|
||||
* is desired while using SDL3, it's used. The sdl3_ctx_enabled() function
|
||||
* ensures it's only selected when SDL3 is actively in use. */
|
||||
#if defined(HAVE_SDL3) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE) || defined(HAVE_OPENGLES))
|
||||
&gfx_ctx_sdl3_gl,
|
||||
#endif
|
||||
#if defined(_WIN32) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)) && !defined(HAVE_ANGLE)
|
||||
&gfx_ctx_wgl,
|
||||
#endif
|
||||
@@ -1981,14 +1987,10 @@ void video_driver_free_internal(void)
|
||||
* the rest still free it inside their own free(). Keyed on
|
||||
* ownership so that a teardown arriving after the next driver is
|
||||
* already up cannot take the live font with it. Under threaded
|
||||
* video the wrapper does this from CMD_FREE, on the video thread. */
|
||||
#ifdef HAVE_THREADS
|
||||
if (!is_threaded)
|
||||
#endif
|
||||
{
|
||||
if (vid && vid->font_backend)
|
||||
font_driver_free_osd_for(video_st->data);
|
||||
}
|
||||
* video the wrapper does this from CMD_FREE, so we don't need to
|
||||
* check HAVE_THREADS here. */
|
||||
if (vid && vid->font_backend)
|
||||
font_driver_free_osd_for(video_st->data);
|
||||
|
||||
if (video_st->data && vid && vid->free)
|
||||
vid->free(video_st->data);
|
||||
@@ -4328,7 +4330,8 @@ bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
|
||||
#endif
|
||||
{
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || \
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || \
|
||||
defined(HAVE_SDL3)
|
||||
bool window_custom_size_enable = settings->bools.video_window_save_positions;
|
||||
#else
|
||||
bool window_custom_size_enable = settings->bools.video_window_custom_size_enable;
|
||||
|
||||
@@ -1570,6 +1570,8 @@ extern video_driver_t video_null;
|
||||
|
||||
extern const gfx_ctx_driver_t gfx_ctx_osmesa;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_sdl_gl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_sdl3_gl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_sdl3_vk;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_x_egl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_uwp;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_vk_wayland;
|
||||
|
||||
@@ -977,6 +977,12 @@ AUDIO
|
||||
#include "../input/drivers/sdl3_input.c"
|
||||
#include "../gfx/drivers/sdl3_gfx.c"
|
||||
#include "../gfx/common/sdl3_common.c"
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE) || defined(HAVE_OPENGLES)
|
||||
#include "../gfx/drivers_context/sdl3_gl_ctx.c"
|
||||
#endif
|
||||
#if defined(HAVE_VULKAN)
|
||||
#include "../gfx/drivers_context/sdl3_vk_ctx.c"
|
||||
#endif
|
||||
#elif defined(HAVE_SDL2)
|
||||
#include "../audio/drivers/sdl_audio.c"
|
||||
#include "../input/drivers/sdl_input.c"
|
||||
|
||||
@@ -471,13 +471,20 @@ static bool sdl3_set_sensor_state(void *data, unsigned port,
|
||||
/* Gets the SDL_Window, if it exists. */
|
||||
static SDL_Window *sdl3_input_window(void)
|
||||
{
|
||||
sdl3_video_t *video_ptr;
|
||||
gfx_ctx_ident_t ident_info;
|
||||
|
||||
if (string_is_not_equal(video_driver_get_ident(), "sdl3"))
|
||||
return NULL;
|
||||
if (string_is_equal(video_driver_get_ident(), "sdl3"))
|
||||
{
|
||||
sdl3_video_t *video_ptr = (sdl3_video_t*)video_driver_get_ptr();
|
||||
return video_ptr != NULL ? video_ptr->window : NULL;
|
||||
}
|
||||
|
||||
if ((video_ptr = (sdl3_video_t*)video_driver_get_ptr()))
|
||||
return video_ptr->window;
|
||||
/* gl/gl1/glcore/vulkan running on the SDL3 context drivers. They
|
||||
* register their SDL_Window as the display userdata via sdl3_set_handles. */
|
||||
ident_info.ident = NULL;
|
||||
video_context_driver_get_ident(&ident_info);
|
||||
if (string_is_equal(ident_info.ident, "gl_sdl3") || string_is_equal(ident_info.ident, "vk_sdl3"))
|
||||
return (SDL_Window*)video_driver_display_userdata_get();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -10254,14 +10254,16 @@ unsigned menu_displaylist_build_list(
|
||||
case DISPLAYLIST_VIDEO_WINDOWED_MODE_SETTINGS_LIST:
|
||||
{
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || \
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || \
|
||||
defined(HAVE_SDL3)
|
||||
bool window_custom_size_enable = settings->bools.video_window_save_positions;
|
||||
#else
|
||||
bool window_custom_size_enable = settings->bools.video_window_custom_size_enable;
|
||||
#endif
|
||||
static menu_displaylist_build_info_selective_t build_list[] = {
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || \
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || \
|
||||
defined(HAVE_SDL3)
|
||||
{MENU_ENUM_LABEL_VIDEO_WINDOW_SAVE_POSITION, PARSE_ONLY_BOOL, true },
|
||||
#else
|
||||
{MENU_ENUM_LABEL_VIDEO_WINDOW_CUSTOM_SIZE_ENABLE, PARSE_ONLY_BOOL, true },
|
||||
|
||||
+6
-5
@@ -11613,14 +11613,14 @@ static const setting_desc_t vid_desc_15[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3)
|
||||
static const setting_desc_t vid_desc_16[] = {
|
||||
/* GENERATED: rows come from settings_def_video_window_save_position.h in order. */
|
||||
#include "../settings/settings_def_video_window_save_position.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)))
|
||||
#if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3))
|
||||
static const setting_desc_t vid_desc_17[] = {
|
||||
/* GENERATED: rows come from settings_def_video_window_custom_size.h in order. */
|
||||
#include "../settings/settings_def_video_window_custom_size.h"
|
||||
@@ -14461,7 +14461,8 @@ static void settings_build_video(
|
||||
ADD_DESC(vid_desc_15);
|
||||
#endif
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || \
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
(defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || \
|
||||
defined(HAVE_SDL3)
|
||||
ADD_DESC(vid_desc_16);
|
||||
#else
|
||||
ADD_DESC(vid_desc_17);
|
||||
@@ -17748,10 +17749,10 @@ static const settings_desc_table_t settings_desc_registry[] = {
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
|
||||
{ vid_desc_15, (uint16_t)ARRAY_SIZE(vid_desc_15) },
|
||||
#endif
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))
|
||||
#if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3)
|
||||
{ vid_desc_16, (uint16_t)ARRAY_SIZE(vid_desc_16) },
|
||||
#endif
|
||||
#if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)))
|
||||
#if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3))
|
||||
{ vid_desc_17, (uint16_t)ARRAY_SIZE(vid_desc_17) },
|
||||
#endif
|
||||
{ video2_desc_0, (uint16_t)ARRAY_SIZE(video2_desc_0) },
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* matches SDESC_<kind>_ROW; row order is menu display order;
|
||||
* h2json.py parses these rows for the Crowdin source upload. */
|
||||
|
||||
/* Descriptor and configuration rows are #if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))); the string
|
||||
/* Descriptor and configuration rows are #if !((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3)); the string
|
||||
* tables always carry this row via the strings pass. */
|
||||
#if (!((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)))) || defined(SETTINGS_DEF_STRINGS_PASS)
|
||||
#if (!((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3))) || defined(SETTINGS_DEF_STRINGS_PASS)
|
||||
S_BOOL_EX(video_window_custom_size_enable, VIDEO_WINDOW_CUSTOM_SIZE_ENABLE,
|
||||
"video_window_custom_size_enable",
|
||||
DEFAULT_WINDOW_CUSTOM_SIZE_ENABLE, SD_FLAG_NONE, 0, CMD_EVENT_REINIT, setting_bool_action_left_with_refresh, NULL, NULL, NULL, setting_bool_action_left_with_refresh, setting_bool_action_right_with_refresh, 0,
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* matches SDESC_<kind>_ROW; row order is menu display order;
|
||||
* h2json.py parses these rows for the Crowdin source upload. */
|
||||
|
||||
/* Descriptor and configuration rows are #if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)); the string
|
||||
/* Descriptor and configuration rows are #if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3); the string
|
||||
* tables always carry this row via the strings pass. */
|
||||
#if ((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH))) || defined(SETTINGS_DEF_STRINGS_PASS)
|
||||
#if ((defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || (defined(HAVE_COCOA_METAL) && !defined(HAVE_COCOATOUCH)) || defined(HAVE_SDL3)) || defined(SETTINGS_DEF_STRINGS_PASS)
|
||||
/* config key "video_window_save_positions" differs from the label string; the
|
||||
* configuration.c row stays literal for this setting. */
|
||||
#ifndef SETTINGS_DEF_CONFIG_PASS
|
||||
|
||||
Reference in new issue
Block a user