1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
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 | |
22 | #include "SDL_internal.h" |
23 | |
24 | #ifdef SDL_VIDEO_DRIVER_KMSDRM |
25 | |
26 | #include "SDL_kmsdrmvideo.h" |
27 | #include "SDL_kmsdrmopengles.h" |
28 | #include "SDL_kmsdrmdyn.h" |
29 | #include <errno.h> |
30 | |
31 | #ifndef EGL_PLATFORM_GBM_MESA |
32 | #define EGL_PLATFORM_GBM_MESA 0x31D7 |
33 | #endif |
34 | |
35 | // EGL implementation of SDL OpenGL support |
36 | |
37 | void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor) |
38 | { |
39 | /* if SDL was _also_ built with the Raspberry Pi driver (so we're |
40 | definitely a Pi device) or with the ROCKCHIP video driver |
41 | (it's a ROCKCHIP device), default to GLES2. */ |
42 | #if defined(SDL_VIDEO_DRIVER_RPI) || defined(SDL_VIDEO_DRIVER_ROCKCHIP) |
43 | *mask = SDL_GL_CONTEXT_PROFILE_ES; |
44 | *major = 2; |
45 | *minor = 0; |
46 | #endif |
47 | } |
48 | |
49 | bool KMSDRM_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path) |
50 | { |
51 | /* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(), |
52 | where we do the same library load we would normally do here. |
53 | because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(), |
54 | so gbm dev isn't yet created when this is called, AND we can't alter the |
55 | call order in SDL_CreateWindow(). */ |
56 | #if 0 |
57 | NativeDisplayType display = (NativeDisplayType)_this->internal->gbm_dev; |
58 | return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA); |
59 | #endif |
60 | return true; |
61 | } |
62 | |
63 | void KMSDRM_GLES_UnloadLibrary(SDL_VideoDevice *_this) |
64 | { |
65 | /* As with KMSDRM_GLES_LoadLibrary(), we define our own "dummy" unloading function |
66 | so we manually unload the library whenever we want. */ |
67 | } |
68 | |
69 | SDL_EGL_CreateContext_impl(KMSDRM) |
70 | |
71 | bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval) |
72 | { |
73 | if (!_this->egl_data) { |
74 | return SDL_SetError("EGL not initialized" ); |
75 | } |
76 | |
77 | if (interval == 0 || interval == 1) { |
78 | _this->egl_data->egl_swapinterval = interval; |
79 | } else { |
80 | return SDL_SetError("Only swap intervals of 0 or 1 are supported" ); |
81 | } |
82 | |
83 | return true; |
84 | } |
85 | |
86 | bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) |
87 | { |
88 | SDL_WindowData *windata = window->internal; |
89 | SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); |
90 | SDL_VideoData *viddata = _this->internal; |
91 | KMSDRM_FBInfo *fb_info; |
92 | int ret = 0; |
93 | |
94 | /* Always wait for the previous issued flip before issuing a new one, |
95 | even if you do async flips. */ |
96 | uint32_t flip_flags = DRM_MODE_PAGE_FLIP_EVENT; |
97 | |
98 | // Skip the swap if we've switched away to another VT |
99 | if (windata->egl_surface == EGL_NO_SURFACE) { |
100 | // Wait a bit, throttling to ~100 FPS |
101 | SDL_Delay(10); |
102 | return true; |
103 | } |
104 | |
105 | // Recreate the GBM / EGL surfaces if the display mode has changed |
106 | if (windata->egl_surface_dirty) { |
107 | KMSDRM_CreateSurfaces(_this, window); |
108 | } |
109 | |
110 | /* Wait for confirmation that the next front buffer has been flipped, at which |
111 | point the previous front buffer can be released */ |
112 | if (!KMSDRM_WaitPageflip(_this, windata)) { |
113 | return SDL_SetError("Wait for previous pageflip failed" ); |
114 | } |
115 | |
116 | // Release the previous front buffer |
117 | if (windata->bo) { |
118 | KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); |
119 | windata->bo = NULL; |
120 | } |
121 | |
122 | windata->bo = windata->next_bo; |
123 | |
124 | /* Mark a buffer to become the next front buffer. |
125 | This won't happen until pagelip completes. */ |
126 | if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, |
127 | windata->egl_surface))) { |
128 | return SDL_SetError("eglSwapBuffers failed" ); |
129 | } |
130 | |
131 | /* From the GBM surface, get the next BO to become the next front buffer, |
132 | and lock it so it can't be allocated as a back buffer (to prevent EGL |
133 | from drawing into it!) */ |
134 | windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs); |
135 | if (!windata->next_bo) { |
136 | return SDL_SetError("Could not lock front buffer on GBM surface" ); |
137 | } |
138 | |
139 | // Get an actual usable fb for the next front buffer. |
140 | fb_info = KMSDRM_FBFromBO(_this, windata->next_bo); |
141 | if (!fb_info) { |
142 | return SDL_SetError("Could not get a framebuffer" ); |
143 | } |
144 | |
145 | if (!windata->bo) { |
146 | /* On the first swap, immediately present the new front buffer. Before |
147 | drmModePageFlip can be used the CRTC has to be configured to use |
148 | the current connector and mode with drmModeSetCrtc */ |
149 | ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, |
150 | dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0, |
151 | &dispdata->connector->connector_id, 1, &dispdata->mode); |
152 | |
153 | if (ret) { |
154 | return SDL_SetError("Could not set videomode on CRTC." ); |
155 | } |
156 | } else { |
157 | /* On subsequent swaps, queue the new front buffer to be flipped during |
158 | the next vertical blank |
159 | |
160 | Remember: drmModePageFlip() never blocks, it just issues the flip, |
161 | which will be done during the next vblank, or immediately if |
162 | we pass the DRM_MODE_PAGE_FLIP_ASYNC flag. |
163 | Since calling drmModePageFlip() will return EBUSY if we call it |
164 | without having completed the last issued flip, we must pass the |
165 | DRM_MODE_PAGE_FLIP_ASYNC if we don't block on EGL (egl_swapinterval = 0). |
166 | That makes it flip immediately, without waiting for the next vblank |
167 | to do so, so even if we don't block on EGL, the flip will have completed |
168 | when we get here again. */ |
169 | if (_this->egl_data->egl_swapinterval == 0 && viddata->async_pageflip_support) { |
170 | flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC; |
171 | } |
172 | |
173 | ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id, |
174 | fb_info->fb_id, flip_flags, &windata->waiting_for_flip); |
175 | |
176 | if (ret == 0) { |
177 | windata->waiting_for_flip = true; |
178 | } else { |
179 | SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d" , ret); |
180 | } |
181 | |
182 | /* Wait immediately for vsync (as if we only had two buffers). |
183 | Even if we are already doing a WaitPageflip at the beginning of this |
184 | function, this is NOT redundant because here we wait immediately |
185 | after submitting the image to the screen, reducing lag, and if |
186 | we have waited here, there won't be a pending pageflip so the |
187 | WaitPageflip at the beginning of this function will be a no-op. |
188 | Just leave it here and don't worry. |
189 | Run your SDL program with "SDL_VIDEO_DOUBLE_BUFFER=1 <program_name>" |
190 | to enable this. */ |
191 | if (windata->double_buffer) { |
192 | if (!KMSDRM_WaitPageflip(_this, windata)) { |
193 | return SDL_SetError("Immediate wait for previous pageflip failed" ); |
194 | } |
195 | } |
196 | } |
197 | |
198 | return true; |
199 | } |
200 | |
201 | SDL_EGL_MakeCurrent_impl(KMSDRM) |
202 | |
203 | #endif // SDL_VIDEO_DRIVER_KMSDRM |
204 | |