[PATCH v2 2/6] ui/sdl2: Implement dpy dmabuf functions

Dmitry Osipenko posted 6 patches 1 week ago
[PATCH v2 2/6] ui/sdl2: Implement dpy dmabuf functions
Posted by Dmitry Osipenko 1 week ago
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 ++++++
 ui/sdl2-gl.c      | 63 +++++++++++++++++++++++++++++++++++++++++++++++
 ui/sdl2.c         | 31 +++++++++++++++++++++++
 3 files changed, 101 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/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..607181071b84 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -120,6 +120,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 +823,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 +854,28 @@ 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(EGL_KHR_platform_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) {
+        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 +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);
@@ -923,6 +953,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
Re: [PATCH v2 2/6] ui/sdl2: Implement dpy dmabuf functions
Posted by Akihiko Odaki 4 days, 18 hours ago
On 2024/10/15 13:32, 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 ++++++
>   ui/sdl2-gl.c      | 63 +++++++++++++++++++++++++++++++++++++++++++++++
>   ui/sdl2.c         | 31 +++++++++++++++++++++++
>   3 files changed, 101 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/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..607181071b84 100644
> --- a/ui/sdl2.c
> +++ b/ui/sdl2.c
> @@ -120,6 +120,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 +823,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 +854,28 @@ 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(EGL_KHR_platform_x11)

EGL_KHR_platform_x11 is always defined when CONFIG_OPENGL is defined.
Check CONFIG_OPENGL for the availability of EGL APIs.

> +    Display *x_disp = XOpenDisplay(NULL);

The availability of Xlib must be checked.

> +    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) {
> +        SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");

SDL may not be going to use X11 but may use e.g., Wayland.

> +        eglTerminate(egl_display);
> +    }
> +
> +    XCloseDisplay(x_disp);
> +#endif
> +}
> +
>   static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
>   {
>       uint8_t data = 0;
> @@ -877,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);
> @@ -923,6 +953,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);
>
Re: [PATCH v2 2/6] ui/sdl2: Implement dpy dmabuf functions
Posted by Dmitry Osipenko 1 day, 22 hours ago
On 10/18/24 08:22, Akihiko Odaki wrote:
...
>> +    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) {
>> +        SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
> 
> SDL may not be going to use X11 but may use e.g., Wayland.

SDL uses this hint only within the X11 code, i.e. setting the hint
doesn't make SDL to use X11 instead of Wayland.

-- 
Best regards,
Dmitry