From: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
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).
Signed-off-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
---
include/ui/sdl2.h | 7 ++++++
meson.build | 4 +++
ui/sdl2-gl.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
ui/sdl2.c | 40 ++++++++++++++++++++++++++++++
4 files changed, 114 insertions(+)
diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h
index dbe6e3d9739b..9daf5ecffae7 100644
--- a/include/ui/sdl2.h
+++ b/include/ui/sdl2.h
@@ -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 */
diff --git a/meson.build b/meson.build
index 089cbd2bb364..1ace2bfa0ebe 100644
--- a/meson.build
+++ b/meson.build
@@ -1531,6 +1531,9 @@ else
sdl_image = not_found
endif
+# libx11 presents together with SDL or GTK libs on systems that support X11
+xlib = dependency('x11', required: false)
+
rbd = not_found
if not get_option('rbd').auto() or have_block
librados = cc.find_library('rados', required: get_option('rbd'))
@@ -2397,6 +2400,7 @@ config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
config_host_data.set('CONFIG_SDL', sdl.found())
config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found())
+config_host_data.set('CONFIG_XLIB', xlib.found())
config_host_data.set('CONFIG_SECCOMP', seccomp.found())
if seccomp.found()
config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc)
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index b1fe96d6af22..7612af18292c 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -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"
@@ -249,3 +251,64 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
SDL_GL_SwapWindow(scon->real_window);
}
+
+void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+
+ assert(scon->opengl);
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
+
+ egl_dmabuf_import_texture(dmabuf);
+ if (!qemu_dmabuf_get_texture(dmabuf)) {
+ error_report("%s: failed fd=%d", __func__, qemu_dmabuf_get_fd(dmabuf));
+ }
+
+ 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;
+}
+
+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;
+}
diff --git a/ui/sdl2.c b/ui/sdl2.c
index bd4f5a9da14a..5a1e5940c66a 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -35,6 +35,10 @@
#include "ui/win32-kbd-hook.h"
#include "qemu/log.h"
+#ifdef CONFIG_XLIB
+#include <X11/Xlib.h>
+#endif
+
static int sdl2_num_outputs;
static struct sdl2_console *sdl2_console;
@@ -120,6 +124,9 @@ 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);
}
+
+ qemu_egl_display = eglGetCurrentDisplay();
+
sdl_update_caption(scon);
}
@@ -820,6 +827,10 @@ 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,
+
+ .dpy_gl_scanout_dmabuf = sdl2_gl_scanout_dmabuf,
+ .dpy_gl_release_dmabuf = sdl2_gl_release_dmabuf,
+ .dpy_has_dmabuf = sdl2_gl_has_dmabuf,
};
static bool
@@ -847,6 +858,33 @@ 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_XLIB)
+ 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 11 over
+ * Wayland. I.e. SDL will use Wayland driver even if XWayland presents.
+ */
+ 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;
@@ -877,6 +915,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);
@@ -923,6 +962,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
sdl2_console[i].kbd = qkbd_state_init(con);
if (display_opengl) {
qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc);
+ sdl2_gl_console_init(&sdl2_console[i]);
}
register_displaychangelistener(&sdl2_console[i].dcl);
--
2.47.0
On 2024/10/25 8:33, Dmitry Osipenko wrote: > From: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> > > 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). > > Signed-off-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> > Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> > --- > include/ui/sdl2.h | 7 ++++++ > meson.build | 4 +++ > ui/sdl2-gl.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ > ui/sdl2.c | 40 ++++++++++++++++++++++++++++++ > 4 files changed, 114 insertions(+) > > diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h > index dbe6e3d9739b..9daf5ecffae7 100644 > --- a/include/ui/sdl2.h > +++ b/include/ui/sdl2.h > @@ -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 */ > diff --git a/meson.build b/meson.build > index 089cbd2bb364..1ace2bfa0ebe 100644 > --- a/meson.build > +++ b/meson.build > @@ -1531,6 +1531,9 @@ else > sdl_image = not_found > endif > > +# libx11 presents together with SDL or GTK libs on systems that support X11 > +xlib = dependency('x11', required: false) There is a line saying: x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found()) Please reuse it. > + > rbd = not_found > if not get_option('rbd').auto() or have_block > librados = cc.find_library('rados', required: get_option('rbd')) > @@ -2397,6 +2400,7 @@ config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable')) > config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack')) > config_host_data.set('CONFIG_SDL', sdl.found()) > config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) > +config_host_data.set('CONFIG_XLIB', xlib.found()) > config_host_data.set('CONFIG_SECCOMP', seccomp.found()) > if seccomp.found() > config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc) > diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c > index b1fe96d6af22..7612af18292c 100644 > --- a/ui/sdl2-gl.c > +++ b/ui/sdl2-gl.c > @@ -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" > @@ -249,3 +251,64 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, > > SDL_GL_SwapWindow(scon->real_window); > } > + > +void sdl2_gl_scanout_dmabuf(DisplayChangeListener *dcl, > + QemuDmaBuf *dmabuf) > +{ > + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); > + > + assert(scon->opengl); > + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); > + > + egl_dmabuf_import_texture(dmabuf); > + if (!qemu_dmabuf_get_texture(dmabuf)) { > + error_report("%s: failed fd=%d", __func__, qemu_dmabuf_get_fd(dmabuf)); It still continues to call sdl2_gl_scanout_texture() and I doubt that's what you meant. > + } > + > + 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; > +} > + > +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; > +} > diff --git a/ui/sdl2.c b/ui/sdl2.c > index bd4f5a9da14a..5a1e5940c66a 100644 > --- a/ui/sdl2.c > +++ b/ui/sdl2.c > @@ -35,6 +35,10 @@ > #include "ui/win32-kbd-hook.h" > #include "qemu/log.h" > > +#ifdef CONFIG_XLIB > +#include <X11/Xlib.h> > +#endif > + > static int sdl2_num_outputs; > static struct sdl2_console *sdl2_console; > > @@ -120,6 +124,9 @@ 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); > } > + > + qemu_egl_display = eglGetCurrentDisplay(); > + > sdl_update_caption(scon); > } > > @@ -820,6 +827,10 @@ 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, > + > + .dpy_gl_scanout_dmabuf = sdl2_gl_scanout_dmabuf, > + .dpy_gl_release_dmabuf = sdl2_gl_release_dmabuf, > + .dpy_has_dmabuf = sdl2_gl_has_dmabuf, > }; > > static bool > @@ -847,6 +858,33 @@ 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_XLIB) > + 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 11 over s/prefer 11 over/prefer X11 over/ Personally, I'm more concerned whether setting that hint will make an invalid argument error or something. > + * Wayland. I.e. SDL will use Wayland driver even if XWayland presents. > + */ > + 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; > @@ -877,6 +915,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); > @@ -923,6 +962,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) > sdl2_console[i].kbd = qkbd_state_init(con); > if (display_opengl) { > qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc); > + sdl2_gl_console_init(&sdl2_console[i]); > } > register_displaychangelistener(&sdl2_console[i].dcl); >
© 2016 - 2024 Red Hat, Inc.