Atlas - SDL_waylandopengles.c
Home / ext / SDL / src / video / wayland Lines: 1 | Size: 9130 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#if defined(SDL_VIDEO_DRIVER_WAYLAND) && defined(SDL_VIDEO_OPENGL_EGL) 24 25#include "../../core/unix/SDL_poll.h" 26#include "../SDL_sysvideo.h" 27#include "../../events/SDL_windowevents_c.h" 28#include "SDL_waylandvideo.h" 29#include "SDL_waylandopengles.h" 30#include "SDL_waylandwindow.h" 31#include "SDL_waylandevents_c.h" 32 33#include "xdg-shell-client-protocol.h" 34 35// EGL implementation of SDL OpenGL ES support 36 37void Wayland_GLES_SetDefaultProfileConfig(SDL_VideoDevice *_this) 38{ 39#if defined(SDL_PLATFORM_QNXNTO) 40 // QNX defaults to EGL_PLATFORM_SCREEN_QNX unless this is explicitly specified 41 _this->gl_config.egl_platform = EGL_PLATFORM_WAYLAND_EXT; 42#endif 43} 44 45bool Wayland_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path) 46{ 47 bool result; 48 SDL_VideoData *data = _this->internal; 49 50 result = SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)data->display); 51 52 Wayland_PumpEvents(_this); 53 WAYLAND_wl_display_flush(data->display); 54 55 return result; 56} 57 58SDL_GLContext Wayland_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) 59{ 60 SDL_GLContext context; 61 context = SDL_EGL_CreateContext(_this, window->internal->egl_surface); 62 WAYLAND_wl_display_flush(_this->internal->display); 63 64 return context; 65} 66 67/* Wayland wants to tell you when to provide new frames, and if you have a non-zero 68 swap interval, Mesa will block until a callback tells it to do so. On some 69 compositors, they might decide that a minimized window _never_ gets a callback, 70 which causes apps to hang during swapping forever. So we always set the official 71 eglSwapInterval to zero to avoid blocking inside EGL, and manage this ourselves. 72 If a swap blocks for too long waiting on a callback, we just go on, under the 73 assumption the frame will be wasted, but this is better than freezing the app. 74 I frown upon platforms that dictate this sort of control inversion (the callback 75 is intended for _rendering_, not stalling until vsync), but we can work around 76 this for now. --ryan. */ 77/* Addendum: several recent APIs demand this sort of control inversion: Emscripten, 78 libretro, Wayland, probably others...it feels like we're eventually going to have 79 to give in with a future SDL API revision, since we can bend the other APIs to 80 this style, but this style is much harder to bend the other way. :/ */ 81bool Wayland_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval) 82{ 83 if (!_this->egl_data) { 84 return SDL_SetError("EGL not initialized"); 85 } 86 87 /* technically, this is _all_ adaptive vsync (-1), because we can't 88 actually wait for the _next_ vsync if you set 1, but things that 89 request 1 probably won't care _that_ much. I hope. No matter what 90 you do, though, you never see tearing on Wayland. */ 91 if (interval > 1) { 92 interval = 1; 93 } else if (interval < -1) { 94 interval = -1; 95 } 96 97 // !!! FIXME: technically, this should be per-context, right? 98 _this->egl_data->egl_swapinterval = interval; 99 _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, 0); 100 return true; 101} 102 103bool Wayland_GLES_GetSwapInterval(SDL_VideoDevice *_this, int *interval) 104{ 105 if (!_this->egl_data) { 106 return SDL_SetError("EGL not initialized"); 107 } 108 109 *interval =_this->egl_data->egl_swapinterval; 110 return true; 111} 112 113bool Wayland_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) 114{ 115 SDL_WindowData *data = window->internal; 116 const int swap_interval = _this->egl_data->egl_swapinterval; 117 118 /* For windows that we know are hidden, skip swaps entirely, if we don't do 119 * this compositors will intentionally stall us indefinitely and there's no 120 * way for an end user to show the window, unlike other situations (i.e. 121 * the window is minimized, behind another window, etc.). 122 * 123 * FIXME: Request EGL_WAYLAND_swap_buffers_with_timeout. 124 * -flibit 125 */ 126 if (data->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_SHOWN && 127 data->shell_surface_status != WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) { 128 return true; 129 } 130 131 /* By default, we wait for the Wayland frame callback and then issue the pageflip (eglSwapBuffers), 132 * but if we want low latency (double buffer scheme), we issue the pageflip and then wait 133 * immediately for the Wayland frame callback. 134 */ 135 if (data->double_buffer) { 136 // Feed the frame to Wayland. This will set it so the wl_surface_frame callback can fire again. 137 if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, data->egl_surface)) { 138 return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers"); 139 } 140 141 WAYLAND_wl_display_flush(data->waylandData->display); 142 } 143 144 // Control swap interval ourselves. See comments on Wayland_GLES_SetSwapInterval 145 if (swap_interval != 0 && data->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOWN) { 146 SDL_VideoData *videodata = _this->internal; 147 struct wl_display *display = videodata->display; 148 // 20hz, so we'll progress even if throttled to zero. 149 const Uint64 max_wait = SDL_GetTicksNS() + (SDL_NS_PER_SECOND / 20); 150 while (SDL_GetAtomicInt(&data->swap_interval_ready) == 0) { 151 Uint64 now; 152 153 WAYLAND_wl_display_flush(display); 154 155 /* wl_display_prepare_read_queue() will return false if the event queue is not empty. 156 * If the event queue is empty, it will prepare us for our SDL_IOReady() call. */ 157 if (WAYLAND_wl_display_prepare_read_queue(display, data->gles_swap_frame_event_queue) != 0) { 158 // We have some pending events. Check if the frame callback happened. 159 WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue); 160 continue; 161 } 162 163 // Beyond this point, we must either call wl_display_cancel_read() or wl_display_read_events() 164 165 now = SDL_GetTicksNS(); 166 if (now >= max_wait) { 167 // Timeout expired. Cancel the read. 168 WAYLAND_wl_display_cancel_read(display); 169 break; 170 } 171 172 if (SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_IOR_READ, max_wait - now) <= 0) { 173 // Error or timeout expired without any events for us. Cancel the read. 174 WAYLAND_wl_display_cancel_read(display); 175 break; 176 } 177 178 // We have events. Read and dispatch them. 179 WAYLAND_wl_display_read_events(display); 180 WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue); 181 } 182 SDL_SetAtomicInt(&data->swap_interval_ready, 0); 183 } 184 185 if (!data->double_buffer) { 186 // Feed the frame to Wayland. This will set it so the wl_surface_frame callback can fire again. 187 if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, data->egl_surface)) { 188 return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers"); 189 } 190 191 WAYLAND_wl_display_flush(data->waylandData->display); 192 } 193 194 return true; 195} 196 197bool Wayland_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context) 198{ 199 bool result; 200 201 if (window && context) { 202 result = SDL_EGL_MakeCurrent(_this, window->internal->egl_surface, context); 203 } else { 204 result = SDL_EGL_MakeCurrent(_this, NULL, NULL); 205 } 206 207 WAYLAND_wl_display_flush(_this->internal->display); 208 209 _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, 0); // see comments on Wayland_GLES_SetSwapInterval. 210 211 return result; 212} 213 214bool Wayland_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context) 215{ 216 bool result = SDL_EGL_DestroyContext(_this, context); 217 WAYLAND_wl_display_flush(_this->internal->display); 218 return result; 219} 220 221EGLSurface Wayland_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window) 222{ 223 SDL_WindowData *windowdata = window->internal; 224 225 return windowdata->egl_surface; 226} 227 228#endif // SDL_VIDEO_DRIVER_WAYLAND && SDL_VIDEO_OPENGL_EGL 229[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.