Dmitry Osipenko <[email protected]> writes:
> From: Pierre-Eric Pelloux-Prayer <[email protected]>
>
> 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 <[email protected]>
> Acked-by: Michael S. Tsirkin <[email protected]>
> Tested-by: Alex Bennée <[email protected]>
> Signed-off-by: Pierre-Eric Pelloux-Prayer <[email protected]>
> Reviewed-by: Yiwei Zhang <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> include/ui/sdl2.h | 7 +++++
> meson.build | 6 ++---
> ui/sdl2-gl.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++
> ui/sdl2.c | 42 ++++++++++++++++++++++++++++++
> 4 files changed, 117 insertions(+), 4 deletions(-)
>
> 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 3cd1d8dbc669..0ce84d45e6f3 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -1937,10 +1937,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'),
> diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
> index fbac3edbc09d..697ba2169c34 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"
> @@ -250,3 +252,67 @@ 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);
> + 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;
> +}
> +
> +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 032dc14bc398..3bb2676f847b 100644
> --- a/ui/sdl2.c
> +++ b/ui/sdl2.c
> @@ -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,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();
> +
This fails on MacOS for some reason:
o libsystem.a.p/ui_sdl2.c.o -c ../ui/sdl2.c
../ui/sdl2.c:128:5: error: use of undeclared identifier 'qemu_egl_display'
128 | qemu_egl_display = eglGetCurrentDisplay();
| ^
../ui/sdl2.c:128:24: error: call to undeclared function
'eglGetCurrentDisplay'; ISO C99 and later do not support implicit function
declarations [-Wimplicit-function-declaration]
128 | qemu_egl_display = eglGetCurrentDisplay();
| ^
2 errors generated.
See: https://gitlab.com/stsquad/qemu/-/jobs/13340079105
Not sure what the include differences are because --enable-sdl builds
fine on Linux.
> sdl_update_caption(scon);
> }
>
> @@ -808,6 +815,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
> @@ -835,6 +846,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 +902,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);
> @@ -908,6 +949,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);
--
Alex Bennée
Virtualisation Tech Lead @ Linaro