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>
This commit is contained in:
Pierre-Eric Pelloux-Prayer
2026-03-04 16:50:30 +00:00
committed by Alex Bennée
parent 2b0e2edc83
commit 52053b7e0a
4 changed files with 125 additions and 4 deletions

View File

@@ -45,6 +45,7 @@ struct sdl2_console {
bool gui_keysym;
SDL_GLContext winctx;
QKbdState *kbd;
bool has_dmabuf;
#ifdef CONFIG_OPENGL
QemuGLShader *gls;
egl_fb guest_fb;
@@ -96,5 +97,11 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
void *d3d_tex2d);
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void sdl2_gl_release_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
bool sdl2_gl_has_dmabuf(DisplayChangeListener *dcl);
void sdl2_gl_console_init(struct sdl2_console *scon);
#endif /* SDL2_H */

View File

@@ -1946,10 +1946,8 @@ if get_option('gtk') \
endif
endif
x11 = not_found
if gtkx11.found()
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found())
endif
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found())
png = not_found
if get_option('png').allowed() and have_system
png = dependency('libpng', version: '>=1.6.34', required: get_option('png'),

View File

@@ -26,6 +26,8 @@
*/
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/error-report.h"
#include "ui/console.h"
#include "ui/input.h"
#include "ui/sdl2.h"
@@ -250,3 +252,69 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
SDL_GL_SwapWindow(scon->real_window);
}
#ifdef CONFIG_GBM
void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
const int *fds;
assert(scon->opengl);
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
egl_dmabuf_import_texture(dmabuf);
if (!qemu_dmabuf_get_texture(dmabuf)) {
fds = qemu_dmabuf_get_fds(dmabuf, NULL);
error_report("%s: failed fd=%d", __func__, fds ? fds[0] : -1);
return;
}
sdl2_gl_scanout_texture(dcl, qemu_dmabuf_get_texture(dmabuf), false,
qemu_dmabuf_get_width(dmabuf),
qemu_dmabuf_get_height(dmabuf),
0, 0,
qemu_dmabuf_get_width(dmabuf),
qemu_dmabuf_get_height(dmabuf),
NULL);
if (qemu_dmabuf_get_allow_fences(dmabuf)) {
scon->guest_fb.dmabuf = dmabuf;
}
}
void sdl2_gl_release_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
egl_dmabuf_release_texture(dmabuf);
}
bool sdl2_gl_has_dmabuf(DisplayChangeListener *dcl)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
return scon->has_dmabuf;
}
#endif
void sdl2_gl_console_init(struct sdl2_console *scon)
{
bool hidden = scon->hidden;
scon->hidden = true;
scon->surface = qemu_create_displaysurface(1, 1);
sdl2_window_create(scon);
/*
* QEMU checks whether console supports dma-buf before switching
* to the console. To break this chicken-egg problem we pre-check
* dma-buf availability beforehand using a dummy SDL window.
*/
scon->has_dmabuf = qemu_egl_has_dmabuf();
sdl2_window_destroy(scon);
qemu_free_displaysurface(scon->surface);
scon->surface = NULL;
scon->hidden = hidden;
}

View File

@@ -35,6 +35,10 @@
#include "qemu/log.h"
#include "qemu-main.h"
#ifdef CONFIG_X11
#include <X11/Xlib.h>
#endif
static int sdl2_num_outputs;
static struct sdl2_console *sdl2_console;
@@ -120,6 +124,11 @@ void sdl2_window_create(struct sdl2_console *scon)
/* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */
scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
}
#ifdef CONFIG_OPENGL
qemu_egl_display = eglGetCurrentDisplay();
#endif
sdl_update_caption(scon);
}
@@ -808,6 +817,12 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
.dpy_gl_scanout_disable = sdl2_gl_scanout_disable,
.dpy_gl_scanout_texture = sdl2_gl_scanout_texture,
.dpy_gl_update = sdl2_gl_scanout_flush,
#ifdef CONFIG_GBM
.dpy_gl_scanout_dmabuf = sdl2_gl_scanout_dmabuf,
.dpy_gl_release_dmabuf = sdl2_gl_release_dmabuf,
.dpy_has_dmabuf = sdl2_gl_has_dmabuf,
#endif
};
static bool
@@ -835,6 +850,35 @@ static void sdl2_display_early_init(DisplayOptions *o)
}
}
static void sdl2_set_hint_x11_force_egl(void)
{
#if defined(SDL_HINT_VIDEO_X11_FORCE_EGL) && defined(CONFIG_OPENGL) && \
defined(CONFIG_X11)
Display *x_disp = XOpenDisplay(NULL);
EGLDisplay egl_display;
if (!x_disp) {
return;
}
/* Prefer EGL over GLX to get dma-buf support. */
egl_display = eglGetDisplay((EGLNativeDisplayType)x_disp);
if (egl_display != EGL_NO_DISPLAY) {
/*
* Setting X11_FORCE_EGL hint doesn't make SDL to prefer X11 over
* Wayland. SDL will use Wayland driver even if XWayland presents.
* It's always safe to set the hint even if X11 is not used by SDL.
* SDL will work regardless of the hint.
*/
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
eglTerminate(egl_display);
}
XCloseDisplay(x_disp);
#endif
}
static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
{
uint8_t data = 0;
@@ -862,6 +906,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
#endif
SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1");
sdl2_set_hint_x11_force_egl();
SDL_EnableScreenSaver();
memset(&info, 0, sizeof(info));
SDL_VERSION(&info.version);
@@ -906,9 +951,12 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
#endif
sdl2_console[i].dcl.con = con;
sdl2_console[i].kbd = qkbd_state_init(con);
#ifdef CONFIG_OPENGL
if (display_opengl) {
qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc);
sdl2_gl_console_init(&sdl2_console[i]);
}
#endif
register_displaychangelistener(&sdl2_console[i].dcl);
#if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)