All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2)
@ 2021-09-13 22:20 Vivek Kasireddy
  2021-09-13 22:20 ` [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE Vivek Kasireddy
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Vivek Kasireddy @ 2021-09-13 22:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Dongwon Kim, Satyeshwar Singh, Vivek Kasireddy, Tina Zhang,
	Gerd Hoffmann, Marc-André Lureau

Why does Qemu need a new Wayland UI backend?
The main reason why there needs to be a plain and simple Wayland backend
for Qemu UI is to eliminate the Blit (aka GPU copy) that happens if using
a toolkit like GTK or SDL (because they use EGL). The Blit can be eliminated
by sharing the dmabuf fd -- associated with the Guest scanout buffer --
directly with the Host compositor via the linux-dmabuf (unstable) protocol.
Once properly integrated, it would be potentially possible to have the
scanout buffer created by the Guest compositor be placed directly on a
hardware plane on the Host thereby improving performance. Only Guest 
compositors that use multiple back buffers (at-least 1 front and 1 back)
and virtio-gpu would benefit from this work.

v2:
- Add support for handling/forwarding keyboard and mouse events.
- Augment handling of events to ensure that they are not lost in a
  multi-threaded environment.
- Rebase

Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Cc: Tina Zhang <tina.zhang@intel.com>
Cc: Satyeshwar Singh <satyeshwar.singh@intel.com>

Vivek Kasireddy (2):
  virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE
  ui: Add a plain Wayland backend for Qemu UI

 configure                                   |   8 +-
 hw/display/virtio-gpu-base.c                |   3 +
 hw/display/virtio-gpu-udmabuf.c             |   4 +-
 hw/display/virtio-gpu.c                     |  54 +-
 include/hw/virtio/virtio-gpu.h              |   6 +
 include/standard-headers/linux/virtio_gpu.h |   2 +
 meson.build                                 |  33 +
 meson_options.txt                           |   2 +
 qapi/ui.json                                |   3 +
 ui/meson.build                              |  52 ++
 ui/wayland.c                                | 628 ++++++++++++++++++++
 11 files changed, 791 insertions(+), 4 deletions(-)
 create mode 100644 ui/wayland.c

-- 
2.30.2



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE
  2021-09-13 22:20 [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Vivek Kasireddy
@ 2021-09-13 22:20 ` Vivek Kasireddy
  2021-09-13 22:20 ` [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Vivek Kasireddy
  2021-09-14 16:29 ` [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Daniel P. Berrangé
  2 siblings, 0 replies; 8+ messages in thread
From: Vivek Kasireddy @ 2021-09-13 22:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Vivek Kasireddy, Gerd Hoffmann

To support this feature, we add a new queue named rel_fenceq to
enqueue finished set_scanout_blob cmds.

Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
---
 hw/display/virtio-gpu-base.c                |  3 ++
 hw/display/virtio-gpu-udmabuf.c             |  4 +-
 hw/display/virtio-gpu.c                     | 54 ++++++++++++++++++++-
 include/hw/virtio/virtio-gpu.h              |  6 +++
 include/standard-headers/linux/virtio_gpu.h |  2 +
 5 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index c8da4806e0..0218a6d3d2 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -211,6 +211,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features,
     if (virtio_gpu_blob_enabled(g->conf)) {
         features |= (1 << VIRTIO_GPU_F_RESOURCE_BLOB);
     }
+    if (virtio_gpu_relfence_enabled(g->conf)) {
+        features |= (1 << VIRTIO_GPU_F_RELEASE_FENCE);
+    }
 
     return features;
 }
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index c6f7f58784..adb3fd4e0c 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -157,7 +157,7 @@ void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res)
     }
 }
 
-static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
+void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
 {
     struct virtio_gpu_scanout *scanout;
 
@@ -216,7 +216,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
                         new_primary->buf.height);
     dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf);
 
-    if (old_primary) {
+    if (old_primary && !virtio_gpu_relfence_enabled(g->parent_obj.conf)) {
         virtio_gpu_free_dmabuf(g, old_primary);
     }
 
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 182e0868b0..115c7033aa 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -771,6 +771,11 @@ static void virtio_gpu_set_scanout_blob(VirtIOGPU *g,
 
     virtio_gpu_do_set_scanout(g, ss.scanout_id,
                               &fb, res, &ss.r, &cmd->error);
+
+    if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) {
+        cmd->finished = true;
+        cmd->error = VIRTIO_GPU_RESP_OK_NODATA;
+    }
 }
 
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
@@ -1038,12 +1043,43 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
                 fprintf(stderr, "inflight: %3d (+)\r", g->inflight);
             }
         } else {
-            g_free(cmd);
+            if (cmd->cmd_hdr.type == VIRTIO_GPU_CMD_SET_SCANOUT_BLOB &&
+                cmd->error == VIRTIO_GPU_RESP_OK_NODATA) {
+                QTAILQ_INSERT_TAIL(&g->rel_fenceq, cmd, next);
+            } else {
+                g_free(cmd);
+            }
         }
     }
     g->processing_cmdq = false;
 }
 
+static void virtio_gpu_process_rel_fenceq(VirtIOGPU *g)
+{
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_set_scanout_blob ss;
+    struct virtio_gpu_ctrl_command *cmd, *tmp;
+    VGPUDMABuf *dmabuf, *temp;
+
+    QTAILQ_FOREACH_SAFE(dmabuf, &g->dmabuf.bufs, next, temp) {
+        if (dmabuf->buf.fence_fd < 0) {
+            QTAILQ_FOREACH_SAFE(cmd, &g->rel_fenceq, next, tmp) {
+                VIRTIO_GPU_FILL_CMD(ss);
+                virtio_gpu_bswap_32(&ss, sizeof(ss));
+                res = virtio_gpu_find_check_resource(g, ss.resource_id, true,
+                                                     __func__, &cmd->error);
+                if (res && dmabuf->buf.fd == res->dmabuf_fd) {
+                    virtio_gpu_ctrl_response_nodata(g, cmd,
+                                                    VIRTIO_GPU_RESP_OK_NODATA);
+                    QTAILQ_REMOVE(&g->rel_fenceq, cmd, next);
+                    g_free(cmd);
+                }
+            }
+            virtio_gpu_free_dmabuf(g, dmabuf);
+        }
+    }
+}
+
 static void virtio_gpu_process_fenceq(VirtIOGPU *g)
 {
     struct virtio_gpu_ctrl_command *cmd, *tmp;
@@ -1064,6 +1100,12 @@ static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
 {
     VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
 
+    if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) {
+        virtio_gpu_process_rel_fenceq(g);
+    }
+    if (g->parent_obj.renderer_blocked) {
+        return;
+    }
     virtio_gpu_process_fenceq(g);
     virtio_gpu_process_cmdq(g);
 }
@@ -1323,6 +1365,13 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
         }
     }
 
+    if (virtio_gpu_relfence_enabled(g->parent_obj.conf)) {
+        if (!virtio_gpu_blob_enabled(g->parent_obj.conf)) {
+            error_setg(errp, "cannot enable relfence without blob resources");
+            return;
+        }
+    }
+
     if (!virtio_gpu_base_device_realize(qdev,
                                         virtio_gpu_handle_ctrl_cb,
                                         virtio_gpu_handle_cursor_cb,
@@ -1337,6 +1386,7 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     QTAILQ_INIT(&g->reslist);
     QTAILQ_INIT(&g->cmdq);
     QTAILQ_INIT(&g->fenceq);
+    QTAILQ_INIT(&g->rel_fenceq);
 }
 
 void virtio_gpu_reset(VirtIODevice *vdev)
@@ -1418,6 +1468,8 @@ static Property virtio_gpu_properties[] = {
                      256 * MiB),
     DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
                     VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
+    DEFINE_PROP_BIT("relfence", VirtIOGPU, parent_obj.conf.flags,
+                    VIRTIO_GPU_FLAG_RELFENCE_ENABLED, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 24c6628944..60dd2b8985 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -90,6 +90,7 @@ enum virtio_gpu_base_conf_flags {
     VIRTIO_GPU_FLAG_EDID_ENABLED,
     VIRTIO_GPU_FLAG_DMABUF_ENABLED,
     VIRTIO_GPU_FLAG_BLOB_ENABLED,
+    VIRTIO_GPU_FLAG_RELFENCE_ENABLED,
 };
 
 #define virtio_gpu_virgl_enabled(_cfg) \
@@ -102,6 +103,8 @@ enum virtio_gpu_base_conf_flags {
     (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED))
 #define virtio_gpu_blob_enabled(_cfg) \
     (_cfg.flags & (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED))
+#define virtio_gpu_relfence_enabled(_cfg) \
+    (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RELFENCE_ENABLED))
 
 struct virtio_gpu_base_conf {
     uint32_t max_outputs;
@@ -170,6 +173,7 @@ struct VirtIOGPU {
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
+    QTAILQ_HEAD(, virtio_gpu_ctrl_command) rel_fenceq;
 
     uint64_t hostmem;
 
@@ -274,6 +278,8 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
                              uint32_t scanout_id,
                              struct virtio_gpu_simple_resource *res,
                              struct virtio_gpu_framebuffer *fb);
+void virtio_gpu_free_dmabuf(VirtIOGPU *g,
+                            VGPUDMABuf *dmabuf);
 
 /* virtio-gpu-3d.c */
 void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h
index 1357e4774e..4331f999c4 100644
--- a/include/standard-headers/linux/virtio_gpu.h
+++ b/include/standard-headers/linux/virtio_gpu.h
@@ -60,6 +60,8 @@
  */
 #define VIRTIO_GPU_F_RESOURCE_BLOB       3
 
+#define VIRTIO_GPU_F_RELEASE_FENCE       4
+
 enum virtio_gpu_ctrl_type {
 	VIRTIO_GPU_UNDEFINED = 0,
 
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI
  2021-09-13 22:20 [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Vivek Kasireddy
  2021-09-13 22:20 ` [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE Vivek Kasireddy
@ 2021-09-13 22:20 ` Vivek Kasireddy
  2021-09-14 15:21   ` Eric Blake
  2021-09-14 16:15   ` Daniel P. Berrangé
  2021-09-14 16:29 ` [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Daniel P. Berrangé
  2 siblings, 2 replies; 8+ messages in thread
From: Vivek Kasireddy @ 2021-09-13 22:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Vivek Kasireddy, Gerd Hoffmann

Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
---
 configure         |   8 +-
 meson.build       |  33 +++
 meson_options.txt |   2 +
 qapi/ui.json      |   3 +
 ui/meson.build    |  52 ++++
 ui/wayland.c      | 628 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 725 insertions(+), 1 deletion(-)
 create mode 100644 ui/wayland.c

diff --git a/configure b/configure
index da2501489f..02339681dc 100755
--- a/configure
+++ b/configure
@@ -406,6 +406,7 @@ cfi_debug="false"
 seccomp="auto"
 glusterfs="auto"
 gtk="auto"
+wayland="auto"
 tls_priority="NORMAL"
 gnutls="auto"
 nettle="auto"
@@ -1383,6 +1384,10 @@ for opt do
   ;;
   --enable-gtk) gtk="enabled"
   ;;
+  --disable-wayland) wayland="disabled"
+  ;;
+  --enable-wayland) wayland="enabled"
+  ;;
   --tls-priority=*) tls_priority="$optarg"
   ;;
   --disable-gnutls) gnutls="disabled"
@@ -1868,6 +1873,7 @@ disabled with --disable-FEATURE, default is enabled if available
   sdl             SDL UI
   sdl-image       SDL Image support for icons
   gtk             gtk UI
+  wayland         Wayland UI
   vte             vte support for the gtk UI
   curses          curses UI
   iconv           font glyph conversion support
@@ -5191,7 +5197,7 @@ if test "$skip_meson" = no; then
         -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \
         -Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf -Dnvmm=$nvmm \
         -Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \
-        -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \
+        -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dwayland=$wayland -Dsdl_image=$sdl_image \
         -Dlibusb=$libusb -Dsmartcard=$smartcard -Dusb_redir=$usb_redir -Dvte=$vte \
         -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \
         -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f -Dvirtiofsd=$virtiofsd \
diff --git a/meson.build b/meson.build
index 7d7d14a4bc..cda10b7d0f 100644
--- a/meson.build
+++ b/meson.build
@@ -927,6 +927,37 @@ if gtkx11.found()
   x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
                    kwargs: static_kwargs)
 endif
+
+wayland = not_found
+if not get_option('wayland').auto()
+  wlclientdep = dependency('wayland-client', version: '>= 1.18.90',
+                           method: 'pkg-config',
+                           required: get_option('wayland'),
+                           kwargs: static_kwargs)
+  wlprotocolsdep = dependency('wayland-protocols', version: '>= 1.14.91',
+                              method: 'pkg-config',
+                              required: get_option('wayland'),
+                              kwargs: static_kwargs)
+
+  if not wlprotocolsdep.found()
+    wlproto_dir = subproject('wayland-protocols').get_variable('wayland_protocols_srcdir')
+  else
+    wlproto_dir = wlprotocolsdep.get_pkgconfig_variable('pkgdatadir')
+  endif
+
+  wayland = declare_dependency(dependencies: [wlclientdep, wlprotocolsdep])
+endif
+
+if wayland.found() and get_option('sdl').enabled()
+  error('Wayland and SDL cannot be enabled at the same time')
+endif
+if wayland.found() and get_option('gtk').enabled()
+  error('Wayland and GTK+ cannot be enabled at the same time')
+endif
+if wayland.found() and get_option('cocoa').enabled()
+  error('Wayland and Cocoa cannot be enabled at the same time')
+endif
+
 vnc = not_found
 png = not_found
 jpeg = not_found
@@ -1256,6 +1287,7 @@ if glusterfs.found()
   config_host_data.set('CONFIG_GLUSTERFS_IOCB_HAS_STAT', glusterfs_iocb_has_stat)
 endif
 config_host_data.set('CONFIG_GTK', gtk.found())
+config_host_data.set('CONFIG_WAYLAND', wayland.found())
 config_host_data.set('CONFIG_VTE', vte.found())
 config_host_data.set('CONFIG_LIBATTR', have_old_libattr)
 config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
@@ -3052,6 +3084,7 @@ summary_info += {'SDL support':       sdl.found()}
 summary_info += {'SDL image support': sdl_image.found()}
 # TODO: add back version
 summary_info += {'GTK support':       gtk.found()}
+summary_info += {'Wayland support':   wayland.found()}
 summary_info += {'pixman':            pixman.found()}
 # TODO: add back version
 summary_info += {'VTE support':       vte.found()}
diff --git a/meson_options.txt b/meson_options.txt
index a9a9b8f4c6..6c0e27e83b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -104,6 +104,8 @@ option('rbd', type : 'feature', value : 'auto',
        description: 'Ceph block device driver')
 option('gtk', type : 'feature', value : 'auto',
        description: 'GTK+ user interface')
+option('wayland', type : 'feature', value : 'auto',
+       description: 'Wayland user interface')
 option('sdl', type : 'feature', value : 'auto',
        description: 'SDL user interface')
 option('sdl_image', type : 'feature', value : 'auto',
diff --git a/qapi/ui.json b/qapi/ui.json
index b2cf7a6759..8da0baa0bd 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1112,6 +1112,8 @@
 #                DRI device. Graphical display need to be paired with
 #                VNC or Spice. (Since 3.1)
 #
+# @wayland: The Wayland user interface.
+#
 # @curses: Display video output via curses.  For graphics device
 #          models which support a text mode, QEMU can display this
 #          output using a curses/ncurses interface. Nothing is
@@ -1135,6 +1137,7 @@
     { 'name': 'none' },
     { 'name': 'gtk', 'if': 'CONFIG_GTK' },
     { 'name': 'sdl', 'if': 'CONFIG_SDL' },
+    { 'name': 'wayland', 'if': 'CONFIG_WAYLAND' },
     { 'name': 'egl-headless',
               'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
     { 'name': 'curses', 'if': 'CONFIG_CURSES' },
diff --git a/ui/meson.build b/ui/meson.build
index a73beb0e54..86fc324c82 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -64,6 +64,58 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found()
   ui_modules += {'egl-headless' : egl_headless_ss}
 endif
 
+wayland_scanner = find_program('wayland-scanner')
+proto_sources = [
+  ['xdg-shell', 'stable', ],
+  ['fullscreen-shell', 'unstable', 'v1', ],
+  ['linux-dmabuf', 'unstable', 'v1', ],
+]
+wayland_headers = []
+wayland_proto_sources = []
+
+if wayland.found()
+  foreach p: proto_sources
+    proto_name = p.get(0)
+    proto_stability = p.get(1)
+
+    if proto_stability == 'stable'
+      output_base = proto_name
+      input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
+    else
+      proto_version = p.get(2)
+      output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability, proto_version)
+      input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
+    endif
+
+    wayland_headers += custom_target('@0@ client header'.format(output_base),
+      input: input,
+      output: '@0@-client-protocol.h'.format(output_base),
+      command: [
+        wayland_scanner,
+        'client-header',
+        '@INPUT@', '@OUTPUT@',
+      ], build_by_default: true
+    )
+
+    wayland_proto_sources += custom_target('@0@ source'.format(output_base),
+      input: input,
+      output: '@0@-protocol.c'.format(output_base),
+      command: [
+        wayland_scanner,
+        'private-code',
+        '@INPUT@', '@OUTPUT@',
+      ], build_by_default: true
+    )
+  endforeach
+endif
+
+if wayland.found()
+  wayland_ss = ss.source_set()
+  wayland_ss.add(when: wayland, if_true: files('wayland.c', 'xdg-shell-protocol.c', 'fullscreen-shell-unstable-v1-protocol.c','linux-dmabuf-unstable-v1-protocol.c'))
+  #wayland_ss.add(when: wayland, if_true: files('wayland.c'), [wayland_proto_sources])
+  ui_modules += {'wayland' : wayland_ss}
+endif
+
 if gtk.found()
   softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c'))
 
diff --git a/ui/wayland.c b/ui/wayland.c
new file mode 100644
index 0000000000..9e011bd701
--- /dev/null
+++ b/ui/wayland.c
@@ -0,0 +1,628 @@
+/*
+ * Wayland UI -- A simple Qemu UI backend to share Guest Compositor buffers
+ * with Host Wayland compositors directly.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Mostly (boilerplate) based on:
+ * https://cgit.freedesktop.org/wayland/weston/tree/clients/simple-dmabuf-egl.c
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "ui/kbd-state.h"
+#include "keymaps.h"
+#include <linux/input.h>
+#include <wayland-client.h>
+#include "xdg-shell-client-protocol.h"
+#include "fullscreen-shell-unstable-v1-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
+
+#define MAX_BUFFERS 4
+
+typedef struct wayland_display {
+    struct wl_display *display;
+    struct wl_registry *registry;
+    struct wl_compositor *compositor;
+    struct xdg_wm_base *wm_base;
+    struct zwp_fullscreen_shell_v1 *fshell;
+    struct zwp_linux_dmabuf_v1 *dmabuf;
+} wayland_display;
+
+typedef struct wayland_buffer {
+    QemuConsole *con;
+    QemuDmaBuf *dmabuf;
+    struct wl_buffer *buffer;
+    bool busy;
+} wayland_buffer;
+
+typedef struct wayland_window {
+    wayland_display *display;
+    DisplayChangeListener dcl;
+    QKbdState *kbd;
+    struct wl_surface *surface;
+    struct xdg_surface *xdg_surface;
+    struct xdg_toplevel *xdg_toplevel;
+    struct wl_callback *callback;
+    wayland_buffer buffers[MAX_BUFFERS];
+    wayland_buffer *new_buffer;
+    int width, height;
+    bool wait_for_configure;
+    int last_x, last_y;
+    bool last_set;
+} wayland_window;
+
+typedef struct wayland_input {
+    wayland_window *focus;
+    struct wl_seat *seat;
+    struct wl_pointer *pointer;
+    struct wl_keyboard *keyboard;
+    const uint16_t *keycode_map;
+    size_t keycode_maplen;
+} wayland_input;
+
+static const struct wl_callback_listener frame_listener;
+
+static void xdg_surface_handle_configure(void *data,
+                                         struct xdg_surface *surface,
+			                 uint32_t serial)
+{
+    wayland_window *window = data;
+
+    xdg_surface_ack_configure(surface, serial);
+    window->wait_for_configure = false;
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+    xdg_surface_handle_configure,
+};
+
+static void xdg_toplevel_handle_configure(void *data,
+                                          struct xdg_toplevel *toplevel,
+			                  int32_t width, int32_t height,
+			                  struct wl_array *states)
+{
+}
+
+static void xdg_toplevel_handle_close(void *data,
+                                      struct xdg_toplevel *xdg_toplevel)
+{
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+    xdg_toplevel_handle_configure,
+    xdg_toplevel_handle_close,
+};
+
+static void wayland_refresh(DisplayChangeListener *dcl)
+{
+    graphic_hw_update(dcl->con);
+}
+
+static QEMUGLContext wayland_create_context(DisplayChangeListener *dcl,
+                                            QEMUGLParams *params)
+{
+    return NULL;
+}
+
+static void wayland_destroy_context(DisplayChangeListener *dcl,
+                                    QEMUGLContext ctx)
+{
+}
+
+static int wayland_make_context_current(DisplayChangeListener *dcl,
+                                        QEMUGLContext ctx)
+{
+    return 0;
+}
+
+static void wayland_scanout_disable(DisplayChangeListener *dcl)
+{
+}
+
+static void wayland_scanout_texture(DisplayChangeListener *dcl,
+                                    uint32_t backing_id,
+                                    bool backing_y_0_top,
+                                    uint32_t backing_width,
+                                    uint32_t backing_height,
+                                    uint32_t x, uint32_t y,
+                                    uint32_t w, uint32_t h)
+{
+}
+
+static void wayland_release_dmabuf(DisplayChangeListener *dcl,
+                                   QemuDmaBuf *dmabuf)
+{
+}
+
+static void wayland_window_redraw(void *data, struct wl_callback *callback,
+                                  uint32_t time)
+{
+    wayland_window *window = data;
+
+    if (callback) {
+        assert(window->callback == callback);
+        wl_callback_destroy(callback);
+        window->callback = NULL;
+    }
+    graphic_hw_gl_block(window->dcl.con, false);
+    graphic_hw_gl_flushed(window->dcl.con);
+}
+
+static const struct wl_callback_listener frame_listener = {
+    wayland_window_redraw
+};
+
+static void wayland_buffer_release(void *data, struct wl_buffer *buf)
+{
+    wayland_buffer *buffer = data;
+    QemuDmaBuf *dmabuf = buffer->dmabuf;
+
+    dmabuf->fence_fd = -1;
+    graphic_hw_gl_flushed(buffer->con);
+    buffer->busy = false;
+    wl_buffer_destroy(buf);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+    wayland_buffer_release
+};
+
+static wayland_buffer *window_next_buffer(wayland_window *window)
+{
+    int i;
+
+    for (i = 0; i < MAX_BUFFERS; i++) {
+        if (!window->buffers[i].busy) {
+            return &window->buffers[i];
+        }
+    }
+    return NULL;
+}
+
+static void wayland_scanout_dmabuf(DisplayChangeListener *dcl,
+                                   QemuDmaBuf *dmabuf)
+{
+    wayland_window *window = container_of(dcl, wayland_window, dcl);
+    wayland_display *display = window->display;
+    wayland_buffer *buffer = window_next_buffer(window);
+    struct zwp_linux_buffer_params_v1 *params;
+
+    if (!buffer) {
+        error_report("Can't find free buffer\n");
+        exit(1);
+    }
+    params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
+    zwp_linux_buffer_params_v1_add(params, dmabuf->fd, 0, 0, dmabuf->stride,
+                                   0, 0);
+    buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params,
+                                                             dmabuf->width,
+                                                             dmabuf->height,
+                                                             dmabuf->fourcc,
+                                                             0);
+    zwp_linux_buffer_params_v1_destroy(params);
+    buffer->dmabuf = dmabuf;
+    buffer->con = window->dcl.con;
+    window->new_buffer = buffer;
+    window->width = dmabuf->width;
+    window->height = dmabuf->height;
+    dmabuf->fence_fd = 1;
+    wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
+}
+
+static void wayland_scanout_flush(DisplayChangeListener *dcl,
+                                  uint32_t x, uint32_t y,
+                                  uint32_t w, uint32_t h)
+{
+    wayland_window *window = container_of(dcl, wayland_window, dcl);
+    struct wl_region *region;
+
+    graphic_hw_gl_block(window->new_buffer->con, true);
+    region = wl_compositor_create_region(window->display->compositor);
+    wl_region_add(region, 0, 0, window->width, window->height);
+    wl_surface_set_opaque_region(window->surface, region);
+    wl_region_destroy(region);
+
+    window->callback = wl_surface_frame(window->surface);
+    wl_callback_add_listener(window->callback, &frame_listener, window);
+    wl_surface_attach(window->surface, window->new_buffer->buffer, 0, 0);
+    wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+    wl_surface_commit(window->surface);
+    wl_display_flush(window->display->display);
+    window->new_buffer->busy = true;
+}
+
+static void dmabuf_modifier(void *data,
+                            struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
+                            uint32_t format, uint32_t modifier_hi,
+                            uint32_t modifier_lo)
+{
+}
+
+static void dmabuf_format(void *data,
+                          struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
+                          uint32_t format)
+{
+}
+
+static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
+    dmabuf_format,
+    dmabuf_modifier
+};
+
+static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell,
+                             uint32_t serial)
+{
+    xdg_wm_base_pong(shell, serial);
+}
+
+static const struct xdg_wm_base_listener wm_base_listener = {
+    xdg_wm_base_ping,
+};
+
+static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
+                                 uint32_t serial, struct wl_surface *surface,
+                                 wl_fixed_t sx, wl_fixed_t sy)
+{
+    wayland_input *input = data;
+
+    if (!surface) {
+        return;
+    }
+    input->focus = wl_surface_get_user_data(surface);
+    wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
+}
+
+static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
+                                 uint32_t serial, struct wl_surface *surface)
+{
+    wayland_input *input = data;
+
+    if (!surface || !input->focus) {
+        return;
+    }
+    input->focus = NULL;
+}
+
+static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
+                                  uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
+{
+    wayland_input *input = data;
+    wayland_window *window = input->focus;
+    int x, y;
+
+    if (!window) {
+        return;
+    }
+    x = wl_fixed_to_int(sx);
+    y = wl_fixed_to_int(sy);
+    if (qemu_input_is_absolute()) {
+        qemu_input_queue_abs(window->dcl.con, INPUT_AXIS_X,
+                             x, 0, window->width);
+        qemu_input_queue_abs(window->dcl.con, INPUT_AXIS_Y,
+                             y, 0, window->height);
+        qemu_input_event_sync();
+    } else if (window->last_set) {
+        qemu_input_queue_rel(window->dcl.con, INPUT_AXIS_X,
+                             x - window->last_x);
+        qemu_input_queue_rel(window->dcl.con, INPUT_AXIS_Y,
+                             y - window->last_y);
+        qemu_input_event_sync();
+    }
+    window->last_x = x;
+    window->last_y = y;
+    window->last_set = true;
+}
+
+static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+                                  uint32_t serial, uint32_t time,
+                                  uint32_t button, uint32_t state)
+{
+    wayland_input *input = data;
+    wayland_window *window = input->focus;
+    InputButton btn;
+
+    if (!window) {
+        return;
+    }
+    if (button == BTN_LEFT) {
+        btn = INPUT_BUTTON_LEFT;
+    } else if (button == BTN_MIDDLE) {
+        btn = INPUT_BUTTON_MIDDLE;
+    } else if (button == BTN_RIGHT) {
+        btn = INPUT_BUTTON_RIGHT;
+    } else if (button == BTN_SIDE) {
+        btn = INPUT_BUTTON_SIDE;
+    } else {
+        return;
+    }
+    qemu_input_queue_btn(window->dcl.con, btn,
+                         state == WL_POINTER_BUTTON_STATE_PRESSED);
+    qemu_input_event_sync();
+}
+
+static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+                                uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+    wayland_input *input = data;
+    wayland_window *window = input->focus;
+    InputButton btn;
+    int delta_y = 0;
+
+    if (!window) {
+        return;
+    }
+    if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+        delta_y = wl_fixed_to_int(value) / 10;
+    }
+    if (delta_y > 0) {
+        btn = INPUT_BUTTON_WHEEL_DOWN;
+    } else {
+        btn = INPUT_BUTTON_WHEEL_UP;
+    }
+
+    qemu_input_queue_btn(window->dcl.con, btn, true);
+    qemu_input_event_sync();
+    qemu_input_queue_btn(window->dcl.con, btn, false);
+    qemu_input_event_sync();
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+    pointer_handle_enter,
+    pointer_handle_leave,
+    pointer_handle_motion,
+    pointer_handle_button,
+    pointer_handle_axis,
+};
+
+static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+                                   uint32_t format, int fd, uint32_t size)
+{
+    close(fd);
+}
+
+static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+                                  uint32_t serial, struct wl_surface *surface,
+                                  struct wl_array *keys)
+{
+}
+
+static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+                                  uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+                                uint32_t serial, uint32_t time, uint32_t key,
+                                uint32_t state)
+{
+    wayland_input *input = data;
+    wayland_window *window = input->focus;
+    int keycode = key + 8, qcode;
+
+    if (!window || !input->keycode_map ||
+        keycode > input->keycode_maplen) {
+        return;
+    }
+    qcode = input->keycode_map[keycode];
+    qkbd_state_key_event(window->kbd, qcode, state);
+}
+
+static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+                                      uint32_t serial, uint32_t mods_depressed,
+                                      uint32_t mods_latched, uint32_t mods_locked,
+                                      uint32_t group)
+{
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+    keyboard_handle_keymap,
+    keyboard_handle_enter,
+    keyboard_handle_leave,
+    keyboard_handle_key,
+    keyboard_handle_modifiers,
+};
+
+static void seat_handle_capabilities(void *data, struct wl_seat *seat,
+                                     enum wl_seat_capability caps)
+{
+    wayland_input *input = data;
+
+    if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+        input->pointer = wl_seat_get_pointer(seat);
+        wl_pointer_add_listener(input->pointer, &pointer_listener, input);
+    } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
+        wl_pointer_destroy(input->pointer);
+        input->pointer = NULL;
+    }
+
+    if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+        input->keyboard = wl_seat_get_keyboard(seat);
+        wl_keyboard_add_listener(input->keyboard, &keyboard_listener, input);
+    } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+        wl_keyboard_destroy(input->keyboard);
+        input->keyboard = NULL;
+    }
+}
+
+static const struct wl_seat_listener seat_listener = {
+    seat_handle_capabilities,
+};
+
+static void registry_handle_global(void *data, struct wl_registry *registry,
+                                   uint32_t id, const char *interface,
+                                   uint32_t version)
+{
+    wayland_display *display = data;
+    wayland_input *input;
+
+    if (strcmp(interface, "wl_compositor") == 0) {
+        display->compositor = wl_registry_bind(registry,
+			                 id, &wl_compositor_interface, 1);
+    } else if (strcmp(interface, "xdg_wm_base") == 0) {
+	display->wm_base = wl_registry_bind(registry,
+				      id, &xdg_wm_base_interface, 1);
+	xdg_wm_base_add_listener(display->wm_base, &wm_base_listener, display);
+    } else if (strcmp(interface, "wl_seat") == 0) {
+        input = g_new0(wayland_input, 1);
+        input->keycode_maplen = qemu_input_map_xorgevdev_to_qcode_len;
+        input->keycode_map = qemu_input_map_xorgevdev_to_qcode;
+        input->seat = wl_registry_bind(registry, id,
+                                       &wl_seat_interface, 1);
+        wl_seat_add_listener(input->seat, &seat_listener, input);
+    } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
+	display->fshell = wl_registry_bind(registry,
+	                             id, &zwp_fullscreen_shell_v1_interface,
+	                             1);
+    } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
+	display->dmabuf = wl_registry_bind(registry,
+	                             id, &zwp_linux_dmabuf_v1_interface, 3);
+	zwp_linux_dmabuf_v1_add_listener(display->dmabuf, &dmabuf_listener,
+	                                 display);
+    }
+}
+
+static void registry_handle_global_remove(void *data,
+                                          struct wl_registry *registry,
+                                          uint32_t name)
+{
+}
+
+static const struct wl_registry_listener registry_listener = {
+    registry_handle_global,
+    registry_handle_global_remove
+};
+
+static const DisplayChangeListenerOps wayland_ops = {
+    .dpy_name                = "wayland",
+    .dpy_refresh             = wayland_refresh,
+    .dpy_gl_ctx_create       = wayland_create_context,
+    .dpy_gl_ctx_destroy      = wayland_destroy_context,
+    .dpy_gl_ctx_make_current = wayland_make_context_current,
+
+    .dpy_gl_scanout_disable  = wayland_scanout_disable,
+    .dpy_gl_scanout_texture  = wayland_scanout_texture,
+    .dpy_gl_scanout_dmabuf   = wayland_scanout_dmabuf,
+    .dpy_gl_release_dmabuf   = wayland_release_dmabuf,
+    .dpy_gl_update           = wayland_scanout_flush,
+};
+
+static wayland_display *wayland_create_display(void)
+{
+    wayland_display *display;
+
+    display = g_new0(wayland_display, 1);
+    display->display = wl_display_connect(NULL);
+    assert(display->display);
+
+    display->registry = wl_display_get_registry(display->display);
+    wl_registry_add_listener(display->registry,
+                             &registry_listener, display);
+    wl_display_roundtrip(display->display);
+    if (display->dmabuf == NULL) {
+        error_report("No zwp_linux_dmabuf global\n");
+	exit(1);
+    }
+    return display;
+}
+
+static wayland_window *wayland_create_window(wayland_display *display)
+{
+    wayland_window *window;
+
+    window = g_new0(wayland_window, 1);
+    window->display = display;
+    window->surface = wl_compositor_create_surface(display->compositor);
+    wl_surface_set_user_data(window->surface, window);
+
+    if (display->wm_base) {
+        window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
+	                                                  window->surface);
+        assert(window->xdg_surface);
+        xdg_surface_add_listener(window->xdg_surface,
+                                 &xdg_surface_listener, window);
+        window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
+        assert(window->xdg_toplevel);
+        xdg_toplevel_add_listener(window->xdg_toplevel,
+                                  &xdg_toplevel_listener, window);
+        xdg_toplevel_set_title(window->xdg_toplevel, "qemu-wayland");
+        window->wait_for_configure = true;
+        wl_surface_commit(window->surface);
+    } else if (display->fshell) {
+        zwp_fullscreen_shell_v1_present_surface(display->fshell,
+	                        window->surface,
+		                ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
+		                NULL);
+    } else {
+         assert(0);
+    }
+    wl_display_roundtrip(display->display);
+    return window;
+}
+
+static void wayland_prepare_for_events(struct wl_display *display)
+{
+    while (wl_display_prepare_read(display) != 0)
+        wl_display_dispatch_pending(display);
+    wl_display_flush(display);
+}
+
+static void wayland_dispatch_handler(void *opaque)
+{
+    wayland_display *display = opaque;
+
+    wl_display_read_events(display->display);
+    wl_display_dispatch_pending(display->display);
+    wayland_prepare_for_events(display->display);
+}
+
+static void wayland_init(DisplayState *ds, DisplayOptions *opts)
+{
+    QemuConsole *con;
+    wayland_display *display;
+    wayland_window *window;
+    int idx;
+
+    warn_report("Wayland UI backend should not be used with Guest compositors \
+                 that do single buffer/frontbuffer rendering\n");
+
+    display = wayland_create_display();
+    for (idx = 0;; idx++) {
+        con = qemu_console_lookup_by_index(idx);
+        if (!con || !qemu_console_is_graphic(con)) {
+            break;
+        }
+
+        window = wayland_create_window(display);
+        window->dcl.con = con;
+        window->dcl.ops = &wayland_ops;
+        window->kbd = qkbd_state_init(con);
+        register_displaychangelistener(&window->dcl);
+    }
+    wayland_prepare_for_events(display->display);
+    qemu_set_fd_handler(wl_display_get_fd(display->display),
+                        wayland_dispatch_handler, NULL, display);
+}
+
+static void early_wayland_init(DisplayOptions *opts)
+{
+    display_opengl = 1;
+}
+
+static QemuDisplay qemu_display_wayland = {
+    .type       = DISPLAY_TYPE_WAYLAND,
+    .early_init = early_wayland_init,
+    .init       = wayland_init,
+};
+
+static void register_wayland(void)
+{
+    qemu_display_register(&qemu_display_wayland);
+}
+
+type_init(register_wayland);
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI
  2021-09-13 22:20 ` [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Vivek Kasireddy
@ 2021-09-14 15:21   ` Eric Blake
  2021-09-14 16:15   ` Daniel P. Berrangé
  1 sibling, 0 replies; 8+ messages in thread
From: Eric Blake @ 2021-09-14 15:21 UTC (permalink / raw)
  To: Vivek Kasireddy; +Cc: qemu-devel, Gerd Hoffmann

On Mon, Sep 13, 2021 at 03:20:36PM -0700, Vivek Kasireddy wrote:
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
> ---
>  configure         |   8 +-
>  meson.build       |  33 +++
>  meson_options.txt |   2 +
>  qapi/ui.json      |   3 +
>  ui/meson.build    |  52 ++++
>  ui/wayland.c      | 628 ++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 725 insertions(+), 1 deletion(-)
>  create mode 100644 ui/wayland.c

UI review:

> +++ b/qapi/ui.json
> @@ -1112,6 +1112,8 @@
>  #                DRI device. Graphical display need to be paired with
>  #                VNC or Spice. (Since 3.1)
>  #
> +# @wayland: The Wayland user interface.

Missing a '(since 6.2)' tag.

> +#
>  # @curses: Display video output via curses.  For graphics device
>  #          models which support a text mode, QEMU can display this
>  #          output using a curses/ncurses interface. Nothing is
> @@ -1135,6 +1137,7 @@
>      { 'name': 'none' },
>      { 'name': 'gtk', 'if': 'CONFIG_GTK' },
>      { 'name': 'sdl', 'if': 'CONFIG_SDL' },
> +    { 'name': 'wayland', 'if': 'CONFIG_WAYLAND' },
>      { 'name': 'egl-headless',
>                'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
>      { 'name': 'curses', 'if': 'CONFIG_CURSES' },

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI
  2021-09-13 22:20 ` [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Vivek Kasireddy
  2021-09-14 15:21   ` Eric Blake
@ 2021-09-14 16:15   ` Daniel P. Berrangé
  2021-09-14 23:03     ` Kasireddy, Vivek
  1 sibling, 1 reply; 8+ messages in thread
From: Daniel P. Berrangé @ 2021-09-14 16:15 UTC (permalink / raw)
  To: Vivek Kasireddy; +Cc: qemu-devel, Gerd Hoffmann

On Mon, Sep 13, 2021 at 03:20:36PM -0700, Vivek Kasireddy wrote:
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
> ---
>  configure         |   8 +-
>  meson.build       |  33 +++
>  meson_options.txt |   2 +
>  qapi/ui.json      |   3 +
>  ui/meson.build    |  52 ++++
>  ui/wayland.c      | 628 ++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 725 insertions(+), 1 deletion(-)
>  create mode 100644 ui/wayland.c


> diff --git a/ui/meson.build b/ui/meson.build
> index a73beb0e54..86fc324c82 100644
> --- a/ui/meson.build
> +++ b/ui/meson.build
> @@ -64,6 +64,58 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found()
>    ui_modules += {'egl-headless' : egl_headless_ss}
>  endif
>  
> +wayland_scanner = find_program('wayland-scanner')
> +proto_sources = [
> +  ['xdg-shell', 'stable', ],
> +  ['fullscreen-shell', 'unstable', 'v1', ],
> +  ['linux-dmabuf', 'unstable', 'v1', ],
> +]
> +wayland_headers = []
> +wayland_proto_sources = []
> +
> +if wayland.found()
> +  foreach p: proto_sources
> +    proto_name = p.get(0)
> +    proto_stability = p.get(1)
> +
> +    if proto_stability == 'stable'
> +      output_base = proto_name
> +      input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
> +    else
> +      proto_version = p.get(2)
> +      output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability, proto_version)
> +      input = files(join_paths(wlproto_dir, '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
> +    endif
> +
> +    wayland_headers += custom_target('@0@ client header'.format(output_base),
> +      input: input,
> +      output: '@0@-client-protocol.h'.format(output_base),
> +      command: [
> +        wayland_scanner,
> +        'client-header',
> +        '@INPUT@', '@OUTPUT@',
> +      ], build_by_default: true
> +    )
> +
> +    wayland_proto_sources += custom_target('@0@ source'.format(output_base),
> +      input: input,
> +      output: '@0@-protocol.c'.format(output_base),
> +      command: [
> +        wayland_scanner,
> +        'private-code',
> +        '@INPUT@', '@OUTPUT@',
> +      ], build_by_default: true
> +    )
> +  endforeach
> +endif
> +
> +if wayland.found()
> +  wayland_ss = ss.source_set()
> +  wayland_ss.add(when: wayland, if_true: files('wayland.c', 'xdg-shell-protocol.c', 'fullscreen-shell-unstable-v1-protocol.c','linux-dmabuf-unstable-v1-protocol.c'))
> +  #wayland_ss.add(when: wayland, if_true: files('wayland.c'), [wayland_proto_sources])
> +  ui_modules += {'wayland' : wayland_ss}
> +endif

Configure fails on this

  Program wayland-scanner found: YES (/usr/bin/wayland-scanner)

  ../ui/meson.build:114:13: ERROR: File xdg-shell-protocol.c does not exist.


the code a few lines above generates xdg-shell-protocol.c, but that
isn't run until you type "make", so when meson is resolving the
source files they don't exist.

The alternative line you have commented out looks more like what we
would need, but it doesn't work either as its syntax is invalid.

How did you actually compile this series ?


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2)
  2021-09-13 22:20 [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Vivek Kasireddy
  2021-09-13 22:20 ` [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE Vivek Kasireddy
  2021-09-13 22:20 ` [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Vivek Kasireddy
@ 2021-09-14 16:29 ` Daniel P. Berrangé
  2021-09-14 23:46   ` Kasireddy, Vivek
  2 siblings, 1 reply; 8+ messages in thread
From: Daniel P. Berrangé @ 2021-09-14 16:29 UTC (permalink / raw)
  To: Vivek Kasireddy
  Cc: Dongwon Kim, Satyeshwar Singh, qemu-devel, Tina Zhang,
	Gerd Hoffmann, Marc-André Lureau

On Mon, Sep 13, 2021 at 03:20:34PM -0700, Vivek Kasireddy wrote:
> Why does Qemu need a new Wayland UI backend?
> The main reason why there needs to be a plain and simple Wayland backend
> for Qemu UI is to eliminate the Blit (aka GPU copy) that happens if using
> a toolkit like GTK or SDL (because they use EGL). The Blit can be eliminated
> by sharing the dmabuf fd -- associated with the Guest scanout buffer --
> directly with the Host compositor via the linux-dmabuf (unstable) protocol.
> Once properly integrated, it would be potentially possible to have the
> scanout buffer created by the Guest compositor be placed directly on a
> hardware plane on the Host thereby improving performance. Only Guest 
> compositors that use multiple back buffers (at-least 1 front and 1 back)
> and virtio-gpu would benefit from this work.

IME, QEMU already suffers from having too many barely maintained UI
implementations and iti s confusing to users. Using a toolkit like GTK
is generally a good thing, even if they don't enable the maximum
theoretical performance, because they reduce the long term maint burden.

I'm far from convinced that we should take on the maint of yet another
UI in QEMU, even if it does have some performance benefit, especially
if implemented using a very low level API like Wayland, that won't let
us easily add rich UI features.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI
  2021-09-14 16:15   ` Daniel P. Berrangé
@ 2021-09-14 23:03     ` Kasireddy, Vivek
  0 siblings, 0 replies; 8+ messages in thread
From: Kasireddy, Vivek @ 2021-09-14 23:03 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, Gerd Hoffmann

Hi Daniel,
 
> On Mon, Sep 13, 2021 at 03:20:36PM -0700, Vivek Kasireddy wrote:
> > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
> > ---
> >  configure         |   8 +-
> >  meson.build       |  33 +++
> >  meson_options.txt |   2 +
> >  qapi/ui.json      |   3 +
> >  ui/meson.build    |  52 ++++
> >  ui/wayland.c      | 628 ++++++++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 725 insertions(+), 1 deletion(-)  create mode 100644
> > ui/wayland.c
> 
> 
> > diff --git a/ui/meson.build b/ui/meson.build index
> > a73beb0e54..86fc324c82 100644
> > --- a/ui/meson.build
> > +++ b/ui/meson.build
> > @@ -64,6 +64,58 @@ if config_host.has_key('CONFIG_OPENGL') and gbm.found()
> >    ui_modules += {'egl-headless' : egl_headless_ss}  endif
> >
> > +wayland_scanner = find_program('wayland-scanner') proto_sources = [
> > +  ['xdg-shell', 'stable', ],
> > +  ['fullscreen-shell', 'unstable', 'v1', ],
> > +  ['linux-dmabuf', 'unstable', 'v1', ], ] wayland_headers = []
> > +wayland_proto_sources = []
> > +
> > +if wayland.found()
> > +  foreach p: proto_sources
> > +    proto_name = p.get(0)
> > +    proto_stability = p.get(1)
> > +
> > +    if proto_stability == 'stable'
> > +      output_base = proto_name
> > +      input = files(join_paths(wlproto_dir,
> '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
> > +    else
> > +      proto_version = p.get(2)
> > +      output_base = '@0@-@1@-@2@'.format(proto_name, proto_stability,
> proto_version)
> > +      input = files(join_paths(wlproto_dir,
> '@0@/@1@/@2@.xml'.format(proto_stability, proto_name, output_base)))
> > +    endif
> > +
> > +    wayland_headers += custom_target('@0@ client header'.format(output_base),
> > +      input: input,
> > +      output: '@0@-client-protocol.h'.format(output_base),
> > +      command: [
> > +        wayland_scanner,
> > +        'client-header',
> > +        '@INPUT@', '@OUTPUT@',
> > +      ], build_by_default: true
> > +    )
> > +
> > +    wayland_proto_sources += custom_target('@0@ source'.format(output_base),
> > +      input: input,
> > +      output: '@0@-protocol.c'.format(output_base),
> > +      command: [
> > +        wayland_scanner,
> > +        'private-code',
> > +        '@INPUT@', '@OUTPUT@',
> > +      ], build_by_default: true
> > +    )
> > +  endforeach
> > +endif
> > +
> > +if wayland.found()
> > +  wayland_ss = ss.source_set()
> > +  wayland_ss.add(when: wayland, if_true: files('wayland.c',
> > +'xdg-shell-protocol.c',
> > +'fullscreen-shell-unstable-v1-protocol.c','linux-dmabuf-unstable-v1-p
> > +rotocol.c'))
> > +  #wayland_ss.add(when: wayland, if_true: files('wayland.c'),
> > +[wayland_proto_sources])
> > +  ui_modules += {'wayland' : wayland_ss} endif
> 
> Configure fails on this
> 
>   Program wayland-scanner found: YES (/usr/bin/wayland-scanner)
> 
>   ../ui/meson.build:114:13: ERROR: File xdg-shell-protocol.c does not exist.
> 
> 
> the code a few lines above generates xdg-shell-protocol.c, but that isn't run until you type
> "make", so when meson is resolving the source files they don't exist.
> 
> The alternative line you have commented out looks more like what we would need, but it
> doesn't work either as its syntax is invalid.
[Kasireddy, Vivek] Right, the commented line is the one we'd need but despite exhaustively
trying various different combinations, I couldn't get Meson to include the auto-generated
protocol sources. If it is not too much trouble, could you please point me to an example
where this is done elsewhere in Qemu source that I can look at?

> 
> How did you actually compile this series ?
[Kasireddy, Vivek] Oh, as a workaround, I just manually added the protocol sources. I am
sorry I did not realize that this code would be compiled/tested; I mainly posted these
RFC/WIP patches to provide additional context to the discussion associated with the DRM/
Virtio-gpu kernel patches.

Thanks,
Vivek

> 
> 
> Regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2)
  2021-09-14 16:29 ` [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Daniel P. Berrangé
@ 2021-09-14 23:46   ` Kasireddy, Vivek
  0 siblings, 0 replies; 8+ messages in thread
From: Kasireddy, Vivek @ 2021-09-14 23:46 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Kim, Dongwon, Singh, Satyeshwar, qemu-devel, Zhang, Tina,
	Gerd Hoffmann, Marc-André Lureau

Hi Daniel,

> On Mon, Sep 13, 2021 at 03:20:34PM -0700, Vivek Kasireddy wrote:
> > Why does Qemu need a new Wayland UI backend?
> > The main reason why there needs to be a plain and simple Wayland backend
> > for Qemu UI is to eliminate the Blit (aka GPU copy) that happens if using
> > a toolkit like GTK or SDL (because they use EGL). The Blit can be eliminated
> > by sharing the dmabuf fd -- associated with the Guest scanout buffer --
> > directly with the Host compositor via the linux-dmabuf (unstable) protocol.
> > Once properly integrated, it would be potentially possible to have the
> > scanout buffer created by the Guest compositor be placed directly on a
> > hardware plane on the Host thereby improving performance. Only Guest
> > compositors that use multiple back buffers (at-least 1 front and 1 back)
> > and virtio-gpu would benefit from this work.
> 
> IME, QEMU already suffers from having too many barely maintained UI
> implementations and iti s confusing to users. Using a toolkit like GTK
> is generally a good thing, even if they don't enable the maximum
> theoretical performance, because they reduce the long term maint burden.
[Kasireddy, Vivek] The Wayland UI is ~600 lines of code and more than half of
that is just boilerplate; it is also fairly standalone. We don't have any major
complaints against GTK UI (which is ~3000 lines of code, just the Qemu piece
excluding libgtk) except that there is no way to dissociate EGL from it. And, to
keep the UI codebase up-to-date and leverage latest features from GTK (for
example GTK4 and beyond) it would require non-trivial amount of work.
Therefore, I think it'd not be too onerous to maintain something lightweight like
the Wayland UI module.

> 
> I'm far from convinced that we should take on the maint of yet another
> UI in QEMU, even if it does have some performance benefit, especially
[Kasireddy, Vivek] There are novel use-cases coming up particularly with 
the arrival of technologies like SRIOV where the Guest is expected to do all the
rendering and share the fully composited buffer with the Host whose job is to
just present it on the screen. And, in this scenario, if we were to use GTK UI,
the (fullscreen sized) Blits incurred would quickly add up if there are 4-6 Guests
running and presenting content at the same time locally on multiple monitors.
Wayland UI helps in this situation as it does not do any additional rendering on
the Host (no Blit) as it just merely forwards the Guest composited buffer to the
Host compositor.

> if implemented using a very low level API like Wayland, that won't let
> us easily add rich UI features.
[Kasireddy, Vivek] Yes, it is a drawback of Wayland UI that it'd not be possible to
do window decorations/rich UI features but there are users/customers that do not care
for them. I think it should be possible to have both Wayland and GTK UI co-exist
where users can choose GTK UI for fancy features and Wayland UI for performance.

Thanks,
Vivek

> 
> Regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-09-14 23:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-13 22:20 [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Vivek Kasireddy
2021-09-13 22:20 ` [RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE Vivek Kasireddy
2021-09-13 22:20 ` [RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI Vivek Kasireddy
2021-09-14 15:21   ` Eric Blake
2021-09-14 16:15   ` Daniel P. Berrangé
2021-09-14 23:03     ` Kasireddy, Vivek
2021-09-14 16:29 ` [RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2) Daniel P. Berrangé
2021-09-14 23:46   ` Kasireddy, Vivek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.