diff mbox series

[RFC,v1,2/2] spice: Add an option to forward the dmabuf directly to the encoder

Message ID 20230111053707.781484-3-vivek.kasireddy@intel.com
State New
Headers show
Series spice: Add an option to forward the dmabuf directly to the encoder | expand

Commit Message

Kasireddy, Vivek Jan. 11, 2023, 5:37 a.m. UTC
This patch adds support for gl=on and port != 0. In other words,
with this option enabled, it should be possible to stream the
content associated with the dmabuf without making any additional
copies.

The encoder (that is part of Spice Server) extracts the dmabuf
fd from the drawable (RedDrawable) which in turn gets it from
the scanout. Once the encoder is done encoding the dmabuf, it
triggers an async that would indicate to Qemu to unblock the
pipeline.

Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
---
 include/ui/spice-display.h |   2 +
 qemu-options.hx            |   6 ++-
 ui/spice-core.c            |  22 ++++++--
 ui/spice-display.c         | 106 ++++++++++++++++++++++++++-----------
 4 files changed, 101 insertions(+), 35 deletions(-)
diff mbox series

Patch

diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index e271e011da..4f9b3aa2d9 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -62,6 +62,7 @@  enum {
     QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
     QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
     QXL_COOKIE_TYPE_GL_DRAW_DONE,
+    QXL_COOKIE_TYPE_DMABUF_ENCODE_DONE,
 };
 
 typedef struct QXLCookie {
@@ -153,6 +154,7 @@  struct SimpleSpiceCursor {
 };
 
 extern bool spice_opengl;
+extern bool spice_dmabuf_encode;
 
 int qemu_spice_rect_is_empty(const QXLRect* r);
 void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
diff --git a/qemu-options.hx b/qemu-options.hx
index aab8df0922..3016f8a6f7 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2143,7 +2143,7 @@  DEF("spice", HAS_ARG, QEMU_OPTION_spice,
     "       [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n"
     "       [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
     "       [,preferred-codec=<encoder>:<codec>\n"
-    "       [,gl=[on|off]][,rendernode=<file>]\n"
+    "       [,gl=[on|off]][,rendernode=<file>][,dmabuf-encode=[on|off]]\n"
     "   enable spice\n"
     "   at least one of {port, tls-port} is mandatory\n",
     QEMU_ARCH_ALL)
@@ -2248,6 +2248,10 @@  SRST
     ``rendernode=<file>``
         DRM render node for OpenGL rendering. If not specified, it will
         pick the first available. (Since 2.9)
+
+    ``dmabuf-encode=[on|off]``
+        Forward the dmabuf directly to the encoder (Gstreamer).
+        Default is off.
 ERST
 
 DEF("portrait", 0, QEMU_OPTION_portrait,
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 6e00211e3a..c9b856b056 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -494,6 +494,9 @@  static QemuOptsList qemu_spice_opts = {
         },{
             .name = "rendernode",
             .type = QEMU_OPT_STRING,
+        },{
+            .name = "dmabuf-encode",
+            .type = QEMU_OPT_BOOL,
 #endif
         },
         { /* end of list */ }
@@ -843,11 +846,24 @@  static void qemu_spice_init(void)
     g_free(password);
 
 #ifdef HAVE_SPICE_GL
+    if (qemu_opt_get_bool(opts, "dmabuf-encode", 0)) {
+        spice_dmabuf_encode = 1;
+    }
     if (qemu_opt_get_bool(opts, "gl", 0)) {
-        if ((port != 0) || (tls_port != 0)) {
-            error_report("SPICE GL support is local-only for now and "
-                         "incompatible with -spice port/tls-port");
+        if (((port != 0) || (tls_port != 0)) && !spice_dmabuf_encode) {
+            error_report("Add dmabuf-encode=on option to enable GL streaming");
             exit(1);
+        } else if (spice_dmabuf_encode) {
+            if (port == 0 && tls_port == 0) {
+                error_report("dmabuf-encode=on is only meant to be used for "
+                             "non-local displays");
+                exit(1);
+            }
+            if (g_strcmp0(preferred_codec, "gstreamer:h264")) {
+                error_report("dmabuf-encode=on currently only works and tested"
+                             "with gstreamer:h264");
+                exit(1);
+            }
         }
         if (egl_rendernode_init(qemu_opt_get(opts, "rendernode"),
                                 DISPLAYGL_MODE_ON) != 0) {
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 494168e7fe..d02ebd7f24 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -28,6 +28,7 @@ 
 #include "ui/spice-display.h"
 
 bool spice_opengl;
+bool spice_dmabuf_encode;
 
 int qemu_spice_rect_is_empty(const QXLRect* r)
 {
@@ -117,7 +118,7 @@  void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
 }
 
 static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
-                                         QXLRect *rect)
+                                         QXLRect *rect, int fd)
 {
     SimpleSpiceUpdate *update;
     QXLDrawable *drawable;
@@ -168,15 +169,17 @@  static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
     image->bitmap.palette = 0;
     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
 
-    dest = pixman_image_create_bits(PIXMAN_LE_x8r8g8b8, bw, bh,
-                                    (void *)update->bitmap, bw * 4);
-    pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror,
-                           rect->left, rect->top, 0, 0,
-                           rect->left, rect->top, bw, bh);
-    pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest,
-                           rect->left, rect->top, 0, 0,
-                           0, 0, bw, bh);
-    pixman_image_unref(dest);
+    if (fd == 0) {
+        dest = pixman_image_create_bits(PIXMAN_LE_x8r8g8b8, bw, bh,
+                                        (void *)update->bitmap, bw * 4);
+        pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror,
+                               rect->left, rect->top, 0, 0,
+                               rect->left, rect->top, bw, bh);
+        pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest,
+                               rect->left, rect->top, 0, 0,
+                               0, 0, bw, bh);
+        pixman_image_unref(dest);
+    }
 
     cmd->type = QXL_CMD_DRAW;
     cmd->data = (uintptr_t)drawable;
@@ -220,7 +223,7 @@  static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
                         .left   = x,
                         .right  = x + bw,
                     };
-                    qemu_spice_create_one_update(ssd, &update);
+                    qemu_spice_create_one_update(ssd, &update, 0);
                     dirty_top[blk] = -1;
                 }
             } else {
@@ -241,7 +244,7 @@  static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
                 .left   = x,
                 .right  = x + bw,
             };
-            qemu_spice_create_one_update(ssd, &update);
+            qemu_spice_create_one_update(ssd, &update, 0);
             dirty_top[blk] = -1;
         }
     }
@@ -642,6 +645,7 @@  static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
     switch (cookie->type) {
 #ifdef HAVE_SPICE_GL
     case QXL_COOKIE_TYPE_GL_DRAW_DONE:
+    case QXL_COOKIE_TYPE_DMABUF_ENCODE_DONE:
     {
         SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
         qemu_bh_schedule(ssd->gl_unblock_bh);
@@ -853,7 +857,8 @@  static void spice_gl_refresh(DisplayChangeListener *dcl)
     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
     uint64_t cookie;
 
-    if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) {
+    if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con) ||
+        spice_dmabuf_encode) {
         return;
     }
 
@@ -879,6 +884,33 @@  static void spice_gl_update(DisplayChangeListener *dcl,
     ssd->gl_updates++;
 }
 
+static void spice_gl_create_update(SimpleSpiceDisplay *ssd, int fd,
+                                   uint32_t width, uint32_t height,
+                                   uint32_t stride, uint32_t format,
+                                   int y_0_top)
+{
+    uint64_t cookie;
+
+    if (spice_dmabuf_encode) {
+        QXLRect update = {
+            .top    = 0,
+            .bottom = height,
+            .left   = 0,
+            .right  = width,
+        };
+
+        qemu_mutex_lock(&ssd->lock);
+        qemu_spice_create_one_update(ssd, &update, fd);
+        cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_DMABUF_ENCODE_DONE,
+                                           0);
+        spice_qxl_dmabuf_encode_async(&ssd->qxl, fd, cookie);
+        qemu_mutex_unlock(&ssd->lock);
+    } else {
+        spice_qxl_gl_scanout(&ssd->qxl, fd, width, height,
+                             stride, format, y_0_top);
+    }
+}
+
 static void spice_gl_switch(DisplayChangeListener *dcl,
                             struct DisplaySurface *new_surface)
 {
@@ -891,6 +923,9 @@  static void spice_gl_switch(DisplayChangeListener *dcl,
     }
     ssd->ds = new_surface;
     if (ssd->ds) {
+        if (spice_dmabuf_encode) {
+            qemu_spice_create_host_primary(ssd);
+        }
         surface_gl_create_texture(ssd->gls, ssd->ds);
         fd = egl_get_fd_for_texture(ssd->ds->texture,
                                     &stride, &fourcc,
@@ -906,10 +941,10 @@  static void spice_gl_switch(DisplayChangeListener *dcl,
                                     fourcc);
 
         /* note: spice server will close the fd */
-        spice_qxl_gl_scanout(&ssd->qxl, fd,
-                             surface_width(ssd->ds),
-                             surface_height(ssd->ds),
-                             stride, fourcc, false);
+        spice_gl_create_update(ssd, fd, surface_width(ssd->ds),
+                               surface_height(ssd->ds), stride,
+                               fourcc, false);
+
         ssd->have_surface = true;
         ssd->have_scanout = false;
 
@@ -932,7 +967,9 @@  static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
     SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
 
     trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
-    spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
+    if (!spice_dmabuf_encode) {
+        spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
+    }
     qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
     ssd->have_surface = false;
     ssd->have_scanout = false;
@@ -959,8 +996,9 @@  static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
     trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
 
     /* note: spice server will close the fd */
-    spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
-                         stride, fourcc, y_0_top);
+    spice_gl_create_update(ssd, fd, backing_width, backing_height,
+                           stride, fourcc, y_0_top);
+
     qemu_spice_gl_monitor_config(ssd, x, y, w, h);
     ssd->have_surface = false;
     ssd->have_scanout = true;
@@ -1032,7 +1070,7 @@  static void qemu_spice_gl_update(DisplayChangeListener *dcl,
     bool render_cursor = false;
     bool y_0_top = false; /* FIXME */
     uint64_t cookie;
-    int fd;
+    int fd = 0;
 
     if (!ssd->have_scanout) {
         return;
@@ -1047,7 +1085,7 @@  static void qemu_spice_gl_update(DisplayChangeListener *dcl,
         egl_fb_destroy(&ssd->blit_fb);
     }
 
-    if (ssd->guest_dmabuf_refresh) {
+    if (ssd->guest_dmabuf_refresh || spice_dmabuf_encode) {
         QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
         if (render_cursor) {
             egl_dmabuf_import_texture(dmabuf);
@@ -1070,18 +1108,21 @@  static void qemu_spice_gl_update(DisplayChangeListener *dcl,
                                      dmabuf->width, dmabuf->height);
                 fd = egl_get_fd_for_texture(ssd->blit_fb.texture,
                                             &stride, &fourcc, NULL);
-                spice_qxl_gl_scanout(&ssd->qxl, fd,
-                                     dmabuf->width, dmabuf->height,
-                                     stride, fourcc, false);
+
+                spice_gl_create_update(ssd, fd, dmabuf->width, dmabuf->height,
+                                       stride, fourcc, false);
             }
         } else {
             trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id,
                                                dmabuf->width, dmabuf->height);
             /* note: spice server will close the fd, so hand over a dup */
-            spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd),
-                                 dmabuf->width, dmabuf->height,
-                                 dmabuf->stride, dmabuf->fourcc,
-                                 dmabuf->y0_top);
+            fd = dup(dmabuf->fd);
+            if (fd <= 0) {
+                return;
+            }
+            spice_gl_create_update(ssd, fd, dmabuf->width,
+                                   dmabuf->height, dmabuf->stride,
+                                   dmabuf->fourcc, dmabuf->y0_top);
         }
         qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height);
         ssd->guest_dmabuf_refresh = false;
@@ -1103,8 +1144,11 @@  static void qemu_spice_gl_update(DisplayChangeListener *dcl,
     trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
     qemu_spice_gl_block(ssd, true);
     glFlush();
-    cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
-    spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
+    if (!spice_dmabuf_encode) {
+        cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
+        spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
+    }
+    qemu_spice_wakeup(ssd);
 }
 
 static const DisplayChangeListenerOps display_listener_gl_ops = {