Skip to content

Commit 152ba82

Browse files
committed
video: Try to reconfigure the window for OpenGL without destroying it
When attaching a renderer (GL based specifically) to a window that was not created with the appropriate flags, the window would be destroyed and recreated to configure it for the desired rendering backend. While most of the issues with this have been mitigated over time, there can still be some undesirable side effects from doing so on certain platforms. If the window was just created and was never configured for any graphics context, it is possible that the reconfiguration can be done without destroying the window first. The Wayland implementation fixes an issue when creating a window with the fullscreen flag on wlroots based Wayland compositors, and can likely be extended to other platforms to avoid unnecessarily destroying/recreating a window in the very common case where a window is created, followed immediately by attaching a renderer.
1 parent 6e5994d commit 152ba82

File tree

7 files changed

+119
-2
lines changed

7 files changed

+119
-2
lines changed

src/render/opengl/SDL_render_gl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1666,7 +1666,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr
16661666
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
16671667
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
16681668

1669-
if (!SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) {
1669+
if (!SDL_ReconfigureWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) {
16701670
goto error;
16711671
}
16721672
}

src/render/opengles2/SDL_render_gles2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2176,7 +2176,7 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
21762176
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
21772177
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
21782178

2179-
if (!SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) {
2179+
if (!SDL_ReconfigureWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) {
21802180
goto error;
21812181
}
21822182
}

src/video/SDL_sysvideo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ struct SDL_VideoDevice
314314
bool (*ApplyWindowProgress)(SDL_VideoDevice *_this, SDL_Window *window);
315315
bool (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
316316
bool (*SyncWindow)(SDL_VideoDevice *_this, SDL_Window *window);
317+
bool (*ReconfigureWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags);
317318

318319
/* * * */
319320
/*
@@ -574,6 +575,7 @@ extern void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right,
574575
extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor);
575576

576577
extern bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags);
578+
extern bool SDL_ReconfigureWindow(SDL_Window *window, SDL_WindowFlags flags);
577579
extern bool SDL_HasWindows(void);
578580
extern void SDL_RelativeToGlobalForWindow(SDL_Window *window, int rel_x, int rel_y, int *abs_x, int *abs_y);
579581
extern void SDL_GlobalToRelativeForWindow(SDL_Window *window, int abs_x, int abs_y, int *rel_x, int *rel_y);

src/video/SDL_video.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,6 +2634,66 @@ SDL_Window *SDL_CreatePopupWindow(SDL_Window *parent, int offset_x, int offset_y
26342634
return window;
26352635
}
26362636

2637+
static bool SDL_ReconfigureWindowInternal(SDL_Window *window, SDL_WindowFlags flags)
2638+
{
2639+
bool loaded_opengl = false;
2640+
bool loaded_vulkan = false;
2641+
2642+
if (!_this->ReconfigureWindow) {
2643+
return false;
2644+
}
2645+
2646+
// Only attempt to reconfigure if the window has no existing graphics flags.
2647+
if (window->flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN)) {
2648+
return false;
2649+
}
2650+
2651+
const SDL_WindowFlags graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN);
2652+
if (graphics_flags & (graphics_flags - 1)) {
2653+
return SDL_SetError("Conflicting window flags specified");
2654+
}
2655+
2656+
if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) {
2657+
return SDL_ContextNotSupported("OpenGL");
2658+
}
2659+
if ((flags & SDL_WINDOW_VULKAN) && !_this->Vulkan_CreateSurface) {
2660+
return SDL_ContextNotSupported("Vulkan");
2661+
}
2662+
if ((flags & SDL_WINDOW_METAL) && !_this->Metal_CreateView) {
2663+
return SDL_ContextNotSupported("Metal");
2664+
}
2665+
2666+
SDL_DestroyWindowSurface(window);
2667+
2668+
if (graphics_flags & SDL_WINDOW_OPENGL) {
2669+
loaded_opengl = SDL_GL_LoadLibrary(NULL);
2670+
if (!loaded_opengl) {
2671+
return false;
2672+
}
2673+
} else if (graphics_flags & SDL_WINDOW_VULKAN) {
2674+
loaded_vulkan = SDL_GL_LoadLibrary(NULL);
2675+
if (!loaded_vulkan) {
2676+
return false;
2677+
}
2678+
}
2679+
2680+
// Try to reconfigure the window for the requested graphics flags.
2681+
if (!_this->ReconfigureWindow(_this, window, graphics_flags)) {
2682+
if (loaded_opengl) {
2683+
SDL_GL_UnloadLibrary();
2684+
}
2685+
if (loaded_vulkan) {
2686+
SDL_Vulkan_UnloadLibrary();
2687+
}
2688+
2689+
return false;
2690+
}
2691+
2692+
window->flags |= graphics_flags;
2693+
2694+
return true;
2695+
}
2696+
26372697
bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
26382698
{
26392699
bool loaded_opengl = false;
@@ -2788,6 +2848,16 @@ bool SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
27882848
return true;
27892849
}
27902850

2851+
bool SDL_ReconfigureWindow(SDL_Window *window, SDL_WindowFlags flags)
2852+
{
2853+
// Try to reconfigure the window for the desired flags first, before completely destroying and recreating it.
2854+
if (!SDL_ReconfigureWindowInternal(window, flags)) {
2855+
return SDL_RecreateWindow(window, flags);
2856+
}
2857+
2858+
return true;
2859+
}
2860+
27912861
bool SDL_HasWindows(void)
27922862
{
27932863
return _this && _this->windows;

src/video/wayland/SDL_waylandvideo.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
660660
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
661661
device->SyncWindow = Wayland_SyncWindow;
662662
device->SetWindowFocusable = Wayland_SetWindowFocusable;
663+
device->ReconfigureWindow = Wayland_ReconfigureWindow;
663664

664665
#ifdef SDL_USE_LIBDBUS
665666
if (SDL_SystemTheme_Init())

src/video/wayland/SDL_waylandwindow.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2543,6 +2543,49 @@ bool Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, b
25432543
return true;
25442544
}
25452545

2546+
bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags)
2547+
{
2548+
SDL_WindowData *data = window->internal;
2549+
2550+
if (data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOWN) {
2551+
// Window is already mapped; abort.
2552+
return false;
2553+
}
2554+
2555+
/* The caller guarantees that only one of the GL or Vulkan flags will be set,
2556+
* and the window will have no previous video flags.
2557+
*/
2558+
if (flags & SDL_WINDOW_OPENGL) {
2559+
if (!data->egl_window) {
2560+
data->egl_window = WAYLAND_wl_egl_window_create(data->surface, data->current.pixel_width, data->current.pixel_height);
2561+
}
2562+
2563+
#ifdef SDL_VIDEO_OPENGL_EGL
2564+
// Create the GLES window surface
2565+
data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)data->egl_window);
2566+
2567+
if (data->egl_surface == EGL_NO_SURFACE) {
2568+
return false; // SDL_EGL_CreateSurface should have set error
2569+
}
2570+
#endif
2571+
2572+
if (!data->gles_swap_frame_event_queue) {
2573+
data->gles_swap_frame_event_queue = WAYLAND_wl_display_create_queue(data->waylandData->display);
2574+
data->gles_swap_frame_surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface);
2575+
WAYLAND_wl_proxy_set_queue((struct wl_proxy *)data->gles_swap_frame_surface_wrapper, data->gles_swap_frame_event_queue);
2576+
data->gles_swap_frame_callback = wl_surface_frame(data->gles_swap_frame_surface_wrapper);
2577+
wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data);
2578+
}
2579+
2580+
return true;
2581+
} else if (flags & SDL_WINDOW_VULKAN) {
2582+
// Nothing to configure for Vulkan.
2583+
return true;
2584+
}
2585+
2586+
return false;
2587+
}
2588+
25462589
bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
25472590
{
25482591
SDL_WindowData *data;

src/video/wayland/SDL_waylandwindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ extern void *Wayland_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *win
253253
extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled);
254254
extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
255255
extern bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
256+
extern bool Wayland_ReconfigureWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowFlags flags);
256257

257258
extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data);
258259

0 commit comments

Comments
 (0)