@samitouri / QOSamiQemu / commits / 52053b7e0a

ui/sdl2: Implement dpy dmabuf functions

If EGL is used, we can rely on dmabuf to import textures without doing copies. To get this working on X11, we use the existing SDL hint: SDL_HINT_VIDEO_X11_FORCE_EGL (because dmabuf can't be used with GLX). Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Reviewed-by: Yiwei Zhang <zzyiwei@gmail.com> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Message-ID: <20260303151422.977399-7-dmitry.osipenko@collabora.com> [AJB: ifdef CONFIG_OPENGL/CONFIG_GBM for non-linux hosts] Message-ID: <20260304165043.1437519-9-alex.bennee@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

Pierre-Eric Pelloux-Prayer committed Mar 4, 2026 at 16:50 UTC 52053b7e0a0e285ce3448b830053b05fb0a9b1a8
4 files changed +125 -4
include/ui/sdl2.h
+7
@@ -45,6 +45,7 @@ struct sdl2_console {
45 bool gui_keysym;
46 SDL_GLContext winctx;
47 QKbdState *kbd;
48 + bool has_dmabuf;
49 #ifdef CONFIG_OPENGL
50 QemuGLShader *gls;
51 egl_fb guest_fb;
@@ -96,5 +97,11 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
97 void *d3d_tex2d);
98 void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
99 uint32_t x, uint32_t y, uint32_t w, uint32_t h);
100 +void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl,
101 + QemuDmaBuf *dmabuf);
102 +void sdl2_gl_release_dmabuf(DisplayChangeListener *dcl,
103 + QemuDmaBuf *dmabuf);
104 +bool sdl2_gl_has_dmabuf(DisplayChangeListener *dcl);
105 +void sdl2_gl_console_init(struct sdl2_console *scon);
106
107 #endif /* SDL2_H */
meson.build
+2 -4
@@ -1946,10 +1946,8 @@ if get_option('gtk') \
1946 endif
1947 endif
1948
1949 -x11 = not_found
1950 -if gtkx11.found()
1951 - x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found())
1952 -endif
1949 +x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found())
1950 +
1951 png = not_found
1952 if get_option('png').allowed() and have_system
1953 png = dependency('libpng', version: '>=1.6.34', required: get_option('png'),
ui/sdl2-gl.c
+68
@@ -26,6 +26,8 @@
26 */
27
28 #include "qemu/osdep.h"
29 +#include "qemu/main-loop.h"
30 +#include "qemu/error-report.h"
31 #include "ui/console.h"
32 #include "ui/input.h"
33 #include "ui/sdl2.h"
@@ -250,3 +252,69 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
252
253 SDL_GL_SwapWindow(scon->real_window);
254 }
255 +
256 +#ifdef CONFIG_GBM
257 +void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl,
258 + QemuDmaBuf *dmabuf)
259 +{
260 + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
261 + const int *fds;
262 +
263 + assert(scon->opengl);
264 + SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
265 +
266 + egl_dmabuf_import_texture(dmabuf);
267 + if (!qemu_dmabuf_get_texture(dmabuf)) {
268 + fds = qemu_dmabuf_get_fds(dmabuf, NULL);
269 + error_report("%s: failed fd=%d", __func__, fds ? fds[0] : -1);
270 + return;
271 + }
272 +
273 + sdl2_gl_scanout_texture(dcl, qemu_dmabuf_get_texture(dmabuf), false,
274 + qemu_dmabuf_get_width(dmabuf),
275 + qemu_dmabuf_get_height(dmabuf),
276 + 0, 0,
277 + qemu_dmabuf_get_width(dmabuf),
278 + qemu_dmabuf_get_height(dmabuf),
279 + NULL);
280 +
281 + if (qemu_dmabuf_get_allow_fences(dmabuf)) {
282 + scon->guest_fb.dmabuf = dmabuf;
283 + }
284 +}
285 +
286 +void sdl2_gl_release_dmabuf(DisplayChangeListener *dcl,
287 + QemuDmaBuf *dmabuf)
288 +{
289 + egl_dmabuf_release_texture(dmabuf);
290 +}
291 +
292 +bool sdl2_gl_has_dmabuf(DisplayChangeListener *dcl)
293 +{
294 + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
295 +
296 + return scon->has_dmabuf;
297 +}
298 +#endif
299 +
300 +void sdl2_gl_console_init(struct sdl2_console *scon)
301 +{
302 + bool hidden = scon->hidden;
303 +
304 + scon->hidden = true;
305 + scon->surface = qemu_create_displaysurface(1, 1);
306 + sdl2_window_create(scon);
307 +
308 + /*
309 + * QEMU checks whether console supports dma-buf before switching
310 + * to the console. To break this chicken-egg problem we pre-check
311 + * dma-buf availability beforehand using a dummy SDL window.
312 + */
313 + scon->has_dmabuf = qemu_egl_has_dmabuf();
314 +
315 + sdl2_window_destroy(scon);
316 + qemu_free_displaysurface(scon->surface);
317 +
318 + scon->surface = NULL;
319 + scon->hidden = hidden;
320 +}
ui/sdl2.c
+48
@@ -35,6 +35,10 @@
35 #include "qemu/log.h"
36 #include "qemu-main.h"
37
38 +#ifdef CONFIG_X11
39 +#include <X11/Xlib.h>
40 +#endif
41 +
42 static int sdl2_num_outputs;
43 static struct sdl2_console *sdl2_console;
44
@@ -120,6 +124,11 @@ void sdl2_window_create(struct sdl2_console *scon)
124 /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */
125 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
126 }
127 +
128 +#ifdef CONFIG_OPENGL
129 + qemu_egl_display = eglGetCurrentDisplay();
130 +#endif
131 +
132 sdl_update_caption(scon);
133 }
134
@@ -808,6 +817,12 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
817 .dpy_gl_scanout_disable = sdl2_gl_scanout_disable,
818 .dpy_gl_scanout_texture = sdl2_gl_scanout_texture,
819 .dpy_gl_update = sdl2_gl_scanout_flush,
820 +
821 +#ifdef CONFIG_GBM
822 + .dpy_gl_scanout_dmabuf = sdl2_gl_scanout_dmabuf,
823 + .dpy_gl_release_dmabuf = sdl2_gl_release_dmabuf,
824 + .dpy_has_dmabuf = sdl2_gl_has_dmabuf,
825 +#endif
826 };
827
828 static bool
@@ -835,6 +850,35 @@ static void sdl2_display_early_init(DisplayOptions *o)
850 }
851 }
852
853 +static void sdl2_set_hint_x11_force_egl(void)
854 +{
855 +#if defined(SDL_HINT_VIDEO_X11_FORCE_EGL) && defined(CONFIG_OPENGL) && \
856 + defined(CONFIG_X11)
857 + Display *x_disp = XOpenDisplay(NULL);
858 + EGLDisplay egl_display;
859 +
860 + if (!x_disp) {
861 + return;
862 + }
863 +
864 + /* Prefer EGL over GLX to get dma-buf support. */
865 + egl_display = eglGetDisplay((EGLNativeDisplayType)x_disp);
866 +
867 + if (egl_display != EGL_NO_DISPLAY) {
868 + /*
869 + * Setting X11_FORCE_EGL hint doesn't make SDL to prefer X11 over
870 + * Wayland. SDL will use Wayland driver even if XWayland presents.
871 + * It's always safe to set the hint even if X11 is not used by SDL.
872 + * SDL will work regardless of the hint.
873 + */
874 + SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
875 + eglTerminate(egl_display);
876 + }
877 +
878 + XCloseDisplay(x_disp);
879 +#endif
880 +}
881 +
882 static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
883 {
884 uint8_t data = 0;
@@ -862,6 +906,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
906 SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
907 #endif
908 SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1");
909 + sdl2_set_hint_x11_force_egl();
910 SDL_EnableScreenSaver();
911 memset(&info, 0, sizeof(info));
912 SDL_VERSION(&info.version);
@@ -906,9 +951,12 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
951 #endif
952 sdl2_console[i].dcl.con = con;
953 sdl2_console[i].kbd = qkbd_state_init(con);
954 +#ifdef CONFIG_OPENGL
955 if (display_opengl) {
956 qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc);
957 + sdl2_gl_console_init(&sdl2_console[i]);
958 }
959 +#endif
960 register_displaychangelistener(&sdl2_console[i].dcl);
961
962 #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)