[PATCH v1 3/7] ui/spice: Submit the gl_draw requests at 60 FPS for remote clients

2024-01-19 Thread Vivek Kasireddy
In the specific case where the display layer (virtio-gpu) is using
dmabuf, and if remote clients are enabled (-spice gl=on,port=),
it makes sense to limit the maximum (streaming) rate to 60 FPS
using the GUI timer. This matches the behavior of GTK UI where the
display updates are submitted at 60 FPS (assuming the underlying
mode is WxY@60).

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/spice-display.c | 38 --
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/ui/spice-display.c b/ui/spice-display.c
index 384b8508d4..90c04623ec 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -841,12 +841,31 @@ static void qemu_spice_gl_block_timer(void *opaque)
 warn_report("spice: no gl-draw-done within one second");
 }
 
+static void spice_gl_draw(SimpleSpiceDisplay *ssd,
+   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+uint64_t cookie;
+
+cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
+spice_qxl_gl_draw_async(>qxl, x, y, w, h, cookie);
+}
+
 static void spice_gl_refresh(DisplayChangeListener *dcl)
 {
 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
-uint64_t cookie;
+QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
 
-if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) {
+if (!ssd->ds) {
+return;
+}
+
+if (qemu_console_is_gl_blocked(ssd->dcl.con)) {
+if (remote_client && ssd->gl_updates && dmabuf) {
+spice_gl_draw(ssd, 0, 0, dmabuf->width, dmabuf->height);
+ssd->gl_updates = 0;
+/* To stream at 60 FPS, the (GUI) timer delay needs to be ~17 ms */
+dcl->update_interval = 1000 / (2 * GUI_REFRESH_INTERVAL_DEFAULT) + 
1;
+}
 return;
 }
 
@@ -854,11 +873,8 @@ static void spice_gl_refresh(DisplayChangeListener *dcl)
 if (ssd->gl_updates && ssd->have_surface) {
 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(>qxl, 0, 0,
-surface_width(ssd->ds),
-surface_height(ssd->ds),
-cookie);
+spice_gl_draw(ssd, 0, 0,
+  surface_width(ssd->ds), surface_height(ssd->ds));
 ssd->gl_updates = 0;
 }
 }
@@ -1025,7 +1041,6 @@ static void qemu_spice_gl_update(DisplayChangeListener 
*dcl,
 EGLint stride = 0, fourcc = 0;
 bool render_cursor = false;
 bool y_0_top = false; /* FIXME */
-uint64_t cookie;
 int fd;
 
 if (!ssd->have_scanout) {
@@ -1098,8 +1113,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(>qxl, x, y, w, h, cookie);
+if (remote_client) {
+ssd->gl_updates++;
+} else {
+spice_gl_draw(ssd, x, y, w, h);
+}
 }
 
 static const DisplayChangeListenerOps display_listener_gl_ops = {
-- 
2.39.2




[PATCH v1 5/7] ui/spice: Override the surface's glformat when gl=on is enabled

2024-01-19 Thread Vivek Kasireddy
When testing with gl=on on an Intel Host, it was noticed that the
R and B channels were interchanged while the Guest FB image was
displayed. This was only seen if the display layer (virtio-gpu)
did not directly share the dmabuf fd with Spice (i.e, blob=false).

One of the main differences in the case where blob=true vs blob=false
is that we create the dmabuf fd from a texture in the latter case
whereas we directly pass the fd from the display layer to Spice in
the former case. Although, the surface's format (PIXMAN_BE_b8g8r8x8)
is the same in both cases, the creation of the texture (which involves
copying data from Pixman image into a GPU buffer) appears to somehow
result in having the R and B channels interchanged. One way to ensure
correct behavior is we have glformat=GL_RGBA while creating the texture.

It looks like having glformat=GL_RGBA and gltype = GL_UNSIGNED_BYTE
should work regardless of the Host's endianness but let us limit
this change only to this specific use-case for now.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/spice-display.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/ui/spice-display.c b/ui/spice-display.c
index 90c04623ec..08b4aec921 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -900,6 +900,9 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
 }
 ssd->ds = new_surface;
 if (ssd->ds) {
+if (remote_client && surface_format(ssd->ds) != PIXMAN_r5g6b5) {
+ssd->ds->target_glformat = GL_RGBA;
+}
 surface_gl_create_texture(ssd->gls, ssd->ds);
 fd = egl_get_fd_for_texture(ssd->ds->texture,
 , ,
-- 
2.39.2




[PATCH v1 1/7] ui/spice: Add an option for users to provide a preferred codec

2024-01-19 Thread Vivek Kasireddy
Giving users an option to choose a particular codec will enable
them to make an appropriate decision based on their hardware and
use-case.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 

---
v2:
- Don't override the default Spice codec if preferred-codec is not
  provided (Frediano)
---
 qemu-options.hx |  5 +
 ui/spice-core.c | 12 
 2 files changed, 17 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index b66570ae00..caaafe01d5 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2260,6 +2260,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
 "   [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n"
 "   [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n"
 "   [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
+"   [,preferred-codec=:\n"
 "   [,gl=[on|off]][,rendernode=]\n"
 "enable spice\n"
 "at least one of {port, tls-port} is mandatory\n",
@@ -2348,6 +2349,10 @@ SRST
 ``seamless-migration=[on|off]``
 Enable/disable spice seamless migration. Default is off.
 
+``preferred-codec=:``
+Provide the preferred codec the Spice server should use.
+Default would be spice:mjpeg.
+
 ``gl=[on|off]``
 Enable/disable OpenGL context. Default is off.
 
diff --git a/ui/spice-core.c b/ui/spice-core.c
index db21db2c94..13bfbe4e89 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -488,6 +488,9 @@ static QemuOptsList qemu_spice_opts = {
 },{
 .name = "streaming-video",
 .type = QEMU_OPT_STRING,
+},{
+.name = "preferred-codec",
+.type = QEMU_OPT_STRING,
 },{
 .name = "agent-mouse",
 .type = QEMU_OPT_BOOL,
@@ -663,6 +666,7 @@ static void qemu_spice_init(void)
 char *x509_key_file = NULL,
 *x509_cert_file = NULL,
 *x509_cacert_file = NULL;
+const char *preferred_codec = NULL;
 int port, tls_port, addr_flags;
 spice_image_compression_t compression;
 spice_wan_compression_t wan_compr;
@@ -802,6 +806,14 @@ static void qemu_spice_init(void)
 spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
 }
 
+preferred_codec = qemu_opt_get(opts, "preferred-codec");
+if (preferred_codec) {
+if (spice_server_set_video_codecs(spice_server, preferred_codec)) {
+error_report("Preferred codec name is not valid");
+exit(1);
+}
+}
+
 spice_server_set_agent_mouse
 (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
 spice_server_set_playback_compression
-- 
2.39.2




[PATCH v1 4/7] ui/console-gl: Add an option to override a surface's glformat

2024-01-19 Thread Vivek Kasireddy
In some cases where a UI component (e.g, Spice) needs to choose
a particular glformat for a surface while creating a texture,
this new GLenum provides an option to do so. One situation
where this needs to be done is when the Host endianness is
causing issues such as interchanged R and B channels.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/surface.h | 1 +
 ui/console-gl.c  | 6 ++
 2 files changed, 7 insertions(+)

diff --git a/include/ui/surface.h b/include/ui/surface.h
index 4244e0ca4a..a39fee55a2 100644
--- a/include/ui/surface.h
+++ b/include/ui/surface.h
@@ -20,6 +20,7 @@ typedef struct DisplaySurface {
 uint8_t flags;
 #ifdef CONFIG_OPENGL
 GLenum glformat;
+GLenum target_glformat;
 GLenum gltype;
 GLuint texture;
 #endif
diff --git a/ui/console-gl.c b/ui/console-gl.c
index 103b954017..dee317f42c 100644
--- a/ui/console-gl.c
+++ b/ui/console-gl.c
@@ -72,6 +72,12 @@ void surface_gl_create_texture(QemuGLShader *gls,
 g_assert_not_reached();
 }
 
+/* The caller wants to override the glformat in some specific cases */
+if (surface->target_glformat &&
+surface->target_glformat != surface->glformat) {
+surface->glformat = surface->target_glformat;
+}
+
 glGenTextures(1, >texture);
 glEnable(GL_TEXTURE_2D);
 glBindTexture(GL_TEXTURE_2D, surface->texture);
-- 
2.39.2




[PATCH v1 0/7] ui/spice: Enable gl=on option for non-local or remote clients

2024-01-19 Thread Vivek Kasireddy
To address the limitation that this option is incompatible with
remote clients, this patch series adds an option to select a
preferred codec and also enable gl=on option for clients that
are connected via the network. In other words, with this option
enabled (and the below linked Spice series merged), it would be
possible to have Qemu share a dmabuf fd with Spice, which would
then forward it to a hardware or software based encoder and
eventually send the data associated with the fd to a client that
could be located on a different machine.

Tested with: -device virtio-vga,max_outputs=1,xres=1920,yres=1080
 -spice 
port=3001,gl=on,disable-ticketing=on,preferred-codec=gstreamer:h264

and remote-viewer --spice-debug spice://x.x.x.x:3001 on the client side.

---
Associated Spice server patches can be found here:
https://lists.freedesktop.org/archives/spice-devel/2023-December/053288.html

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 

Vivek Kasireddy (7):
  ui/spice: Add an option for users to provide a preferred codec
  ui/spice: Enable gl=on option for non-local or remote clients
  ui/spice: Submit the gl_draw requests at 60 FPS for remote clients
  ui/console-gl: Add an option to override a surface's glformat
  ui/spice: Override the surface's glformat when gl=on is enabled
  ui/console-gl: Add a helper to create a texture with linear memory
layout
  ui/spice: Create another texture with linear layout when gl=on is
enabled

 include/ui/console.h   |  2 +
 include/ui/spice-display.h |  1 +
 include/ui/surface.h   |  1 +
 qemu-options.hx|  5 +++
 ui/console-gl.c| 32 
 ui/spice-core.c| 16 
 ui/spice-display.c | 75 +-
 7 files changed, 122 insertions(+), 10 deletions(-)

-- 
2.39.2




[PATCH v1 7/7] ui/spice: Create another texture with linear layout when gl=on is enabled

2024-01-19 Thread Vivek Kasireddy
Since most encoders (invoked by Spice) may not work with tiled memory
associated with a texture, we need to create another texture that has
linear memory layout and use that instead.

Note that, there does not seem to be a direct way to indicate to the
GL implementation that a texture's backing memory needs to be linear.
Instead, we have to do it in a roundabout way where we first need to
create a tiled texture and obtain a dmabuf fd associated with it via
EGL. Next, we have to create a memory object by importing the dmabuf
fd created earlier and finally create a linear texture by using the
memory object as the texture storage mechanism.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/spice-display.c | 33 +
 1 file changed, 33 insertions(+)

diff --git a/ui/spice-display.c b/ui/spice-display.c
index 08b4aec921..94cb378dbe 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -893,6 +893,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
 {
 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
 EGLint stride, fourcc;
+GLuint texture = 0;
 int fd;
 
 if (ssd->ds) {
@@ -912,6 +913,38 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
 return;
 }
 
+if (remote_client && surface_format(ssd->ds) != PIXMAN_r5g6b5) {
+/*
+ * We really want to ensure that the memory layout of the texture
+ * is linear; otherwise, the encoder's output may show corruption.
+ */
+surface_gl_create_texture_from_fd(ssd->ds, fd, );
+
+/*
+ * A successful return after glImportMemoryFdEXT() means that
+ * the ownership of fd has been passed to GL. In other words,
+ * the fd we got above should not be used anymore.
+ */
+if (texture > 0) {
+fd = egl_get_fd_for_texture(texture,
+, ,
+NULL);
+if (fd < 0) {
+glDeleteTextures(1, );
+fd = egl_get_fd_for_texture(ssd->ds->texture,
+, ,
+NULL);
+if (fd < 0) {
+surface_gl_destroy_texture(ssd->gls, ssd->ds);
+return;
+}
+} else {
+surface_gl_destroy_texture(ssd->gls, ssd->ds);
+ssd->ds->texture = texture;
+}
+}
+}
+
 trace_qemu_spice_gl_surface(ssd->qxl.id,
 surface_width(ssd->ds),
 surface_height(ssd->ds),
-- 
2.39.2




[PATCH v1 2/7] ui/spice: Enable gl=on option for non-local or remote clients

2024-01-19 Thread Vivek Kasireddy
Newer versions of Spice server should be able to accept dmabuf
fds from Qemu for clients that are connected via the network.
In other words, when this option is enabled, Qemu would share
a dmabuf fd with Spice which would encode and send the data
associated with the fd to a client that could be located on
a different machine.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/spice-display.h | 1 +
 ui/spice-core.c| 4 
 ui/spice-display.c | 1 +
 3 files changed, 6 insertions(+)

diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index e1a9b36185..f4922dd74b 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -151,6 +151,7 @@ struct SimpleSpiceCursor {
 };
 
 extern bool spice_opengl;
+extern bool remote_client;
 
 int qemu_spice_rect_is_empty(const QXLRect* r);
 void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 13bfbe4e89..3b9a54685f 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -849,9 +849,13 @@ static void qemu_spice_init(void)
 #ifdef HAVE_SPICE_GL
 if (qemu_opt_get_bool(opts, "gl", 0)) {
 if ((port != 0) || (tls_port != 0)) {
+#if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */
+remote_client = 1;
+#else
 error_report("SPICE GL support is local-only for now and "
  "incompatible with -spice port/tls-port");
 exit(1);
+#endif
 }
 egl_init(qemu_opt_get(opts, "rendernode"), DISPLAYGL_MODE_ON, 
_fatal);
 spice_opengl = 1;
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 6eb98a5a5c..384b8508d4 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -29,6 +29,7 @@
 #include "ui/spice-display.h"
 
 bool spice_opengl;
+bool remote_client;
 
 int qemu_spice_rect_is_empty(const QXLRect* r)
 {
-- 
2.39.2




[PATCH v1 6/7] ui/console-gl: Add a helper to create a texture with linear memory layout

2024-01-19 Thread Vivek Kasireddy
There are cases where we do not want the memory layout of a texture to
be tiled as the component processing the texture memory would not know
how to de-tile either via software or hardware. Therefore, ensuring
that the memory backing the texture has a linear layout is absolutely
necessary in these situations.

Note that, requesting GL implementation to create a texture with
linear memory layout can only be done using memory objects. And, a
memory object can be created by importing a dmabuf fd.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Frediano Ziglio 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 ui/console-gl.c  | 26 ++
 2 files changed, 28 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index a4a49ffc64..e53e3ce03e 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -444,6 +444,8 @@ bool console_gl_check_format(DisplayChangeListener *dcl,
  pixman_format_code_t format);
 void surface_gl_create_texture(QemuGLShader *gls,
DisplaySurface *surface);
+void surface_gl_create_texture_from_fd(DisplaySurface *surface,
+   int fd, GLuint *texture);
 void surface_gl_update_texture(QemuGLShader *gls,
DisplaySurface *surface,
int x, int y, int w, int h);
diff --git a/ui/console-gl.c b/ui/console-gl.c
index dee317f42c..2c1b2ae377 100644
--- a/ui/console-gl.c
+++ b/ui/console-gl.c
@@ -102,6 +102,32 @@ void surface_gl_create_texture(QemuGLShader *gls,
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 }
 
+void surface_gl_create_texture_from_fd(DisplaySurface *surface,
+   int fd, GLuint *texture)
+{
+unsigned long size = surface_stride(surface) * surface_height(surface);
+GLuint mem_obj;
+
+if (!epoxy_has_gl_extension("GL_EXT_memory_object") ||
+!epoxy_has_gl_extension("GL_EXT_memory_object_fd")) {
+return;
+}
+
+#ifdef GL_EXT_memory_object_fd
+glCreateMemoryObjectsEXT(1, _obj);
+glImportMemoryFdEXT(mem_obj, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd);
+if (!glIsMemoryObjectEXT(mem_obj)) {
+return;
+}
+
+glGenTextures(1, texture);
+glBindTexture(GL_TEXTURE_2D, *texture);
+glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, 
GL_LINEAR_TILING_EXT);
+glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, surface_width(surface),
+ surface_height(surface), mem_obj, 0);
+#endif
+}
+
 void surface_gl_update_texture(QemuGLShader *gls,
DisplaySurface *surface,
int x, int y, int w, int h)
-- 
2.39.2




[PATCH v2] target/i386/host-cpu: Use iommu phys_bits with VFIO assigned devices on Intel h/w

2024-01-18 Thread Vivek Kasireddy
Recent updates in OVMF and Seabios have resulted in MMIO regions
being placed at the upper end of the physical address space. As a
result, when a Host device is assigned to the Guest via VFIO, the
following mapping failures occur when VFIO tries to map the MMIO
regions of the device:
VFIO_MAP_DMA failed: Invalid argument
vfio_dma_map(0x557b2f2736d0, 0x3800, 0x100, 0x7f98ac40) = -22 
(Invalid argument)

The above failures are mainly seen on some Intel platforms where
the physical address width is larger than the Host's IOMMU
address width. In these cases, VFIO fails to map the MMIO regions
because the IOVAs would be larger than the IOMMU aperture regions.

Therefore, one way to solve this problem would be to ensure that
cpu->phys_bits = 
This can be done by parsing the IOMMU caps value from sysfs and
extracting the address width and using it to override the
phys_bits value as shown in this patch.

Previous attempt at solving this issue in OVMF:
https://edk2.groups.io/g/devel/topic/102359124

Cc: Gerd Hoffmann 
Cc: Philippe Mathieu-Daudé 
Cc: Alex Williamson 
Cc: Cédric Le Goater 
Cc: Laszlo Ersek 
Cc: Dongwon Kim 
Acked-by: Gerd Hoffmann 
Tested-by: Yanghang Liu 
Signed-off-by: Vivek Kasireddy 

---
v2:
- Replace the term passthrough with assigned (Laszlo)
- Update the commit message to note that both OVMF and Seabios
  guests are affected (Cédric)
- Update the subject to indicate what is done in the patch
---
 target/i386/host-cpu.c | 61 +-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c
index 92ecb7254b..5c9fcd7dc2 100644
--- a/target/i386/host-cpu.c
+++ b/target/i386/host-cpu.c
@@ -12,6 +12,8 @@
 #include "host-cpu.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
 #include "sysemu/sysemu.h"
 
 /* Note: Only safe for use on x86(-64) hosts */
@@ -51,11 +53,58 @@ static void host_cpu_enable_cpu_pm(X86CPU *cpu)
 env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR;
 }
 
+static int intel_iommu_check(void *opaque, QemuOpts *opts, Error **errp)
+{
+g_autofree char *dev_path = NULL, *iommu_path = NULL, *caps = NULL;
+const char *driver = qemu_opt_get(opts, "driver");
+const char *device = qemu_opt_get(opts, "host");
+uint32_t *iommu_phys_bits = opaque;
+struct stat st;
+uint64_t iommu_caps;
+
+/*
+ * Check if the user requested VFIO device assignment. We don't have
+ * to limit phys_bits if there are no valid assigned devices.
+ */
+if (g_strcmp0(driver, "vfio-pci") || !device) {
+return 0;
+}
+
+dev_path = g_strdup_printf("/sys/bus/pci/devices/%s", device);
+if (stat(dev_path, ) < 0) {
+return 0;
+}
+
+iommu_path = g_strdup_printf("%s/iommu/intel-iommu/cap", dev_path);
+if (stat(iommu_path, ) < 0) {
+return 0;
+}
+
+if (g_file_get_contents(iommu_path, , NULL, NULL)) {
+if (sscanf(caps, "%lx", _caps) != 1) {
+return 0;
+}
+*iommu_phys_bits = ((iommu_caps >> 16) & 0x3f) + 1;
+}
+
+return 0;
+}
+
+static uint32_t host_iommu_phys_bits(void)
+{
+uint32_t iommu_phys_bits = 0;
+
+qemu_opts_foreach(qemu_find_opts("device"),
+  intel_iommu_check, _phys_bits, NULL);
+return iommu_phys_bits;
+}
+
 static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
 {
 uint32_t host_phys_bits = host_cpu_phys_bits();
+uint32_t iommu_phys_bits = host_iommu_phys_bits();
 uint32_t phys_bits = cpu->phys_bits;
-static bool warned;
+static bool warned, warned2;
 
 /*
  * Print a warning if the user set it to a value that's not the
@@ -78,6 +127,16 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
 }
 }
 
+if (iommu_phys_bits && phys_bits > iommu_phys_bits) {
+phys_bits = iommu_phys_bits;
+if (!warned2) {
+warn_report("Using physical bits (%u)"
+" to prevent VFIO mapping failures",
+iommu_phys_bits);
+warned2 = true;
+}
+}
+
 return phys_bits;
 }
 
-- 
2.39.2




[PATCH v1] target/i386/host-cpu: Use IOMMU addr width for passthrough devices on Intel platforms

2023-11-12 Thread Vivek Kasireddy
A recent OVMF update has resulted in MMIO regions being placed at
the upper end of the physical address space. As a result, when a
Host device is passthrough'd to the Guest via VFIO, the following
mapping failures occur when VFIO tries to map the MMIO regions of
the device:
VFIO_MAP_DMA failed: Invalid argument
vfio_dma_map(0x557b2f2736d0, 0x3800, 0x100, 0x7f98ac40) = -22 
(Invalid argument)

The above failures are mainly seen on some Intel platforms where
the physical address width is larger than the Host's IOMMU
address width. In these cases, VFIO fails to map the MMIO regions
because the IOVAs would be larger than the IOMMU aperture regions.

Therefore, one way to solve this problem would be to ensure that
cpu->phys_bits = 
This can be done by parsing the IOMMU caps value from sysfs and
extracting the address width and using it to override the
phys_bits value as shown in this patch.

Previous attempt at solving this issue in OVMF:
https://edk2.groups.io/g/devel/topic/102359124

Cc: Gerd Hoffmann 
Cc: Philippe Mathieu-Daudé 
Cc: Alex Williamson 
Cc: Laszlo Ersek 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 target/i386/host-cpu.c | 61 +-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c
index 92ecb7254b..8326ec95bc 100644
--- a/target/i386/host-cpu.c
+++ b/target/i386/host-cpu.c
@@ -12,6 +12,8 @@
 #include "host-cpu.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
 #include "sysemu/sysemu.h"
 
 /* Note: Only safe for use on x86(-64) hosts */
@@ -51,11 +53,58 @@ static void host_cpu_enable_cpu_pm(X86CPU *cpu)
 env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR;
 }
 
+static int intel_iommu_check(void *opaque, QemuOpts *opts, Error **errp)
+{
+g_autofree char *dev_path = NULL, *iommu_path = NULL, *caps = NULL;
+const char *driver = qemu_opt_get(opts, "driver");
+const char *device = qemu_opt_get(opts, "host");
+uint32_t *iommu_phys_bits = opaque;
+struct stat st;
+uint64_t iommu_caps;
+
+/*
+ * Check if the user is passthroughing any devices via VFIO. We don't
+ * have to limit phys_bits if there are no valid passthrough devices.
+ */
+if (g_strcmp0(driver, "vfio-pci") || !device) {
+return 0;
+}
+
+dev_path = g_strdup_printf("/sys/bus/pci/devices/%s", device);
+if (stat(dev_path, ) < 0) {
+return 0;
+}
+
+iommu_path = g_strdup_printf("%s/iommu/intel-iommu/cap", dev_path);
+if (stat(iommu_path, ) < 0) {
+return 0;
+}
+
+if (g_file_get_contents(iommu_path, , NULL, NULL)) {
+if (sscanf(caps, "%lx", _caps) != 1) {
+return 0;
+}
+*iommu_phys_bits = ((iommu_caps >> 16) & 0x3f) + 1;
+}
+
+return 0;
+}
+
+static uint32_t host_iommu_phys_bits(void)
+{
+uint32_t iommu_phys_bits = 0;
+
+qemu_opts_foreach(qemu_find_opts("device"),
+  intel_iommu_check, _phys_bits, NULL);
+return iommu_phys_bits;
+}
+
 static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
 {
 uint32_t host_phys_bits = host_cpu_phys_bits();
+uint32_t iommu_phys_bits = host_iommu_phys_bits();
 uint32_t phys_bits = cpu->phys_bits;
-static bool warned;
+static bool warned, warned2;
 
 /*
  * Print a warning if the user set it to a value that's not the
@@ -78,6 +127,16 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
 }
 }
 
+if (iommu_phys_bits && phys_bits > iommu_phys_bits) {
+phys_bits = iommu_phys_bits;
+if (!warned2) {
+warn_report("Using physical bits (%u)"
+" to prevent VFIO mapping failures",
+iommu_phys_bits);
+warned2 = true;
+}
+}
+
 return phys_bits;
 }
 
-- 
2.39.2




[PATCH v1] virtio-gpu: Make non-gl display updates work again when blob=true

2023-06-23 Thread Vivek Kasireddy
In the case where the console does not have gl capability, and
if blob is set to true, make sure that the display updates still
work. Commit e86a93f55463 accidentally broke this by misplacing
the return statement (in resource_flush) causing the updates to
be silently ignored.

Fixes: e86a93f55463 ("virtio-gpu: splitting one extended mode guest fb into 
n-scanouts")
Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 27 ++-
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 66cddd94d9..97cd987cf3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -498,6 +498,8 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 struct virtio_gpu_resource_flush rf;
 struct virtio_gpu_scanout *scanout;
 pixman_region16_t flush_region;
+bool within_bounds = false;
+bool update_submitted = false;
 int i;
 
 VIRTIO_GPU_FILL_CMD(rf);
@@ -518,13 +520,28 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 rf.r.x < scanout->x + scanout->width &&
 rf.r.x + rf.r.width >= scanout->x &&
 rf.r.y < scanout->y + scanout->height &&
-rf.r.y + rf.r.height >= scanout->y &&
-console_has_gl(scanout->con)) {
-dpy_gl_update(scanout->con, 0, 0, scanout->width,
-  scanout->height);
+rf.r.y + rf.r.height >= scanout->y) {
+within_bounds = true;
+
+if (console_has_gl(scanout->con)) {
+dpy_gl_update(scanout->con, 0, 0, scanout->width,
+  scanout->height);
+update_submitted = true;
+}
 }
 }
-return;
+
+if (update_submitted) {
+return;
+}
+if (!within_bounds) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside scanouts"
+  " bounds for flush %d: %d %d %d %d\n",
+  __func__, rf.resource_id, rf.r.x, rf.r.y,
+  rf.r.width, rf.r.height);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+return;
+}
 }
 
 if (!res->blob &&
-- 
2.39.2




[RFC v2 1/2] spice: Add an option for users to provide a preferred codec

2023-01-23 Thread Vivek Kasireddy
Giving users an option to choose a particular codec will enable
them to make an appropriate decision based on their hardware and
use-case.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 qemu-options.hx |  5 +
 ui/spice-core.c | 14 ++
 2 files changed, 19 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index 3aa3a2f5a3..aab8df0922 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2142,6 +2142,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
 "   [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n"
 "   [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n"
 "   [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
+"   [,preferred-codec=:\n"
 "   [,gl=[on|off]][,rendernode=]\n"
 "   enable spice\n"
 "   at least one of {port, tls-port} is mandatory\n",
@@ -2237,6 +2238,10 @@ SRST
 ``seamless-migration=[on|off]``
 Enable/disable spice seamless migration. Default is off.
 
+``preferred-codec=:``
+Provide the preferred codec the Spice server should use.
+Default would be spice:mjpeg.
+
 ``gl=[on|off]``
 Enable/disable OpenGL context. Default is off.
 
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 72f8f1681c..6e00211e3a 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -469,6 +469,9 @@ static QemuOptsList qemu_spice_opts = {
 },{
 .name = "streaming-video",
 .type = QEMU_OPT_STRING,
+},{
+.name = "preferred-codec",
+.type = QEMU_OPT_STRING,
 },{
 .name = "agent-mouse",
 .type = QEMU_OPT_BOOL,
@@ -644,6 +647,7 @@ static void qemu_spice_init(void)
 char *x509_key_file = NULL,
 *x509_cert_file = NULL,
 *x509_cacert_file = NULL;
+const char *preferred_codec = NULL;
 int port, tls_port, addr_flags;
 spice_image_compression_t compression;
 spice_wan_compression_t wan_compr;
@@ -795,6 +799,16 @@ static void qemu_spice_init(void)
 spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
 }
 
+preferred_codec = qemu_opt_get(opts, "preferred-codec");
+if (preferred_codec) {
+if (spice_server_set_video_codecs(spice_server, preferred_codec)) {
+error_report("Preferred codec name is not valid");
+exit(1);
+}
+} else {
+spice_server_set_video_codecs(spice_server, "spice:mjpeg");
+}
+
 spice_server_set_agent_mouse
 (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
 spice_server_set_playback_compression
-- 
2.37.2




[RFC v2 0/2] spice: Add an option to forward the dmabuf directly to the encoder (v2)

2023-01-23 Thread Vivek Kasireddy
This patch series adds options to select a preferred codec and also
to forward a dmabuf directly to the encoder module that is part of
the Spice server. Currently, gstreamer:h264 is the only combination
tested but additional work is ongoing to test other combinations. 

Tested with: -device virtio-gpu-pci,max_outputs=1,blob=true,xres=1920,yres=1080
 -spice port=3001,gl=on,disable-ticketing=on,dmabuf-encode=on,
  preferred-codec=gstreamer:h264

and remote-viewer --spice-debug spice://x.x.x.x:3001 on the client side.

Associated Spice server patches (v1) can be found here:
https://lists.freedesktop.org/archives/spice-devel/2023-January/052927.html

v2:
- Used the already available gl_scanout and gl_draw_async APIs instead
  of adding new ones to Spice.
- Improved the commit message of the second patch

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 

Vivek Kasireddy (2):
  spice: Add an option for users to provide a preferred codec
  spice: Add an option to forward the dmabuf directly to the encoder
(v2)

 include/ui/spice-display.h |  1 +
 qemu-options.hx| 11 +-
 ui/spice-core.c| 36 --
 ui/spice-display.c | 75 --
 4 files changed, 100 insertions(+), 23 deletions(-)

-- 
2.37.2




[RFC v2 2/2] spice: Add an option to forward the dmabuf directly to the encoder (v2)

2023-01-23 Thread Vivek Kasireddy
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 to a remote client.

Here is the flow of things from the Qemu side:
- Call gl_scanout (to update the fd) and gl_draw_async just like
  in the local display case.
- Additionally, create an update with the cmd set to QXL_CMD_DRAW
  to trigger the creation of a new drawable (associated with the fd)
  by the Spice server.
- Wait (or block) until the Encoder is done encoding the content.
- Unblock the pipeline once the async completion cookie is received.

v2:
- Use the existing gl_scanout and gl_draw_async APIs instead of
  adding new ones.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/spice-display.h |  1 +
 qemu-options.hx|  6 ++-
 ui/spice-core.c| 22 +--
 ui/spice-display.c | 75 --
 4 files changed, 81 insertions(+), 23 deletions(-)

diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index e271e011da..df74f5ee9b 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -153,6 +153,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=:\n"
-"   [,gl=[on|off]][,rendernode=]\n"
+"   [,gl=[on|off]][,rendernode=][,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=``
 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..90ada643a2 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, bool dmabuf)
 {
 SimpleSpiceUpdate *update;
 QXLDrawable *drawable;
@@ -168,15 +169,17 @@ static void 
qemu_spice_create_one_upda

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

2023-01-10 Thread Vivek Kasireddy
This patch series adds options to select a preferred codec and also
to forward a dmabuf directly to the encoder module that is part of
the Spice server. Currently, gstreamer:h264 is the only combination
tested but additional work is ongoing to test other combinations. 

Tested with: -device virtio-gpu-pci,max_outputs=1,blob=true,xres=1920,yres=1080
 -spice port=3001,gl=on,disable-ticketing=on,dmabuf-encode=on,
  preferred-codec=gstreamer:h264

and remote-viewer --spice-debug spice://x.x.x.x:3001 on the client side.

Associated Spice server patches can be found here:
https://lists.freedesktop.org/archives/spice-devel/2023-January/052927.html

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 

Vivek Kasireddy (2):
  spice: Add an option for users to provide a preferred codec
  spice: Add an option to forward the dmabuf directly to the encoder

 include/ui/spice-display.h |   2 +
 qemu-options.hx|  11 +++-
 ui/spice-core.c|  36 +++--
 ui/spice-display.c | 106 ++---
 4 files changed, 120 insertions(+), 35 deletions(-)

-- 
2.37.2




[RFC v1 1/2] spice: Add an option for users to provide a preferred codec

2023-01-10 Thread Vivek Kasireddy
Giving users an option to choose a particular codec will enable
them to make an appropriate decision based on their hardware and
use-case.

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 qemu-options.hx |  5 +
 ui/spice-core.c | 14 ++
 2 files changed, 19 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index 3aa3a2f5a3..aab8df0922 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2142,6 +2142,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
 "   [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n"
 "   [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n"
 "   [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
+"   [,preferred-codec=:\n"
 "   [,gl=[on|off]][,rendernode=]\n"
 "   enable spice\n"
 "   at least one of {port, tls-port} is mandatory\n",
@@ -2237,6 +2238,10 @@ SRST
 ``seamless-migration=[on|off]``
 Enable/disable spice seamless migration. Default is off.
 
+``preferred-codec=:``
+Provide the preferred codec the Spice server should use.
+Default would be spice:mjpeg.
+
 ``gl=[on|off]``
 Enable/disable OpenGL context. Default is off.
 
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 72f8f1681c..6e00211e3a 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -469,6 +469,9 @@ static QemuOptsList qemu_spice_opts = {
 },{
 .name = "streaming-video",
 .type = QEMU_OPT_STRING,
+},{
+.name = "preferred-codec",
+.type = QEMU_OPT_STRING,
 },{
 .name = "agent-mouse",
 .type = QEMU_OPT_BOOL,
@@ -644,6 +647,7 @@ static void qemu_spice_init(void)
 char *x509_key_file = NULL,
 *x509_cert_file = NULL,
 *x509_cacert_file = NULL;
+const char *preferred_codec = NULL;
 int port, tls_port, addr_flags;
 spice_image_compression_t compression;
 spice_wan_compression_t wan_compr;
@@ -795,6 +799,16 @@ static void qemu_spice_init(void)
 spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
 }
 
+preferred_codec = qemu_opt_get(opts, "preferred-codec");
+if (preferred_codec) {
+if (spice_server_set_video_codecs(spice_server, preferred_codec)) {
+error_report("Preferred codec name is not valid");
+exit(1);
+}
+} else {
+spice_server_set_video_codecs(spice_server, "spice:mjpeg");
+}
+
 spice_server_set_agent_mouse
 (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
 spice_server_set_playback_compression
-- 
2.37.2




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

2023-01-10 Thread Vivek Kasireddy
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 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 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 --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=:\n"
-"   [,gl=[on|off]][,rendernode=]\n"
+"   [,gl=[on|off]][,rendernode=][,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=``
 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

[PATCH v2 5/6] ui/gtk: Factor out tab window creation into a separate function

2022-11-17 Thread Vivek Kasireddy
Pull the code that creates a new window associated with a notebook
tab into a separate function. This new function can be useful not
just when user wants to detach a tab but also in the future when
a new window creation is needed in other scenarios.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk.c | 65 +++-
 1 file changed, 36 insertions(+), 29 deletions(-)

diff --git a/ui/gtk.c b/ui/gtk.c
index 4ac3655694..6b0369e3ed 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1400,6 +1400,41 @@ static gboolean gd_win_grab(void *opaque)
 return TRUE;
 }
 
+static void gd_tab_window_create(VirtualConsole *vc)
+{
+GtkDisplayState *s = vc->s;
+
+gtk_widget_set_sensitive(vc->menu_item, false);
+vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+#if defined(CONFIG_OPENGL)
+if (vc->gfx.esurface) {
+   eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
+   vc->gfx.esurface = NULL;
+}
+if (vc->gfx.esurface) {
+   eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
+   vc->gfx.ectx = NULL;
+}
+#endif
+gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
+
+g_signal_connect(vc->window, "delete-event",
+G_CALLBACK(gd_tab_window_close), vc);
+gtk_widget_show_all(vc->window);
+
+if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
+   GtkAccelGroup *ag = gtk_accel_group_new();
+   gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
+
+   GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
+  vc, NULL);
+   gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
+}
+
+gd_update_geometry_hints(vc);
+gd_update_caption(s);
+}
+
 static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
 {
 GtkDisplayState *s = opaque;
@@ -1411,35 +1446,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void 
*opaque)
FALSE);
 }
 if (!vc->window) {
-gtk_widget_set_sensitive(vc->menu_item, false);
-vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-#if defined(CONFIG_OPENGL)
-if (vc->gfx.esurface) {
-eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
-vc->gfx.esurface = NULL;
-}
-if (vc->gfx.esurface) {
-eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
-vc->gfx.ectx = NULL;
-}
-#endif
-gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
-
-g_signal_connect(vc->window, "delete-event",
- G_CALLBACK(gd_tab_window_close), vc);
-gtk_widget_show_all(vc->window);
-
-if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
-GtkAccelGroup *ag = gtk_accel_group_new();
-gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
-
-GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
-   vc, NULL);
-gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
-}
-
-gd_update_geometry_hints(vc);
-gd_update_caption(s);
+gd_tab_window_create(vc);
 }
 }
 
-- 
2.37.2




[PATCH v2 0/6] ui/gtk: Add a new parameter to assign connectors/monitors (v2)

2022-11-17 Thread Vivek Kasireddy
There is a need (expressed by several customers/users) to assign
ownership of one or more physical monitors/connectors to individual
Guests such that there is a clear notion of which Guest's contents
are being displayed on any given monitor. Given that there is always
a Display Server/Compositor running on the Host, monitor ownership
can never truly be transferred to Guests. However, the closest we
can come to realizing this concept is to request the Host compositor
to fullscreen the Guest's windows on individual monitors. This way,
it would become possible to have 4 different Guests' windows be
displayed on 4 different monitors or a single Guest's windows (or
virtual consoles/outputs) be displayed on 4 monitors or any such
combination.

This patch series attempts to accomplish this by introducing a new
parameter named "connector" to assign the monitors to the GFX VCs
associated with a Guest. If the assigned monitor is not connected,
then the Guest's window would not be displayed anywhere similar to
how a Host compositor would behave when the connectors are not
connected. Once the monitor is hotplugged, the Guest's window(s)
would be positioned on the assigned monitor.

The first 3 patches are bug fixes associated with pointer positioning
in relative mode. The 4th patch is also a bug fix to ensure that 
context related objects are destroyed when an associated window is
destroyed. The 5th patch is a minor refactor and the last patch
introduces the new parameter. This patch series is expected to
supersede a similar series from Dongwon Kim here:
https://lists.nongnu.org/archive/html/qemu-devel/2022-07/msg03214.html

Example Usage: -device virtio-gpu-pci,max_outputs=2,blob=true..
   -display gtk,gl=on,connector.0=eDP-1,connector.1=DP-1.

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Cc: Markus Armbruster 
Cc: Marc-André Lureau 

Vivek Kasireddy (6):
  ui/gtk: Consider the scaling factor when getting the root coordinates
  ui/gtk-gl-area: Don't forget to calculate the scaling factors in draw
  ui/gtk: Handle relative mode events correctly with Wayland compositors
  ui/gtk: Disable the scanout when a detached tab is closed
  ui/gtk: Factor out tab window creation into a separate function
  ui/gtk: Add a new parameter to assign connectors/monitors to GFX VCs
(v2)

 include/ui/gtk.h |   3 +
 qapi/ui.json |  10 +-
 qemu-options.hx  |   5 +-
 ui/gtk-egl.c |   2 +
 ui/gtk-gl-area.c |   7 +
 ui/gtk.c | 383 +++
 6 files changed, 375 insertions(+), 35 deletions(-)

-- 
2.37.2




[PATCH v2 2/6] ui/gtk-gl-area: Don't forget to calculate the scaling factors in draw

2022-11-17 Thread Vivek Kasireddy
Just like it is done in gtk-egl.c, we need to ensure that the scaling
factors are correctly calculated in draw callback. Otherwise, they
would just be set to 1.0. And, use gtk_widget_get_allocated_width/height
variants to determine width and height in the Wayland case similar to
how it is done in draw.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk-gl-area.c | 5 +
 ui/gtk.c | 9 +++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 682638a197..6799805f8e 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -81,6 +81,9 @@ void gd_gl_area_draw(VirtualConsole *vc)
 egl_dmabuf_create_sync(dmabuf);
 }
 #endif
+vc->gfx.scale_x = (double)ww / vc->gfx.w;
+vc->gfx.scale_y = (double)wh / vc->gfx.h;
+
 glFlush();
 #ifdef CONFIG_GBM
 if (dmabuf) {
@@ -100,6 +103,8 @@ void gd_gl_area_draw(VirtualConsole *vc)
 
 surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
+vc->gfx.scale_x = (double)ww / vc->gfx.w;
+vc->gfx.scale_y = (double)wh / vc->gfx.h;
 }
 }
 
diff --git a/ui/gtk.c b/ui/gtk.c
index 6c23903173..9d0c27c9e7 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -883,9 +883,14 @@ static gboolean gd_motion_event(GtkWidget *widget, 
GdkEventMotion *motion,
 fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 
 window = gtk_widget_get_window(vc->gfx.drawing_area);
-ww = gdk_window_get_width(window);
-wh = gdk_window_get_height(window);
 ws = gdk_window_get_scale_factor(window);
+if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
+} else {
+ww = gdk_window_get_width(window);
+wh = gdk_window_get_height(window);
+}
 
 mx = my = 0;
 if (ww > fbw) {
-- 
2.37.2




[PATCH v2 3/6] ui/gtk: Handle relative mode events correctly with Wayland compositors

2022-11-17 Thread Vivek Kasireddy
gdk_device_warp() is a no-op when running in a Host environment that is
Wayland based as the Wayland protocol does not allow clients to move
the cursor to a specific location. This presents a problem when Qemu is
running with GTK UI + relative mouse mode, as we would no longer be able
to warp the cursor to the Guest's location like it is done when running
in Xorg-based environments. To solve this problem, we just store the
Guest's cursor location and add/subtract the difference compared to the
Host's cursor location when injecting the next motion event.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk.c | 71 ++--
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index ae0f53740d..f8df042f95 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -130,6 +130,8 @@ struct GtkDisplayState {
 gboolean last_set;
 int last_x;
 int last_y;
+int guest_x;
+int guest_y;
 int grab_x_root;
 int grab_y_root;
 VirtualConsole *kbd_owner;
diff --git a/ui/gtk.c b/ui/gtk.c
index 9d0c27c9e7..8ccc948813 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -447,22 +447,44 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
  int x, int y, int visible)
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-GdkDisplay *dpy;
+GtkDisplayState *s = vc->s;
+GdkDisplay *dpy = gtk_widget_get_display(vc->gfx.drawing_area);
 gint x_root, y_root;
 
 if (qemu_input_is_absolute()) {
 return;
 }
+/*
+ * When the mouse cursor moves from one vc (or connector in guest
+ * terminology) to another, some guest compositors (e.g. Weston)
+ * set x and y to 0 on the old vc. We check for this condition
+ * and return right away as we do not want to move the cursor
+ * back to the old vc (at 0, 0).
+ */
+if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+if (s->ptr_owner != vc || (x == 0 && y == 0)) {
+return;
+}
+}
+/*
+ * Since Wayland compositors do not support clients warping/moving
+ * the cursor, we just store the Guest's cursor location here and
+ * add or subtract the difference when injecting the next motion event.
+ */
+if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+s->guest_x = x;
+s->guest_y = y;
+return;
+}
 
-dpy = gtk_widget_get_display(vc->gfx.drawing_area);
 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
x * vc->gfx.scale_x, y * vc->gfx.scale_y,
_root, _root);
 gdk_device_warp(gd_get_pointer(dpy),
 gtk_widget_get_screen(vc->gfx.drawing_area),
 x_root, y_root);
-vc->s->last_x = x;
-vc->s->last_y = y;
+s->last_x = x;
+s->last_y = y;
 }
 
 static void gd_cursor_define(DisplayChangeListener *dcl,
@@ -869,6 +891,7 @@ static gboolean gd_motion_event(GtkWidget *widget, 
GdkEventMotion *motion,
 {
 VirtualConsole *vc = opaque;
 GtkDisplayState *s = vc->s;
+GdkDisplay *dpy = gtk_widget_get_display(vc->gfx.drawing_area);
 GdkWindow *window;
 int x, y;
 int mx, my;
@@ -915,14 +938,41 @@ static gboolean gd_motion_event(GtkWidget *widget, 
GdkEventMotion *motion,
  0, surface_height(vc->gfx.ds));
 qemu_input_event_sync();
 } else if (s->last_set && s->ptr_owner == vc) {
-qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
-qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
+int dx = x - s->last_x;
+int dy = y - s->last_y;
+
+/*
+ * To converge/sync the Guest's and Host's cursor locations more
+ * accurately, we can avoid doing the / 2 below but it appears
+ * some Guest compositors (e.g. Weston) do not like large jumps;
+ * so we just do / 2 which seems to work reasonably well.
+ */
+if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+dx += s->guest_x ? (x - s->guest_x) / 2 : 0;
+dy += s->guest_y ? (y - s->guest_y) / 2 : 0;
+s->guest_x = 0;
+s->guest_y = 0;
+}
+qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, dx);
+qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, dy);
 qemu_input_event_sync();
 }
 s->last_x = x;
 s->last_y = y;
 s->last_set = TRUE;
 
+/*
+ * When running in Wayland environment, we don't grab the cursor; so,
+ * we want to return right away as it would not make sense to warp it
+ * (below).
+ */
+if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+if (s->ptr_owner != vc) {
+s->ptr_owner = vc;
+}
+

[PATCH v2 4/6] ui/gtk: Disable the scanout when a detached tab is closed

2022-11-17 Thread Vivek Kasireddy
When a detached tab window is closed, the underlying (EGL) context
is destroyed; therefore, disable the scanout which also destroys the
underlying framebuffer (id) and other objects. Also add calls to
make the context current in disable scanout and other missing places.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk-egl.c | 2 ++
 ui/gtk-gl-area.c | 2 ++
 ui/gtk.c | 1 +
 3 files changed, 5 insertions(+)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 35f917ceb1..396cb6590a 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -214,6 +214,8 @@ void gd_egl_scanout_disable(DisplayChangeListener *dcl)
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+   vc->gfx.esurface, vc->gfx.ectx);
 vc->gfx.w = 0;
 vc->gfx.h = 0;
 gtk_egl_set_scanout_mode(vc, false);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 6799805f8e..816f213158 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -275,6 +275,7 @@ void gd_gl_area_scanout_disable(DisplayChangeListener *dcl)
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
 gtk_gl_area_set_scanout_mode(vc, false);
 }
 
@@ -283,6 +284,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
 if (vc->gfx.guest_fb.dmabuf) {
 graphic_hw_gl_block(vc->gfx.dcl.con, true);
 vc->gfx.guest_fb.dmabuf->draw_submitted = true;
diff --git a/ui/gtk.c b/ui/gtk.c
index 8ccc948813..4ac3655694 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1367,6 +1367,7 @@ static gboolean gd_tab_window_close(GtkWidget *widget, 
GdkEvent *event,
 VirtualConsole *vc = opaque;
 GtkDisplayState *s = vc->s;
 
+dpy_gl_scanout_disable(vc->gfx.dcl.con);
 gtk_widget_set_sensitive(vc->menu_item, true);
 gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
 gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
-- 
2.37.2




[PATCH v2 6/6] ui/gtk: Add a new parameter to assign connectors/monitors to GFX VCs (v2)

2022-11-17 Thread Vivek Kasireddy
The new parameter named "connector" can be used to assign physical
monitors/connectors to individual GFX VCs such that when the monitor
is connected or hotplugged, the associated GTK window would be
moved to it. If the monitor is disconnected or unplugged, the
associated GTK window would be destroyed and a relevant disconnect
event would be sent to the Guest.

Usage: -device virtio-gpu-pci,max_outputs=2,blob=true,...
   -display gtk,gl=on,connectors.0=eDP-1,connectors.1=DP-1.

v2:
- Make various style improvements suggested by Markus.
- Fullscreen the window only if the fullscreen option is enabled.

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Cc: Markus Armbruster 
Cc: Marc-André Lureau 
Acked-by: Markus Armbruster  (QAPI schema)
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |   1 +
 qapi/ui.json |  10 +-
 qemu-options.hx  |   5 +-
 ui/gtk.c | 260 +--
 4 files changed, 268 insertions(+), 8 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index f8df042f95..bbea0316fb 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -83,6 +83,7 @@ typedef struct VirtualConsole {
 GtkWidget *menu_item;
 GtkWidget *tab_item;
 GtkWidget *focus;
+GdkMonitor *monitor;
 VirtualConsoleType type;
 union {
 VirtualGfxConsole gfx;
diff --git a/qapi/ui.json b/qapi/ui.json
index 0abba3e930..a4d1616445 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1201,6 +1201,13 @@
 #   Since 7.1
 # @show-menubar: Display the main window menubar. Defaults to "on".
 #Since 8.0
+# @connectors:  List of physical monitor/connector names where the GTK
+#   windows containing the respective graphics virtual consoles
+#   (VCs) are to be placed. If a mapping exists for a VC, it
+#   will be moved to that specific monitor or else it would
+#   not be displayed anywhere and would appear disconnected
+#   to the guest.
+#   Since 8.0
 #
 # Since: 2.12
 ##
@@ -1208,7 +1215,8 @@
   'data': { '*grab-on-hover' : 'bool',
 '*zoom-to-fit'   : 'bool',
 '*show-tabs' : 'bool',
-'*show-menubar'  : 'bool'  } }
+'*show-menubar'  : 'bool',
+'*connectors': ['str'] } }
 
 ##
 # @DisplayEGLHeadless:
diff --git a/qemu-options.hx b/qemu-options.hx
index eb38e5dc40..ae6289e4f3 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1980,7 +1980,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
 #if defined(CONFIG_GTK)
 "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
 "
[,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
-"[,show-menubar=on|off]\n"
+"[,show-menubar=on|off][,connectors.=]\n"
 #endif
 #if defined(CONFIG_VNC)
 "-display vnc=[,]\n"
@@ -2075,6 +2075,9 @@ SRST
 
 ``show-menubar=on|off`` : Display the main window menubar, defaults to 
"on"
 
+``connectors=`` : VC to connector mappings to display the VC
+ window on a specific monitor
+
 ``curses[,charset=]``
 Display video output via curses. For graphics device models
 which support a text mode, QEMU can display this output using a
diff --git a/ui/gtk.c b/ui/gtk.c
index 6b0369e3ed..03fc454a1f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -37,6 +37,7 @@
 #include "qapi/qapi-commands-misc.h"
 #include "qemu/cutils.h"
 #include "qemu/main-loop.h"
+#include "qemu/option.h"
 
 #include "ui/console.h"
 #include "ui/gtk.h"
@@ -116,6 +117,11 @@
 
 #define HOTKEY_MODIFIERS(GDK_CONTROL_MASK | GDK_MOD1_MASK)
 
+/* Upper limit on number of times to check for a valid monitor name */
+#define MAX_NUM_RETRIES 5
+/* Max num of milliseconds to wait before checking for a valid monitor name */
+#define WAIT_MS 50
+
 static const guint16 *keycode_map;
 static size_t keycode_maplen;
 
@@ -126,6 +132,14 @@ struct VCChardev {
 };
 typedef struct VCChardev VCChardev;
 
+typedef struct gd_monitor_data {
+GtkDisplayState *s;
+GdkDisplay *dpy;
+GdkMonitor *monitor;
+QEMUTimer *hp_timer;
+int num_retries;
+} gd_monitor_data;
+
 #define TYPE_CHARDEV_VC "chardev-vc"
 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
  TYPE_CHARDEV_VC)
@@ -461,7 +475,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
  * and return right away as we do not want to move the cursor
  * back to the old vc (at 0, 0).
  */
-if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
+if (GDK_IS_WAYLAND_DISPLAY(dpy) || s->opts->u.gtk.has_connectors) {
 if (s->ptr_owner != vc || (x == 0 && y == 0)) {
 return;
 }
@@ -962,11 +976,11 @@ static gboolean gd_motio

[PATCH v2 1/6] ui/gtk: Consider the scaling factor when getting the root coordinates

2022-11-17 Thread Vivek Kasireddy
Since gdk_window_get_root_coords() expects a position within the window,
we need to translate Guest's cooridinates to window local coordinates
by multiplying them with the scaling factor.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/ui/gtk.c b/ui/gtk.c
index 92daaa6a6e..6c23903173 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -456,7 +456,8 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
 
 dpy = gtk_widget_get_display(vc->gfx.drawing_area);
 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
-   x, y, _root, _root);
+   x * vc->gfx.scale_x, y * vc->gfx.scale_y,
+   _root, _root);
 gdk_device_warp(gd_get_pointer(dpy),
 gtk_widget_get_screen(vc->gfx.drawing_area),
 x_root, y_root);
-- 
2.37.2




[PATCH v1] ui/gtk: Add support for extended absolute mode for the pointer device

2022-11-17 Thread Vivek Kasireddy
Currently, the range of the absolute pointer device (usb-tablet) is
restricted to any one monitor. This presents a problem when there are
multiple VCs (Guest GTK windows) located on different monitors.
Therefore, it makes sense to extend the range of the absolute pointer
device to span all monitors. This would work nicely as long as the
monitors (aka outputs/connectors/displays) on the Host and Guest are
in alignment with each other (i.e, if the Host has monitor 2, 3, 4
to the right of monitor 1, then Guest's need to be arranged accordingly).

Relative mode could also be used in these situations but the user
experience is not as seamless as the absolute mode.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  1 +
 qapi/ui.json |  6 -
 qemu-options.hx  |  5 +++-
 ui/gtk.c | 63 +++-
 4 files changed, 56 insertions(+), 19 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index ae0f53740d..01eb560b0b 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -136,6 +136,7 @@ struct GtkDisplayState {
 VirtualConsole *ptr_owner;
 
 gboolean full_screen;
+gboolean ext_abs_mode;
 
 GdkCursor *null_cursor;
 Notifier mouse_mode_notifier;
diff --git a/qapi/ui.json b/qapi/ui.json
index 0abba3e930..1ce2945c84 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1201,6 +1201,9 @@
 #   Since 7.1
 # @show-menubar: Display the main window menubar. Defaults to "on".
 #Since 8.0
+# @extend-abs-mode: Extend the absolute mode across all monitors or
+#   limit it to just one. Defaults to "off".
+#   Since 8.0
 #
 # Since: 2.12
 ##
@@ -1208,7 +1211,8 @@
   'data': { '*grab-on-hover' : 'bool',
 '*zoom-to-fit'   : 'bool',
 '*show-tabs' : 'bool',
-'*show-menubar'  : 'bool'  } }
+'*show-menubar'  : 'bool',
+'*extend-abs-mode'  : 'bool'  } }
 
 ##
 # @DisplayEGLHeadless:
diff --git a/qemu-options.hx b/qemu-options.hx
index eb38e5dc40..e6ef0634cd 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1980,7 +1980,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
 #if defined(CONFIG_GTK)
 "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
 "
[,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
-"[,show-menubar=on|off]\n"
+"[,show-menubar=on|off][,extend-abs-mode=on|off]\n"
 #endif
 #if defined(CONFIG_VNC)
 "-display vnc=[,]\n"
@@ -2075,6 +2075,9 @@ SRST
 
 ``show-menubar=on|off`` : Display the main window menubar, defaults to 
"on"
 
+``extend-abs-mode=on|off`` : Extend the absolute mode across all
+ monitors or limit it to just one.
+
 ``curses[,charset=]``
 Display video output via curses. For graphics device models
 which support a text mode, QEMU can display this output using a
diff --git a/ui/gtk.c b/ui/gtk.c
index 2be9755b0a..a4644675b2 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -883,12 +883,42 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t 
*cr, void *opaque)
 return TRUE;
 }
 
+static void gd_calc_width_height(VirtualConsole *vc,
+ uint32_t *max_w, uint32_t *max_h)
+{
+GtkDisplayState *s = vc->s;
+GdkDisplay *dpy = gtk_widget_get_display(vc->gfx.drawing_area);
+GdkRectangle geometry;
+int i;
+
+if (s->ext_abs_mode) {
+for (i = 0; i < gdk_display_get_n_monitors(dpy); i++) {
+gdk_monitor_get_geometry(gdk_display_get_monitor(dpy, i),
+ );
+if (geometry.x + geometry.width > *max_w) {
+*max_w = geometry.x + geometry.width;
+}
+if (geometry.y + geometry.height > *max_h) {
+*max_h = geometry.y + geometry.height;
+}
+}
+} else {
+*max_w = surface_width(vc->gfx.ds);
+*max_h = surface_height(vc->gfx.ds);
+}
+}
+
 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
 void *opaque)
 {
 VirtualConsole *vc = opaque;
 GtkDisplayState *s = vc->s;
-GdkWindow *window;
+GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
+GdkDisplay *dpy = gtk_widget_get_display(widget);
+GdkWindow *window = gtk_widget_get_window(widget);
+GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, window);
+GdkRectangle geometry;
+uint32_t max_w = 0, max_h = 0;
 int x, y;
 int mx, my;
 int fbh, fbw;
@@ -901,7 +931,6 @@ static gboolean gd_motion_event(GtkWidget *widget, 
GdkEventMotion *motion,
 fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;

[PATCH v1 1/2] virtio-gpu: Provide position info (x, y) to the Guest

2022-11-17 Thread Vivek Kasireddy
While filling out the display info such as width, height to
be provided to the Guest, make sure that the position information
(x, y) is also included. This position info corresponds with the
x and y fields mentioned in the spec:
https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-gpu.tex#L343

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-base.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index a29f191aa8..3b6b480131 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -47,6 +47,8 @@ virtio_gpu_base_fill_display_info(VirtIOGPUBase *g,
 dpy_info->pmodes[i].enabled = 1;
 dpy_info->pmodes[i].r.width = cpu_to_le32(g->req_state[i].width);
 dpy_info->pmodes[i].r.height = cpu_to_le32(g->req_state[i].height);
+dpy_info->pmodes[i].r.x = cpu_to_le32(g->req_state[i].x);
+dpy_info->pmodes[i].r.y = cpu_to_le32(g->req_state[i].y);
 }
 }
 }
-- 
2.37.2




[PATCH v1 0/2] virtio-gpu: Provide display position info (x, y) to the Guest

2022-11-17 Thread Vivek Kasireddy
While filling out the display info such as width, height to
be provided to the Guest, make sure that the position information
(x, y) is also included. This position info corresponds with the
x and y fields mentioned in the spec:
https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-gpu.tex#L343

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 

Vivek Kasireddy (2):
  virtio-gpu: Provide position info (x, y) to the Guest
  ui/gtk: Include the position info while setting the ui info

 hw/display/virtio-gpu-base.c |  2 ++
 ui/gtk.c | 20 
 2 files changed, 22 insertions(+)

-- 
2.37.2




[PATCH v1 2/2] ui/gtk: Include the position info while setting the ui info

2022-11-17 Thread Vivek Kasireddy
In situtations where the Guest uses multiple displays/outputs, this
position info is useful for aligning the Guest's outputs with that of
the Host's.

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk.c | 20 
 1 file changed, 20 insertions(+)

diff --git a/ui/gtk.c b/ui/gtk.c
index 92daaa6a6e..12b3bc6481 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -714,12 +714,31 @@ static gboolean gd_window_close(GtkWidget *widget, 
GdkEvent *event,
 return TRUE;
 }
 
+static void gd_set_ui_window_position(VirtualConsole *vc, QemuUIInfo *info)
+{
+GdkDisplay *dpy = gtk_widget_get_display(vc->gfx.drawing_area);
+GdkWindow *window;
+GdkMonitor *monitor;
+GdkRectangle geometry;
+
+if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
+return;
+}
+
+window = gtk_widget_get_window(vc->gfx.drawing_area);
+monitor = gdk_display_get_monitor_at_window(dpy, window);
+gdk_monitor_get_geometry(monitor, );
+info->xoff = geometry.x;
+info->yoff = geometry.y;
+}
+
 static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate)
 {
 QemuUIInfo info;
 
 info = *dpy_get_ui_info(vc->gfx.dcl.con);
 info.refresh_rate = refresh_rate;
+gd_set_ui_window_position(vc, );
 dpy_set_ui_info(vc->gfx.dcl.con, , true);
 }
 
@@ -730,6 +749,7 @@ static void gd_set_ui_size(VirtualConsole *vc, gint width, 
gint height)
 info = *dpy_get_ui_info(vc->gfx.dcl.con);
 info.width = width;
 info.height = height;
+gd_set_ui_window_position(vc, );
 dpy_set_ui_info(vc->gfx.dcl.con, , true);
 }
 
-- 
2.37.2




[PATCH v1 3/3] ui/gtk: Add a new parameter to assign connectors/monitors to GFX VCs

2022-09-16 Thread Vivek Kasireddy
The new parameter named "connector" can be used to assign physical
monitors/connectors to individual GFX VCs such that when the monitor
is connected or hotplugged, the associated GTK window would be
fullscreened on it. If the monitor is disconnected or unplugged,
the associated GTK window would be destroyed and a relevant
disconnect event would be sent to the Guest.

Usage: -device virtio-gpu-pci,max_outputs=2,blob=true,xres=1920,yres=1080...
   -display gtk,gl=on,connector.0=eDP-1,connector.1=DP-1.

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Cc: Daniel P. Berrangé 
Cc: Markus Armbruster 
Cc: Philippe Mathieu-Daudé 
Cc: Marc-André Lureau 
Cc: Thomas Huth 
Signed-off-by: Vivek Kasireddy 
---
 qapi/ui.json|   9 ++-
 qemu-options.hx |   1 +
 ui/gtk.c| 168 
 3 files changed, 177 insertions(+), 1 deletion(-)

diff --git a/qapi/ui.json b/qapi/ui.json
index 286c5731d1..86787a4c95 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1199,13 +1199,20 @@
 #   interfaces (e.g. VGA and virtual console character devices)
 #   by default.
 #   Since 7.1
+# @connector:   List of physical monitor/connector names where the GTK windows
+#   containing the respective graphics virtual consoles (VCs) are
+#   to be placed. If a mapping exists for a VC, it will be
+#   fullscreened on that specific monitor or else it would not be
+#   displayed anywhere and would appear disconnected to the guest.
+#   Since 7.2
 #
 # Since: 2.12
 ##
 { 'struct'  : 'DisplayGTK',
   'data': { '*grab-on-hover' : 'bool',
 '*zoom-to-fit'   : 'bool',
-'*show-tabs' : 'bool'  } }
+'*show-tabs' : 'bool',
+'*connector' : ['str']  } }
 
 ##
 # @DisplayEGLHeadless:
diff --git a/qemu-options.hx b/qemu-options.hx
index 31c04f7eea..576b65ef9f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1945,6 +1945,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
 #if defined(CONFIG_GTK)
 "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
 "
[,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
+"[,connector.=]\n"
 #endif
 #if defined(CONFIG_VNC)
 "-display vnc=[,]\n"
diff --git a/ui/gtk.c b/ui/gtk.c
index 945c550909..651aaaf174 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -37,6 +37,7 @@
 #include "qapi/qapi-commands-misc.h"
 #include "qemu/cutils.h"
 #include "qemu/main-loop.h"
+#include "qemu/option.h"
 
 #include "ui/console.h"
 #include "ui/gtk.h"
@@ -115,6 +116,7 @@
 #endif
 
 #define HOTKEY_MODIFIERS(GDK_CONTROL_MASK | GDK_MOD1_MASK)
+#define MAX_NUM_ATTEMPTS 5
 
 static const guint16 *keycode_map;
 static size_t keycode_maplen;
@@ -126,6 +128,15 @@ struct VCChardev {
 };
 typedef struct VCChardev VCChardev;
 
+struct gd_monitor_data {
+GtkDisplayState *s;
+GdkDisplay *dpy;
+GdkMonitor *monitor;
+QEMUTimer *hp_timer;
+int attempt;
+};
+typedef struct gd_monitor_data gd_monitor_data;
+
 #define TYPE_CHARDEV_VC "chardev-vc"
 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
  TYPE_CHARDEV_VC)
@@ -1385,6 +1396,147 @@ static void gd_menu_untabify(GtkMenuItem *item, void 
*opaque)
 }
 }
 
+static void gd_monitor_fullscreen(GdkDisplay *dpy, VirtualConsole *vc,
+  gint monitor_num)
+{
+GtkDisplayState *s = vc->s;
+
+if (!vc->window) {
+gd_tab_window_create(vc);
+}
+gtk_window_fullscreen_on_monitor(GTK_WINDOW(vc->window),
+ gdk_display_get_default_screen(dpy),
+ monitor_num);
+s->full_screen = TRUE;
+gd_update_cursor(vc);
+}
+
+static int gd_monitor_lookup(GdkDisplay *dpy, char *label)
+{
+GdkMonitor *monitor;
+const char *monitor_name;
+int i, total_monitors;
+
+total_monitors = gdk_display_get_n_monitors(dpy);
+for (i = 0; i < total_monitors; i++) {
+monitor = gdk_display_get_monitor(dpy, i);
+if (monitor) {
+monitor_name = gdk_monitor_get_model(monitor);
+if (monitor_name && !strcmp(monitor_name, label)) {
+return i;
+}
+}
+}
+return -1;
+}
+
+static void gd_monitor_check_vcs(GdkDisplay *dpy, GdkMonitor *monitor,
+ GtkDisplayState *s)
+{
+VirtualConsole *vc;
+const char *monitor_name = gdk_monitor_get_model(monitor);
+int i;
+
+for (i = 0; i < s->nb_vcs; i++) {
+vc = >vc[i];
+if (!strcmp(vc->label, monitor_name)) {
+gd_monitor_fullscreen(dpy, vc, gd_monitor_lookup(dpy, vc->label));
+gd_set_ui_size(vc, surface_w

[PATCH v1 0/3] ui/gtk: Add a new parameter to assign connectors/monitors to Guests' windows

2022-09-16 Thread Vivek Kasireddy
There is a need (expressed by several customers/users) to assign
ownership of one or more physical monitors/connectors to individual
Guests such that there is a clear notion of which Guest's contents
are being displayed on any given monitor. Given that there is always
a Display Server/Compositor running on the Host, monitor ownership
can never truly be transferred to Guests. However, the closest we
can come to realizing this concept is to request the Host compositor
to fullscreen the Guest's windows on individual monitors. This way,
it would become possible to have 4 different Guests' windows be
displayed on 4 different monitors or a single Guest's windows (or
virtual consoles/outputs) be displayed on 4 monitors or any such
combination.

This patch series attempts to accomplish this by introducing a new
parameter named "connector" to assign the monitors to the GFX VCs
associated with a Guest. If the assigned monitor is not connected,
then the Guest's window would not be displayed anywhere similar to
how a Host compositor would behave when the connectors are not
connected. Once the monitor is hotplugged, the Guest's window(s)
would be fullscreened on the assigned monitor. The first patch is
just a bug fix to destroy context related objects when an associated
window is destroyed. The second patch is a minor refactor and the
third and last patch introduces the new parameter. This patch series
is expected to supersede a similar series from Dongwon Kim here:
https://lists.nongnu.org/archive/html/qemu-devel/2022-07/msg03214.html

Example Usage: -device virtio-gpu-pci,max_outputs=2,blob=true..
   -display gtk,gl=on,connector.0=eDP-1,connector.1=DP-1.

Cc: Dongwon Kim 
Cc: Gerd Hoffmann 
Cc: Daniel P. Berrangé 
Cc: Markus Armbruster 
Cc: Philippe Mathieu-Daudé 
Cc: Marc-André Lureau 
Cc: Thomas Huth 

Vivek Kasireddy (3):
  ui/gtk: Disable the scanout when a detached tab is closed
  ui/gtk: Factor out tab window creation into a separate function
  ui/gtk: Add a new parameter to assign connectors/monitors to GFX VCs

 qapi/ui.json |   9 +-
 qemu-options.hx  |   1 +
 ui/gtk-egl.c |   2 +
 ui/gtk-gl-area.c |   2 +
 ui/gtk.c | 220 ++-
 5 files changed, 211 insertions(+), 23 deletions(-)

-- 
2.37.2




[PATCH v1 1/3] ui/gtk: Disable the scanout when a detached tab is closed

2022-09-16 Thread Vivek Kasireddy
When a detached tab window is closed, the underlying (EGL) context
is destroyed; therefore, disable the scanout which also destroys the
underlying framebuffer (id) and other objects. Also add calls to
make the context current in disable scanout and other missing places.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk-egl.c | 2 ++
 ui/gtk-gl-area.c | 2 ++
 ui/gtk.c | 1 +
 3 files changed, 5 insertions(+)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b5bffbab25..0f9ef11f4c 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -211,6 +211,8 @@ void gd_egl_scanout_disable(DisplayChangeListener *dcl)
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+   vc->gfx.esurface, vc->gfx.ectx);
 vc->gfx.w = 0;
 vc->gfx.h = 0;
 gtk_egl_set_scanout_mode(vc, false);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 682638a197..0b9fd4713a 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -270,6 +270,7 @@ void gd_gl_area_scanout_disable(DisplayChangeListener *dcl)
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
 gtk_gl_area_set_scanout_mode(vc, false);
 }
 
@@ -278,6 +279,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
 {
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
 if (vc->gfx.guest_fb.dmabuf) {
 graphic_hw_gl_block(vc->gfx.dcl.con, true);
 vc->gfx.guest_fb.dmabuf->draw_submitted = true;
diff --git a/ui/gtk.c b/ui/gtk.c
index 1467b8c7d7..0ff31cb852 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1302,6 +1302,7 @@ static gboolean gd_tab_window_close(GtkWidget *widget, 
GdkEvent *event,
 VirtualConsole *vc = opaque;
 GtkDisplayState *s = vc->s;
 
+dpy_gl_scanout_disable(vc->gfx.dcl.con);
 gtk_widget_set_sensitive(vc->menu_item, true);
 gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
 gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
-- 
2.37.2




[PATCH v1 2/3] ui/gtk: Factor out tab window creation into a separate function

2022-09-16 Thread Vivek Kasireddy
Pull the code that creates a new window associated with a notebook
tab into a separate function. This new function can be useful not
just when user wants to detach a tab but also in the future when
a new window creation is needed in other scenarios.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk.c | 65 +++-
 1 file changed, 36 insertions(+), 29 deletions(-)

diff --git a/ui/gtk.c b/ui/gtk.c
index 0ff31cb852..945c550909 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1335,6 +1335,41 @@ static gboolean gd_win_grab(void *opaque)
 return TRUE;
 }
 
+static void gd_tab_window_create(VirtualConsole *vc)
+{
+GtkDisplayState *s = vc->s;
+
+gtk_widget_set_sensitive(vc->menu_item, false);
+vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+#if defined(CONFIG_OPENGL)
+if (vc->gfx.esurface) {
+   eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
+   vc->gfx.esurface = NULL;
+}
+if (vc->gfx.esurface) {
+   eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
+   vc->gfx.ectx = NULL;
+}
+#endif
+gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
+
+g_signal_connect(vc->window, "delete-event",
+G_CALLBACK(gd_tab_window_close), vc);
+gtk_widget_show_all(vc->window);
+
+if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
+   GtkAccelGroup *ag = gtk_accel_group_new();
+   gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
+
+   GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
+  vc, NULL);
+   gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
+}
+
+gd_update_geometry_hints(vc);
+gd_update_caption(s);
+}
+
 static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
 {
 GtkDisplayState *s = opaque;
@@ -1346,35 +1381,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void 
*opaque)
FALSE);
 }
 if (!vc->window) {
-gtk_widget_set_sensitive(vc->menu_item, false);
-vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-#if defined(CONFIG_OPENGL)
-if (vc->gfx.esurface) {
-eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
-vc->gfx.esurface = NULL;
-}
-if (vc->gfx.esurface) {
-eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
-vc->gfx.ectx = NULL;
-}
-#endif
-gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
-
-g_signal_connect(vc->window, "delete-event",
- G_CALLBACK(gd_tab_window_close), vc);
-gtk_widget_show_all(vc->window);
-
-if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
-GtkAccelGroup *ag = gtk_accel_group_new();
-gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
-
-GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
-   vc, NULL);
-gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
-}
-
-gd_update_geometry_hints(vc);
-gd_update_caption(s);
+gd_tab_window_create(vc);
 }
 }
 
-- 
2.37.2




[PATCH v1] ui/gtk-egl: Check for a valid context before making EGL calls

2022-03-06 Thread Vivek Kasireddy
Since not all listeners (i.e VirtualConsoles) of GL events have
a valid EGL context, make sure that there is a valid context
before making EGL calls.

This fixes the following crash seen while launching the VM with
"-device virtio-gpu-pci,max_outputs=1,blob=true -display gtk,gl=on"

No provider of eglCreateImageKHR found.  Requires one of:
EGL_KHR_image
EGL_KHR_image_base

Fixes: 7cc712e9862ff ("ui: dispatch GL events to all listeners")

Cc: Marc-André Lureau 
Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk-egl.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index e3bd4bc274..31175827d0 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -244,6 +244,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 #ifdef CONFIG_GBM
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+if (!vc->gfx.ectx || !vc->gfx.esurface) {
+return;
+}
+
 eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
 
@@ -269,6 +273,10 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
 #ifdef CONFIG_GBM
 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
 
+if (!vc->gfx.ectx || !vc->gfx.esurface) {
+return;
+}
+
 if (dmabuf) {
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
-- 
2.35.1




[PATCH v6 2/5] ui/egl: Add egl helpers to help with synchronization

2021-09-14 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  2 ++
 ui/egl-helpers.c | 26 ++
 3 files changed, 30 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index 3be21497a2..45ec129174 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..2c3ba92b53 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,8 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..385a3fa752 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -287,6 +287,32 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
 dmabuf->texture = 0;
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
 #endif /* CONFIG_GBM */
 
 /* -- */
-- 
2.30.2




[PATCH v6 5/5] virtio-gpu: Add gl_flushed callback

2021-09-14 Thread Vivek Kasireddy
Adding this callback provides a way to resume the processing of
cmds in fenceq and cmdq that were not processed because the UI
was waiting on a fence and blocked cmd processing.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 32 ++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 72da5bf500..182e0868b0 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -985,8 +985,10 @@ void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
 break;
 }
 if (!cmd->finished) {
-virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
-VIRTIO_GPU_RESP_OK_NODATA);
+if (!g->parent_obj.renderer_blocked) {
+virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+VIRTIO_GPU_RESP_OK_NODATA);
+}
 }
 }
 
@@ -1042,6 +1044,30 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
 g->processing_cmdq = false;
 }
 
+static void virtio_gpu_process_fenceq(VirtIOGPU *g)
+{
+struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+QTAILQ_FOREACH_SAFE(cmd, >fenceq, next, tmp) {
+trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>fenceq, cmd, next);
+g_free(cmd);
+g->inflight--;
+if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+}
+}
+}
+
+static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
+{
+VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
+
+virtio_gpu_process_fenceq(g);
+virtio_gpu_process_cmdq(g);
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
 VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -1400,10 +1426,12 @@ static void virtio_gpu_class_init(ObjectClass *klass, 
void *data)
 DeviceClass *dc = DEVICE_CLASS(klass);
 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass);
+VirtIOGPUBaseClass *vgbc = >parent;
 
 vgc->handle_ctrl = virtio_gpu_handle_ctrl;
 vgc->process_cmd = virtio_gpu_simple_process_cmd;
 vgc->update_cursor_data = virtio_gpu_update_cursor_data;
+vgbc->gl_flushed = virtio_gpu_handle_gl_flushed;
 
 vdc->realize = virtio_gpu_device_realize;
 vdc->reset = virtio_gpu_reset;
-- 
2.30.2




[PATCH v6 4/5] ui/gtk-egl: Wait for the draw signal for dmabuf blobs

2021-09-14 Thread Vivek Kasireddy
Instead of immediately drawing and submitting, queue and wait
for the draw signal if the dmabuf submitted is a blob.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk-egl.c | 15 +++
 ui/gtk.c |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 43854f3509..7d22affd38 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -182,6 +182,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 3a90aeb2b9..72ce5e1f8f 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -309,6 +309,21 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+GtkWidget *area = vc->gfx.drawing_area;
+
+if (vc->gfx.guest_fb.dmabuf) {
+graphic_hw_gl_block(vc->gfx.dcl.con, true);
+gtk_widget_queue_draw_area(area, x, y, w, h);
+return;
+}
+
+gd_egl_scanout_flush(>gfx.dcl, x, y, w, h);
+}
+
 void gtk_egl_init(DisplayGLMode mode)
 {
 GdkDisplay *gdk_display = gdk_display_get_default();
diff --git a/ui/gtk.c b/ui/gtk.c
index 5105c0a33f..b0564d80c1 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -637,7 +637,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_update   = gd_egl_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
-- 
2.30.2




[PATCH v6 1/5] ui/gtk: Create a common release_dmabuf helper

2021-09-14 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 7835ef1a71..8e98a79ac8 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -181,8 +181,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index cfb0728d1f..784a2f6c74 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v6 0/5] virtio-gpu: Add a default synchronization mechanism for blobs (v6)

2021-09-14 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before letting
the Guest reuse the Blob.

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as some X compositors,
Windows compositors, etc.

The way it works is the Guest submits the resource_flush command and
waits -- for example over a dma fence -- until virtio-gpu sends an ack.
And, the UI will queue a new repaint request and waits until the sync
object associated with the Blit is signaled. Once this is done, the UI
will trigger virtio-gpu to send an ack for the resource_flush cmd.

v2:
- Added more description in the cover letter
- Removed the wait from resource_flush and included it in
  a gl_flushed() callback

v3:
- Instead of explicitly waiting on the sync object and stalling the
  thread, add the relevant fence fd to Qemu's main loop and wait
  for it to be signalled. (suggested by Gerd Hoffmann)

v4:
- Replace the field 'blob' with 'allow_fences' in QemuDmabuf struct.
  (Gerd)

v5: rebase

v6: Fixed the compilation error on platforms that do not have GBM.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 

Vivek Kasireddy (5):
  ui/gtk: Create a common release_dmabuf helper
  ui/egl: Add egl helpers to help with synchronization
  ui: Create sync objects and fences only for blobs
  ui/gtk-egl: Wait for the draw signal for dmabuf blobs
  virtio-gpu: Add gl_flushed callback

 hw/display/virtio-gpu-udmabuf.c |  1 +
 hw/display/virtio-gpu.c | 32 --
 include/ui/console.h|  3 +++
 include/ui/egl-helpers.h|  3 +++
 include/ui/gtk.h|  5 ++--
 ui/egl-helpers.c| 26 ++
 ui/gtk-egl.c| 48 +++--
 ui/gtk-gl-area.c| 26 ++
 ui/gtk.c| 26 --
 9 files changed, 156 insertions(+), 14 deletions(-)

-- 
2.30.2




[PATCH v6 3/5] ui: Create sync objects and fences only for blobs

2021-09-14 Thread Vivek Kasireddy
Create sync objects and fences only for dmabufs that are blobs. Once a
fence is created (after glFlush) and is signalled,
graphic_hw_gl_flushed() will be called and virtio-gpu cmd processing
will be resumed.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  1 +
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 include/ui/gtk.h|  1 +
 ui/gtk-egl.c| 25 +
 ui/gtk-gl-area.c| 26 ++
 ui/gtk.c| 13 +
 7 files changed, 68 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..c6f7f58784 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,7 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.allow_fences = true;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index 45ec129174..244664d727 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  allow_fences;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 2c3ba92b53..2fb6e0dd6b 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 8e98a79ac8..43854f3509 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -155,6 +155,7 @@ extern bool gtk_use_gl_area;
 /* ui/gtk.c */
 void gd_update_windowsize(VirtualConsole *vc);
 int gd_monitor_update_interval(GtkWidget *widget);
+void gd_hw_gl_flushed(void *vc);
 
 /* ui/gtk-egl.c */
 void gd_egl_init(VirtualConsole *vc);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..3a90aeb2b9 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -94,6 +95,18 @@ void gd_egl_draw(VirtualConsole *vc)
 }
 
 glFlush();
+#ifdef CONFIG_GBM
+if (vc->gfx.guest_fb.dmabuf) {
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
+
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+return;
+}
+graphic_hw_gl_block(vc->gfx.dcl.con, false);
+}
+#endif
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
@@ -209,6 +222,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +232,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->allow_fences) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +300,12 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+#ifdef CONFIG_GBM
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+#endif
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..b23523748e 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -8,6 +8,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -71,7 +72,25 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+#ifdef CONFIG_GBM
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+#endif
+
 glFlush();
+#ifdef CONFIG_GBM
+if (vc->gfx.guest_fb.dmabuf) {
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
+
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+return;
+}
+graphic_hw_gl_block(vc->gfx.dcl.co

[RFC v2 1/2] virtio-gpu: Add support for VIRTIO_GPU_F_RELEASE_FENCE

2021-09-13 Thread Vivek Kasireddy
To support this feature, we add a new queue named rel_fenceq to
enqueue finished set_scanout_blob cmds.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 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, _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,
   , res, , >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(>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, >dmabuf.bufs, next, temp) {
+if (dmabuf->buf.fence_fd < 0) {
+QTAILQ_FOREACH_SAFE(cmd, >rel_fenceq, next, tmp) {
+VIRTIO_GPU_FILL_CMD(ss);
+virtio_gpu_bswap_32(, sizeof(ss));
+res = virtio_gpu_find_check_resource(g, ss.resource_id, true,
+ __func__, >error);
+if (res && dmabuf->buf.fd == res->dmabuf_fd) {
+virtio_gpu_ctrl_response_nodata(g, cmd,
+VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>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,
   

[RFC v2 2/2] ui: Add a plain Wayland backend for Qemu UI

2021-09-13 Thread Vivek Kasireddy
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 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 : 'featu

[RFC v2 0/2] ui: Add a Wayland backend for Qemu UI (v2)

2021-09-13 Thread Vivek Kasireddy
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 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Cc: Tina Zhang 
Cc: Satyeshwar Singh 

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




[PATCH v5 5/5] virtio-gpu: Add gl_flushed callback

2021-09-01 Thread Vivek Kasireddy
Adding this callback provides a way to resume the processing of
cmds in fenceq and cmdq that were not processed because the UI
was waiting on a fence and blocked cmd processing.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 32 ++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 72da5bf500..182e0868b0 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -985,8 +985,10 @@ void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
 break;
 }
 if (!cmd->finished) {
-virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
-VIRTIO_GPU_RESP_OK_NODATA);
+if (!g->parent_obj.renderer_blocked) {
+virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+VIRTIO_GPU_RESP_OK_NODATA);
+}
 }
 }
 
@@ -1042,6 +1044,30 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
 g->processing_cmdq = false;
 }
 
+static void virtio_gpu_process_fenceq(VirtIOGPU *g)
+{
+struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+QTAILQ_FOREACH_SAFE(cmd, >fenceq, next, tmp) {
+trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>fenceq, cmd, next);
+g_free(cmd);
+g->inflight--;
+if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+}
+}
+}
+
+static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
+{
+VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
+
+virtio_gpu_process_fenceq(g);
+virtio_gpu_process_cmdq(g);
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
 VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -1400,10 +1426,12 @@ static void virtio_gpu_class_init(ObjectClass *klass, 
void *data)
 DeviceClass *dc = DEVICE_CLASS(klass);
 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass);
+VirtIOGPUBaseClass *vgbc = >parent;
 
 vgc->handle_ctrl = virtio_gpu_handle_ctrl;
 vgc->process_cmd = virtio_gpu_simple_process_cmd;
 vgc->update_cursor_data = virtio_gpu_update_cursor_data;
+vgbc->gl_flushed = virtio_gpu_handle_gl_flushed;
 
 vdc->realize = virtio_gpu_device_realize;
 vdc->reset = virtio_gpu_reset;
-- 
2.30.2




[PATCH v5 4/5] ui/gtk-egl: Wait for the draw signal for dmabuf blobs

2021-09-01 Thread Vivek Kasireddy
Instead of immediately drawing and submitting, queue and wait
for the draw signal if the dmabuf submitted is a blob.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk-egl.c | 15 +++
 ui/gtk.c |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 43854f3509..7d22affd38 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -182,6 +182,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2c68696d9f..737e7b90d4 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -304,6 +304,21 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+GtkWidget *area = vc->gfx.drawing_area;
+
+if (vc->gfx.guest_fb.dmabuf) {
+graphic_hw_gl_block(vc->gfx.dcl.con, true);
+gtk_widget_queue_draw_area(area, x, y, w, h);
+return;
+}
+
+gd_egl_scanout_flush(>gfx.dcl, x, y, w, h);
+}
+
 void gtk_egl_init(DisplayGLMode mode)
 {
 GdkDisplay *gdk_display = gdk_display_get_default();
diff --git a/ui/gtk.c b/ui/gtk.c
index 5105c0a33f..b0564d80c1 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -637,7 +637,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_update   = gd_egl_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
-- 
2.30.2




[PATCH v5 2/5] ui/egl: Add egl helpers to help with synchronization

2021-09-01 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  2 ++
 ui/egl-helpers.c | 26 ++
 3 files changed, 30 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index 3be21497a2..45ec129174 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..2c3ba92b53 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,8 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..d8986b0a7f 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -76,6 +76,32 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
   GL_TEXTURE_2D, fb->texture, 0);
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
 {
 GLuint texture;
-- 
2.30.2




[PATCH v5 3/5] ui: Create sync objects and fences only for blobs

2021-09-01 Thread Vivek Kasireddy
Create sync objects and fences only for dmabufs that are blobs. Once a
fence is created (after glFlush) and is signalled,
graphic_hw_gl_flushed() will be called and virtio-gpu cmd processing
will be resumed.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  1 +
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 include/ui/gtk.h|  1 +
 ui/gtk-egl.c| 20 
 ui/gtk-gl-area.c| 20 
 ui/gtk.c| 13 +
 7 files changed, 57 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..c6f7f58784 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,7 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.allow_fences = true;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index 45ec129174..244664d727 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  allow_fences;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 2c3ba92b53..2fb6e0dd6b 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 8e98a79ac8..43854f3509 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -155,6 +155,7 @@ extern bool gtk_use_gl_area;
 /* ui/gtk.c */
 void gd_update_windowsize(VirtualConsole *vc);
 int gd_monitor_update_interval(GtkWidget *widget);
+void gd_hw_gl_flushed(void *vc);
 
 /* ui/gtk-egl.c */
 void gd_egl_init(VirtualConsole *vc);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..2c68696d9f 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -63,6 +64,7 @@ void gd_egl_draw(VirtualConsole *vc)
 {
 GdkWindow *window;
 int ww, wh;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -94,6 +96,14 @@ void gd_egl_draw(VirtualConsole *vc)
 }
 
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+return;
+}
+graphic_hw_gl_block(vc->gfx.dcl.con, false);
+}
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
@@ -209,6 +219,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +229,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->allow_fences) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +297,10 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..1654941dc9 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -8,6 +8,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -38,6 +39,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, 
bool scanout)
 void gd_gl_area_draw(VirtualConsole *vc)
 {
 int ww, wh, y1, y2;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -71,7 +73,18 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+if (dmabuf) {
+egl_dmabuf_create_sync(dmabuf);
+}
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 

[PATCH v5 1/5] ui/gtk: Create a common release_dmabuf helper

2021-09-01 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 7835ef1a71..8e98a79ac8 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -181,8 +181,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index cfb0728d1f..784a2f6c74 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v5 0/5] virtio-gpu: Add a default synchronization mechanism for blobs

2021-09-01 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before letting
the Guest reuse the Blob.

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as some X compositors,
Windows compositors, etc.

The way it works is the Guest submits the resource_flush command and
waits -- for example over a dma fence -- until virtio-gpu sends an ack.
And, the UI will queue a new repaint request and waits until the sync
object associated with the Blit is signaled. Once this is done, the UI
will trigger virtio-gpu to send an ack for the resource_flush cmd.

v2:
- Added more description in the cover letter
- Removed the wait from resource_flush and included it in
  a gl_flushed() callback

v3:
- Instead of explicitly waiting on the sync object and stalling the
  thread, add the relevant fence fd to Qemu's main loop and wait
  for it to be signalled. (suggested by Gerd Hoffmann)

v4:
- Replace the field 'blob' with 'allow_fences' in QemuDmabuf struct.
  (Gerd)

v5: rebase

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (5):
  ui/gtk: Create a common release_dmabuf helper
  ui/egl: Add egl helpers to help with synchronization
  ui: Create sync objects and fences only for blobs
  ui/gtk-egl: Wait for the draw signal for dmabuf blobs
  virtio-gpu: Add gl_flushed callback

 hw/display/virtio-gpu-udmabuf.c |  1 +
 hw/display/virtio-gpu.c | 32 ++--
 include/ui/console.h|  3 +++
 include/ui/egl-helpers.h|  3 +++
 include/ui/gtk.h|  5 ++--
 ui/egl-helpers.c| 26 
 ui/gtk-egl.c| 43 +++--
 ui/gtk-gl-area.c| 20 +++
 ui/gtk.c| 26 ++--
 9 files changed, 145 insertions(+), 14 deletions(-)

-- 
2.30.2




[PATCH v4 3/5] ui: Create sync objects and fences only for blobs

2021-06-24 Thread Vivek Kasireddy
Create sync objects and fences only for dmabufs that are blobs. Once a
fence is created (after glFlush) and is signalled,
graphic_hw_gl_flushed() will be called and virtio-gpu cmd processing
will be resumed.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  1 +
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 include/ui/gtk.h|  1 +
 ui/gtk-egl.c| 20 
 ui/gtk-gl-area.c| 20 
 ui/gtk.c| 13 +
 7 files changed, 57 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..c6f7f58784 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,7 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.allow_fences = true;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index 49978fdae3..93a0378e1d 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  allow_fences;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 2c3ba92b53..2fb6e0dd6b 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index e6cbf0507c..3e6a48b978 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -152,6 +152,7 @@ extern bool gtk_use_gl_area;
 /* ui/gtk.c */
 void gd_update_windowsize(VirtualConsole *vc);
 int gd_monitor_update_interval(GtkWidget *widget);
+void gd_hw_gl_flushed(void *vc);
 
 /* ui/gtk-egl.c */
 void gd_egl_init(VirtualConsole *vc);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..2c68696d9f 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -63,6 +64,7 @@ void gd_egl_draw(VirtualConsole *vc)
 {
 GdkWindow *window;
 int ww, wh;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -94,6 +96,14 @@ void gd_egl_draw(VirtualConsole *vc)
 }
 
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+return;
+}
+graphic_hw_gl_block(vc->gfx.dcl.con, false);
+}
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
@@ -209,6 +219,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +229,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->allow_fences) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +297,10 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..1654941dc9 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -8,6 +8,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -38,6 +39,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, 
bool scanout)
 void gd_gl_area_draw(VirtualConsole *vc)
 {
 int ww, wh, y1, y2;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -71,7 +73,18 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+if (dmabuf) {
+egl_dmabuf_create_sync(dmabuf);
+}
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 

[PATCH v4 4/5] ui/gtk-egl: Wait for the draw signal for dmabuf blobs

2021-06-24 Thread Vivek Kasireddy
Instead of immediately drawing and submitting, queue and wait
for the draw signal if the dmabuf submitted is a blob.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk-egl.c | 15 +++
 ui/gtk.c |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 3e6a48b978..a25f5bfecc 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -179,6 +179,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2c68696d9f..737e7b90d4 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -304,6 +304,21 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+GtkWidget *area = vc->gfx.drawing_area;
+
+if (vc->gfx.guest_fb.dmabuf) {
+graphic_hw_gl_block(vc->gfx.dcl.con, true);
+gtk_widget_queue_draw_area(area, x, y, w, h);
+return;
+}
+
+gd_egl_scanout_flush(>gfx.dcl, x, y, w, h);
+}
+
 void gtk_egl_init(DisplayGLMode mode)
 {
 GdkDisplay *gdk_display = gdk_display_get_default();
diff --git a/ui/gtk.c b/ui/gtk.c
index ee3a084c21..ce885d2ca3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -637,7 +637,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_update   = gd_egl_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
-- 
2.30.2




[PATCH v4 0/5] virtio-gpu: Add a default synchronization mechanism for blobs

2021-06-24 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before letting
the Guest reuse the Blob.

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as Linux with X
or Windows 10, etc.

The way it works is the Guest includes a fence as part of 
resource_flush and waits for it to be signalled. The Host will
queue a repaint request and signal the fence after it completes
waiting on the sync object associated with the Blit.

v2:
- Added more description in the cover letter
- Removed the wait from resource_flush and included it in
  a gl_flushed() callback

v3:
- Instead of explicitly waiting on the sync object and stalling the
  thread, add the relevant fence fd to Qemu's main loop and wait
  for it to be signalled. (suggested by Gerd Hoffmann)

v4:
- Replace the field 'blob' with 'allow_fences' in QemuDmabuf struct.
  (Gerd)

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (5):
  ui/gtk: Create a common release_dmabuf helper
  ui/egl: Add egl helpers to help with synchronization
  ui: Create sync objects and fences only for blobs
  ui/gtk-egl: Wait for the draw signal for dmabuf blobs
  virtio-gpu: Add gl_flushed callback

 hw/display/virtio-gpu-udmabuf.c |  1 +
 hw/display/virtio-gpu.c | 32 ++--
 include/ui/console.h|  3 +++
 include/ui/egl-helpers.h|  3 +++
 include/ui/gtk.h|  5 ++--
 ui/egl-helpers.c| 26 
 ui/gtk-egl.c| 43 +++--
 ui/gtk-gl-area.c| 20 +++
 ui/gtk.c| 26 ++--
 9 files changed, 145 insertions(+), 14 deletions(-)

-- 
2.30.2




[PATCH v4 2/5] ui/egl: Add egl helpers to help with synchronization

2021-06-24 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  2 ++
 ui/egl-helpers.c | 26 ++
 3 files changed, 30 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index b30b63976a..49978fdae3 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..2c3ba92b53 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,8 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..d8986b0a7f 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -76,6 +76,32 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
   GL_TEXTURE_2D, fb->texture, 0);
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
 {
 GLuint texture;
-- 
2.30.2




[PATCH v4 1/5] ui/gtk: Create a common release_dmabuf helper

2021-06-24 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 9516670ebc..e6cbf0507c 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -178,8 +178,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index 98046f577b..6132bab52f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v4 5/5] virtio-gpu: Add gl_flushed callback

2021-06-24 Thread Vivek Kasireddy
Adding this callback provides a way to resume the processing of
cmds in fenceq and cmdq that were not processed because the UI
was waiting on a fence and blocked cmd processing.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 32 ++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index e183f4ecda..3fcd44ac5e 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -991,8 +991,10 @@ void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
 break;
 }
 if (!cmd->finished) {
-virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
-VIRTIO_GPU_RESP_OK_NODATA);
+if (!g->parent_obj.renderer_blocked) {
+virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+VIRTIO_GPU_RESP_OK_NODATA);
+}
 }
 }
 
@@ -1048,6 +1050,30 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
 g->processing_cmdq = false;
 }
 
+static void virtio_gpu_process_fenceq(VirtIOGPU *g)
+{
+struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+QTAILQ_FOREACH_SAFE(cmd, >fenceq, next, tmp) {
+trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>fenceq, cmd, next);
+g_free(cmd);
+g->inflight--;
+if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+}
+}
+}
+
+static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
+{
+VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
+
+virtio_gpu_process_fenceq(g);
+virtio_gpu_process_cmdq(g);
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
 VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -1406,10 +1432,12 @@ static void virtio_gpu_class_init(ObjectClass *klass, 
void *data)
 DeviceClass *dc = DEVICE_CLASS(klass);
 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass);
+VirtIOGPUBaseClass *vgbc = >parent;
 
 vgc->handle_ctrl = virtio_gpu_handle_ctrl;
 vgc->process_cmd = virtio_gpu_simple_process_cmd;
 vgc->update_cursor_data = virtio_gpu_update_cursor_data;
+vgbc->gl_flushed = virtio_gpu_handle_gl_flushed;
 
 vdc->realize = virtio_gpu_device_realize;
 vdc->reset = virtio_gpu_reset;
-- 
2.30.2




[RFC v1 1/1] ui: Add a plain Wayland backend for Qemu UI

2021-06-23 Thread Vivek Kasireddy
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 configure |  17 ++
 meson.build   |  25 +++
 meson_options.txt |   2 +
 qapi/ui.json  |  19 ++-
 ui/meson.build|  52 ++
 ui/wayland.c  | 402 ++
 6 files changed, 516 insertions(+), 1 deletion(-)
 create mode 100644 ui/wayland.c

diff --git a/configure b/configure
index 8dcb9965b2..f6caaa6329 100755
--- a/configure
+++ b/configure
@@ -403,6 +403,7 @@ cfi_debug="false"
 seccomp="auto"
 glusterfs="auto"
 gtk="auto"
+wayland="auto"
 tls_priority="NORMAL"
 gnutls="$default_feature"
 nettle="$default_feature"
@@ -1372,6 +1373,10 @@ for opt do
   ;;
   --enable-gtk) gtk="enabled"
   ;;
+  --disable-wayland) wayland="disabled"
+  ;;
+  --enable-wayland) wayland="enabled"
+  ;;
   --tls-priority=*) tls_priority="$optarg"
   ;;
   --disable-gnutls) gnutls="no"
@@ -1845,6 +1850,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
@@ -3616,6 +3622,11 @@ if $pkg_config gbm; then
 gbm="yes"
 fi
 
+if $pkg_config wayland-client; then
+wayland_cflags="$($pkg_config --cflags wayland-client)"
+wayland_libs="$($pkg_config --libs wayland-client)"
+fi
+
 if test "$opengl" != "no" ; then
   epoxy=no
   if $pkg_config epoxy; then
@@ -5870,6 +5881,11 @@ if test "$gbm" = "yes" ; then
 echo "GBM_CFLAGS=$gbm_cflags" >> $config_host_mak
 fi
 
+if test "$wayland" = "enabled" ; then
+echo "CONFIG_WAYLAND=y" >> $config_host_mak
+echo "WAYLAND_LIBS=$wayland_libs" >> $config_host_mak
+echo "WAYLAND_CFLAGS=$wayland_cflags" >> $config_host_mak
+fi
 
 if test "$avx2_opt" = "yes" ; then
   echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
@@ -6436,6 +6452,7 @@ if test "$skip_meson" = no; then
 -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 \
+-Dwayland=$wayland \
 -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg 
-Dvnc_png=$vnc_png \
 -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f 
-Dvirtiofsd=$virtiofsd \
 -Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt -Dbrlapi=$brlapi \
diff --git a/meson.build b/meson.build
index 626cf932c1..dbafe4a5d4 100644
--- a/meson.build
+++ b/meson.build
@@ -855,6 +855,29 @@ 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],
+   compile_args: 
config_host['WAYLAND_CFLAGS'].split(),
+   link_args: config_host['WAYLAND_LIBS'].split())
+endif
+
 vnc = not_found
 png = not_found
 jpeg = not_found
@@ -1146,6 +1169,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_LIBATTR', have_old_libattr)
 config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
 config_host_data.set('CONFIG_EBPF', libbpf.found())
@@ -2695,6 +2719,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':  

[RFC v1 0/1] ui: Add a Wayland backend for Qemu UI

2021-06-23 Thread Vivek Kasireddy
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.

The patch(es) are still WIP and the only reason why I am sending them now
is to get feedback and see if anyone thinks this work is interesting. And,
even after this work is complete, it is not meant to be merged and can be
used for performance testing purposes. Given Qemu UI's new direction, the
proper way to add new backends is to create a separate UI/display module
that is part of the dbus/pipewire infrastructure that Marc-Andre is
working on:
https://lists.nongnu.org/archive/html/qemu-devel/2021-03/msg04331.html

Cc: Gerd Hoffmann 
Cc: Marc-André Lureau 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (1):
  ui: Add a plain Wayland backend for Qemu UI

 configure |  17 ++
 meson.build   |  25 +++
 meson_options.txt |   2 +
 qapi/ui.json  |  19 ++-
 ui/meson.build|  52 ++
 ui/wayland.c  | 402 ++
 6 files changed, 516 insertions(+), 1 deletion(-)
 create mode 100644 ui/wayland.c

-- 
2.30.2




[PATCH v3 1/5] ui/gtk: Create a common release_dmabuf helper

2021-06-21 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Reviewed-by: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 9516670ebc..e6cbf0507c 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -178,8 +178,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index 98046f577b..6132bab52f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v3 5/5] virtio-gpu: Add gl_flushed callback

2021-06-21 Thread Vivek Kasireddy
Adding this callback provides a way to resume the processing of
cmds in fenceq and cmdq that were not processed because the UI
was waiting on a fence and blocked cmd processing.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 32 ++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 4d549377cb..ec9d621e64 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -983,8 +983,10 @@ void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
 break;
 }
 if (!cmd->finished) {
-virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
-VIRTIO_GPU_RESP_OK_NODATA);
+if (!g->parent_obj.renderer_blocked) {
+virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+VIRTIO_GPU_RESP_OK_NODATA);
+}
 }
 }
 
@@ -1040,6 +1042,30 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
 g->processing_cmdq = false;
 }
 
+static void virtio_gpu_process_fenceq(VirtIOGPU *g)
+{
+struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+QTAILQ_FOREACH_SAFE(cmd, >fenceq, next, tmp) {
+trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>fenceq, cmd, next);
+g_free(cmd);
+g->inflight--;
+if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+}
+}
+}
+
+static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
+{
+VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
+
+virtio_gpu_process_fenceq(g);
+virtio_gpu_process_cmdq(g);
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
 VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -1398,10 +1424,12 @@ static void virtio_gpu_class_init(ObjectClass *klass, 
void *data)
 DeviceClass *dc = DEVICE_CLASS(klass);
 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass);
+VirtIOGPUBaseClass *vgbc = >parent;
 
 vgc->handle_ctrl = virtio_gpu_handle_ctrl;
 vgc->process_cmd = virtio_gpu_simple_process_cmd;
 vgc->update_cursor_data = virtio_gpu_update_cursor_data;
+vgbc->gl_flushed = virtio_gpu_handle_gl_flushed;
 
 vdc->realize = virtio_gpu_device_realize;
 vdc->reset = virtio_gpu_reset;
-- 
2.30.2




[PATCH v3 4/5] ui/gtk-egl: Wait for the draw signal for dmabuf blobs

2021-06-21 Thread Vivek Kasireddy
Instead of immediately drawing and submitting, queue and wait
for the draw signal if the dmabuf submitted is a blob.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk-egl.c | 15 +++
 ui/gtk.c |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 3e6a48b978..a25f5bfecc 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -179,6 +179,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index f530bcd940..3c7eb244b4 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -304,6 +304,21 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+GtkWidget *area = vc->gfx.drawing_area;
+
+if (vc->gfx.guest_fb.dmabuf) {
+graphic_hw_gl_block(vc->gfx.dcl.con, true);
+gtk_widget_queue_draw_area(area, x, y, w, h);
+return;
+}
+
+gd_egl_scanout_flush(>gfx.dcl, x, y, w, h);
+}
+
 void gtk_egl_init(DisplayGLMode mode)
 {
 GdkDisplay *gdk_display = gdk_display_get_default();
diff --git a/ui/gtk.c b/ui/gtk.c
index ee3a084c21..ce885d2ca3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -637,7 +637,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_update   = gd_egl_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
-- 
2.30.2




[PATCH v3 2/5] ui/egl: Add egl helpers to help with synchronization

2021-06-21 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  2 ++
 ui/egl-helpers.c | 26 ++
 3 files changed, 30 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index b30b63976a..49978fdae3 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..2c3ba92b53 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,8 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..6fa3f2ec45 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -76,6 +76,32 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
   GL_TEXTURE_2D, fb->texture, 0);
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+   EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
 {
 GLuint texture;
-- 
2.30.2




[PATCH v3 3/5] ui: Create sync objects and fences only for blobs

2021-06-21 Thread Vivek Kasireddy
Create sync objects and fences only for dmabufs that are blobs. Once a
fence is created (after glFlush) and is signalled,
graphic_hw_gl_flushed() will be called and virtio-gpu cmd processing
will be resumed.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  1 +
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 include/ui/gtk.h|  1 +
 ui/gtk-egl.c| 20 
 ui/gtk-gl-area.c| 20 
 ui/gtk.c| 13 +
 7 files changed, 57 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..632ba06cbc 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,7 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.blob = true;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index 49978fdae3..570d827644 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  blob;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 2c3ba92b53..2fb6e0dd6b 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index e6cbf0507c..3e6a48b978 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -152,6 +152,7 @@ extern bool gtk_use_gl_area;
 /* ui/gtk.c */
 void gd_update_windowsize(VirtualConsole *vc);
 int gd_monitor_update_interval(GtkWidget *widget);
+void gd_hw_gl_flushed(void *vc);
 
 /* ui/gtk-egl.c */
 void gd_egl_init(VirtualConsole *vc);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..f530bcd940 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -63,6 +64,7 @@ void gd_egl_draw(VirtualConsole *vc)
 {
 GdkWindow *window;
 int ww, wh;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -94,6 +96,14 @@ void gd_egl_draw(VirtualConsole *vc)
 }
 
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+return;
+}
+graphic_hw_gl_block(vc->gfx.dcl.con, false);
+}
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
 
@@ -209,6 +219,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +229,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->blob) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +297,10 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..091194789e 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -8,6 +8,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 
 #include "trace.h"
 
@@ -38,6 +39,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, 
bool scanout)
 void gd_gl_area_draw(VirtualConsole *vc)
 {
 int ww, wh, y1, y2;
+QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 
 if (!vc->gfx.gls) {
 return;
@@ -71,7 +73,18 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+if (dmabuf) {
+egl_dmabuf_create_sync(dmabuf);
+}
 glFlush();
+if (dmabuf) {
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd > 0) {
+qemu_set_fd_handler(d

[PATCH v3 0/5] virtio-gpu: Add a default synchronization mechanism for blobs

2021-06-21 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before letting
the Guest reuse the Blob.

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as Linux with X
or Windows 10, etc.

The way it works is the Guest includes a fence as part of 
resource_flush and waits for it to be signalled. The Host will
queue a repaint request and signal the fence after it completes
waiting on the sync object associated with the Blit.

v2:
- Added more description in the cover letter
- Removed the wait from resource_flush and included it in
  a gl_flushed() callback

v3:
- Instead of explicitly waiting on the sync object and stalling the
  thread, add the relevant fence fd to Qemu's main loop and wait
  for it to be signalled. (suggested by Gerd Hoffmann)

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (5):
  ui/gtk: Create a common release_dmabuf helper
  ui/egl: Add egl helpers to help with synchronization
  ui: Create sync objects and fences only for blobs
  ui/gtk-egl: Wait for the draw signal for dmabuf blobs
  virtio-gpu: Add gl_flushed callback

 hw/display/virtio-gpu-udmabuf.c |  1 +
 hw/display/virtio-gpu.c | 32 ++--
 include/ui/console.h|  3 +++
 include/ui/egl-helpers.h|  3 +++
 include/ui/gtk.h|  5 ++--
 ui/egl-helpers.c| 26 
 ui/gtk-egl.c| 43 +++--
 ui/gtk-gl-area.c| 20 +++
 ui/gtk.c| 26 ++--
 9 files changed, 145 insertions(+), 14 deletions(-)

-- 
2.30.2




[PATCH v2 4/8] ui/gtk: Implement wait_dmabuf function

2021-06-10 Thread Vivek Kasireddy
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 ui/gtk.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/ui/gtk.c b/ui/gtk.c
index 6132bab52f..cd884ca26c 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -583,6 +583,19 @@ static void gd_gl_release_dmabuf(DisplayChangeListener 
*dcl,
 #endif
 }
 
+static void gd_gl_wait_dmabuf(DisplayChangeListener *dcl,
+  QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd <= 0) {
+return;
+}
+
+egl_dmabuf_wait_sync(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -602,6 +615,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
+.dpy_gl_wait_dmabuf  = gd_gl_wait_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -626,6 +640,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
 .dpy_gl_update   = gd_egl_scanout_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
+.dpy_gl_wait_dmabuf  = gd_gl_wait_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v2 2/8] ui/egl: Add egl helpers to help with synchronization

2021-06-10 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  3 +++
 ui/egl-helpers.c | 44 
 3 files changed, 49 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index b30b63976a..49978fdae3 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..5a7575dc13 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,9 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
+void egl_dmabuf_wait_sync(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..47220b66e0 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -76,6 +76,50 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
   GL_TEXTURE_2D, fb->texture, 0);
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+   EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
+void egl_dmabuf_wait_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+EGLint attrib_list[] = {
+EGL_SYNC_NATIVE_FENCE_FD_ANDROID, dmabuf->fence_fd,
+EGL_NONE,
+};
+
+sync = eglCreateSyncKHR(qemu_egl_display,
+EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list);
+if (sync != EGL_NO_SYNC_KHR) {
+eglClientWaitSyncKHR(qemu_egl_display, sync,
+ 0, EGL_FOREVER_KHR);
+eglDestroySyncKHR(qemu_egl_display, sync);
+dmabuf->fence_fd = -1;
+}
+}
+
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
 {
 GLuint texture;
-- 
2.30.2




[PATCH v2 5/8] ui: Create sync objects only for blobs

2021-06-10 Thread Vivek Kasireddy
For now, create sync objects only for dmabufs that are blobs.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  2 ++
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 ui/gtk-egl.c| 10 ++
 ui/gtk-gl-area.c|  8 
 5 files changed, 22 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..33e329e8aa 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,8 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.blob = true;
+dmabuf->buf.sync = NULL;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index a89f739f10..310d34c67a 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  blob;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 5a7575dc13..1bc0e31b03 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..b748f51b0b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -209,6 +209,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +219,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->blob) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +287,10 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..94f3b87c42 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -71,6 +71,10 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 glFlush();
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
@@ -231,6 +235,10 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_gl_area_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->blob) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
-- 
2.30.2




[PATCH v2 8/8] virtio-gpu: Add gl_flushed callback

2021-06-10 Thread Vivek Kasireddy
Adding this callback provides a way to determine when the UI
has submitted the buffer to the Host windowing system. Making
the guest wait for this event will ensure that the dmabuf/buffer
updates are synchronized.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 44 -
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 4d549377cb..bd96332973 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -982,7 +982,7 @@ void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 break;
 }
-if (!cmd->finished) {
+if (!cmd->finished && !(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
 virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
 VIRTIO_GPU_RESP_OK_NODATA);
 }
@@ -1040,6 +1040,46 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
 g->processing_cmdq = false;
 }
 
+static void virtio_gpu_signal_fence(VirtIOGPU *g,
+struct virtio_gpu_ctrl_command *cmd,
+enum virtio_gpu_ctrl_type type)
+{
+struct virtio_gpu_simple_resource *res;
+struct virtio_gpu_resource_flush rf;
+
+VIRTIO_GPU_FILL_CMD(rf);
+virtio_gpu_bswap_32(, sizeof(rf));
+res = virtio_gpu_find_check_resource(g, rf.resource_id, true,
+ __func__, >error);
+if (res) {
+virtio_gpu_resource_wait_sync(g, res);
+}
+virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+}
+
+static void virtio_gpu_process_fenceq(VirtIOGPU *g)
+{
+struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+QTAILQ_FOREACH_SAFE(cmd, >fenceq, next, tmp) {
+trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+virtio_gpu_signal_fence(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+QTAILQ_REMOVE(>fenceq, cmd, next);
+g_free(cmd);
+g->inflight--;
+if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+}
+}
+}
+
+static void virtio_gpu_handle_gl_flushed(VirtIOGPUBase *b)
+{
+VirtIOGPU *g = container_of(b, VirtIOGPU, parent_obj);
+
+virtio_gpu_process_fenceq(g);
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
 VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -1398,10 +1438,12 @@ static void virtio_gpu_class_init(ObjectClass *klass, 
void *data)
 DeviceClass *dc = DEVICE_CLASS(klass);
 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(klass);
+VirtIOGPUBaseClass *vgbc = >parent;
 
 vgc->handle_ctrl = virtio_gpu_handle_ctrl;
 vgc->process_cmd = virtio_gpu_simple_process_cmd;
 vgc->update_cursor_data = virtio_gpu_update_cursor_data;
+vgbc->gl_flushed = virtio_gpu_handle_gl_flushed;
 
 vdc->realize = virtio_gpu_device_realize;
 vdc->reset = virtio_gpu_reset;
-- 
2.30.2




[PATCH v2 1/8] ui/gtk: Create a common release_dmabuf helper

2021-06-10 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 9516670ebc..e6cbf0507c 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -178,8 +178,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index 98046f577b..6132bab52f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v2 6/8] ui/gtk-egl: Wait for the draw signal for dmabuf blobs

2021-06-10 Thread Vivek Kasireddy
Instead of immediately drawing and submitting, queue and wait
for the draw signal if the dmabuf submitted is a blob.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 ++
 ui/gtk-egl.c | 14 ++
 ui/gtk.c |  2 +-
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index e6cbf0507c..34e767a1da 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -178,6 +178,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b748f51b0b..a5655b6bbc 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -294,6 +294,20 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
+void gd_egl_flush(DisplayChangeListener *dcl,
+  uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+GtkWidget *area = vc->gfx.drawing_area;
+
+if (vc->gfx.guest_fb.dmabuf) {
+gtk_widget_queue_draw_area(area, x, y, w, h);
+return;
+}
+
+gd_egl_scanout_flush(>gfx.dcl, x, y, w, h);
+}
+
 void gtk_egl_init(DisplayGLMode mode)
 {
 GdkDisplay *gdk_display = gdk_display_get_default();
diff --git a/ui/gtk.c b/ui/gtk.c
index cd884ca26c..af94f12a98 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -638,7 +638,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_update   = gd_egl_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_gl_wait_dmabuf  = gd_gl_wait_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
-- 
2.30.2




[PATCH v2 3/8] ui: Add a helper to wait on a dmabuf sync object

2021-06-10 Thread Vivek Kasireddy
This will be called by virtio-gpu in the subsequent patches.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  5 +
 ui/console.c | 10 ++
 2 files changed, 15 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index 49978fdae3..a89f739f10 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -242,6 +242,9 @@ typedef struct DisplayChangeListenerOps {
 /* optional */
 void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl,
   QemuDmaBuf *dmabuf);
+/* optional */
+void (*dpy_gl_wait_dmabuf)(DisplayChangeListener *dcl,
+   QemuDmaBuf *dmabuf);
 /* required if GL */
 void (*dpy_gl_update)(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
@@ -314,6 +317,8 @@ void dpy_gl_cursor_position(QemuConsole *con,
 uint32_t pos_x, uint32_t pos_y);
 void dpy_gl_release_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
+void dpy_gl_wait_dmabuf(QemuConsole *con,
+QemuDmaBuf *dmabuf);
 void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 
diff --git a/ui/console.c b/ui/console.c
index 2de5f4105b..b0abfd2246 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1917,6 +1917,16 @@ void dpy_gl_release_dmabuf(QemuConsole *con,
 }
 }
 
+void dpy_gl_wait_dmabuf(QemuConsole *con,
+QemuDmaBuf *dmabuf)
+{
+assert(con->gl);
+
+if (con->gl->ops->dpy_gl_wait_dmabuf) {
+con->gl->ops->dpy_gl_wait_dmabuf(con->gl, dmabuf);
+}
+}
+
 void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
-- 
2.30.2




[PATCH v2 0/8] virtio-gpu: Add a default synchronization mechanism for blobs

2021-06-10 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before letting
the Guest reuse the Blob.

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as Linux with X
or Windows 10, etc.

The way it works is the Guest includes a fence as part of 
resource_flush and waits for it to be signalled. The Host will
queue a repaint request and signal the fence after it completes
waiting on the sync object associated with the Blit.

v2:
- Added more description in the cover letter
- Removed the wait from resource_flush and included it in
  a gl_flushed() callback

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (8):
  ui/gtk: Create a common release_dmabuf helper
  ui/egl: Add egl helpers to help with synchronization
  ui: Add a helper to wait on a dmabuf sync object
  ui/gtk: Implement wait_dmabuf function
  ui: Create sync objects only for blobs
  ui/gtk-egl: Wait for the draw signal for dmabuf blobs
  virtio-gpu: Add dmabuf helpers for synchronization
  virtio-gpu: Add gl_flushed callback

 hw/display/virtio-gpu-udmabuf.c | 30 ++
 hw/display/virtio-gpu.c | 44 -
 include/hw/virtio/virtio-gpu.h  |  2 ++
 include/ui/console.h|  8 ++
 include/ui/egl-helpers.h|  4 +++
 include/ui/gtk.h|  4 +--
 stubs/virtio-gpu-udmabuf.c  |  6 +
 ui/console.c| 10 
 ui/egl-helpers.c| 44 +
 ui/gtk-egl.c| 32 ++--
 ui/gtk-gl-area.c|  8 ++
 ui/gtk.c| 28 +++--
 12 files changed, 207 insertions(+), 13 deletions(-)

-- 
2.30.2




[PATCH v2 7/8] virtio-gpu: Add dmabuf helpers for synchronization

2021-06-10 Thread Vivek Kasireddy
These helpers will be used in the next subsequent patches to
wait until a dmabuf object (via a texture) has been used
by the UI to render and submit its buffer.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c | 28 
 include/hw/virtio/virtio-gpu.h  |  2 ++
 stubs/virtio-gpu-udmabuf.c  |  6 ++
 3 files changed, 36 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 33e329e8aa..8c1b6f8763 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -167,6 +167,34 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, 
VGPUDMABuf *dmabuf)
 g_free(dmabuf);
 }
 
+static VGPUDMABuf
+*virtio_gpu_find_dmabuf(VirtIOGPU *g,
+struct virtio_gpu_simple_resource *res)
+{
+VGPUDMABuf *dmabuf, *tmp;
+
+QTAILQ_FOREACH_SAFE(dmabuf, >dmabuf.bufs, next, tmp) {
+if (dmabuf->buf.fd == res->dmabuf_fd) {
+return dmabuf;
+}
+}
+
+return NULL;
+}
+
+void virtio_gpu_resource_wait_sync(VirtIOGPU *g,
+   struct virtio_gpu_simple_resource *res)
+{
+struct virtio_gpu_scanout *scanout;
+VGPUDMABuf *dmabuf;
+
+dmabuf = virtio_gpu_find_dmabuf(g, res);
+if (dmabuf && dmabuf->buf.sync) {
+scanout = >parent_obj.scanout[dmabuf->scanout_id];
+dpy_gl_wait_dmabuf(scanout->con, >buf);
+}
+}
+
 static VGPUDMABuf
 *virtio_gpu_create_dmabuf(VirtIOGPU *g,
   uint32_t scanout_id,
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index bcf54d970f..9b9b499d06 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -274,6 +274,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_resource_wait_sync(VirtIOGPU *g,
+   struct virtio_gpu_simple_resource *res);
 
 /* virtio-gpu-3d.c */
 void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
diff --git a/stubs/virtio-gpu-udmabuf.c b/stubs/virtio-gpu-udmabuf.c
index 81f661441a..59dab1a66c 100644
--- a/stubs/virtio-gpu-udmabuf.c
+++ b/stubs/virtio-gpu-udmabuf.c
@@ -25,3 +25,9 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
 /* nothing (stub) */
 return 0;
 }
+
+void virtio_gpu_resource_wait_sync(VirtIOGPU *g,
+   struct virtio_gpu_simple_resource *res)
+{
+/* nothing (stub) */
+}
-- 
2.30.2




[PATCH v1 1/5] ui/gtk: Create a common release_dmabuf helper

2021-06-07 Thread Vivek Kasireddy
Since the texture release mechanism is same for both gtk-egl
and gtk-glarea, move the helper from gtk-egl to common gtk
code so that it can be shared by both gtk backends.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/gtk.h |  2 --
 ui/gtk-egl.c |  8 
 ui/gtk.c | 11 ++-
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 9516670ebc..e6cbf0507c 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -178,8 +178,6 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
   uint32_t hot_x, uint32_t hot_y);
 void gd_egl_cursor_position(DisplayChangeListener *dcl,
 uint32_t pos_x, uint32_t pos_y);
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf);
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 void gtk_egl_init(DisplayGLMode mode);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 2a2e6d3a17..b671181272 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -249,14 +249,6 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl,
 vc->gfx.cursor_y = pos_y * vc->gfx.scale_y;
 }
 
-void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
-   QemuDmaBuf *dmabuf)
-{
-#ifdef CONFIG_GBM
-egl_dmabuf_release_texture(dmabuf);
-#endif
-}
-
 void gd_egl_scanout_flush(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
diff --git a/ui/gtk.c b/ui/gtk.c
index 98046f577b..6132bab52f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -575,6 +575,14 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
 return vc->gfx.has_dmabuf;
 }
 
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_release_texture(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -593,6 +601,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -615,8 +624,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
 .dpy_gl_cursor_dmabuf= gd_egl_cursor_dmabuf,
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
-.dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
 .dpy_gl_update   = gd_egl_scanout_flush,
+.dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v1 3/5] ui/egl: Add egl helpers to help with synchronization

2021-06-07 Thread Vivek Kasireddy
These egl helpers would be used for creating and waiting on
a sync object.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  2 ++
 include/ui/egl-helpers.h |  3 +++
 ui/egl-helpers.c | 44 
 ui/gtk.c | 15 ++
 4 files changed, 64 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index c3dca61c31..a89f739f10 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -168,6 +168,8 @@ typedef struct QemuDmaBuf {
 uint64_t  modifier;
 uint32_t  texture;
 bool  y0_top;
+void  *sync;
+int   fence_fd;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index f1bf8f97fc..5a7575dc13 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -45,6 +45,9 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, 
EGLint *fourcc,
 
 void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
 void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf);
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
+void egl_dmabuf_wait_sync(QemuDmaBuf *dmabuf);
 
 #endif
 
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 6d0cb2b5cb..47220b66e0 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -76,6 +76,50 @@ void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
   GL_TEXTURE_2D, fb->texture, 0);
 }
 
+void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+
+if (epoxy_has_egl_extension(qemu_egl_display,
+"EGL_KHR_fence_sync") &&
+epoxy_has_egl_extension(qemu_egl_display,
+"EGL_ANDROID_native_fence_sync")) {
+sync = eglCreateSyncKHR(qemu_egl_display,
+   EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+if (sync != EGL_NO_SYNC_KHR) {
+dmabuf->sync = sync;
+}
+}
+}
+
+void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
+{
+if (dmabuf->sync) {
+dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+  dmabuf->sync);
+eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
+dmabuf->sync = NULL;
+}
+}
+
+void egl_dmabuf_wait_sync(QemuDmaBuf *dmabuf)
+{
+EGLSyncKHR sync;
+EGLint attrib_list[] = {
+EGL_SYNC_NATIVE_FENCE_FD_ANDROID, dmabuf->fence_fd,
+EGL_NONE,
+};
+
+sync = eglCreateSyncKHR(qemu_egl_display,
+EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list);
+if (sync != EGL_NO_SYNC_KHR) {
+eglClientWaitSyncKHR(qemu_egl_display, sync,
+ 0, EGL_FOREVER_KHR);
+eglDestroySyncKHR(qemu_egl_display, sync);
+dmabuf->fence_fd = -1;
+}
+}
+
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height)
 {
 GLuint texture;
diff --git a/ui/gtk.c b/ui/gtk.c
index 6132bab52f..cd884ca26c 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -583,6 +583,19 @@ static void gd_gl_release_dmabuf(DisplayChangeListener 
*dcl,
 #endif
 }
 
+static void gd_gl_wait_dmabuf(DisplayChangeListener *dcl,
+  QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+egl_dmabuf_create_fence(dmabuf);
+if (dmabuf->fence_fd <= 0) {
+return;
+}
+
+egl_dmabuf_wait_sync(dmabuf);
+#endif
+}
+
 /** DisplayState Callbacks (opengl version) **/
 
 static const DisplayChangeListenerOps dcl_gl_area_ops = {
@@ -602,6 +615,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
 .dpy_gl_update   = gd_gl_area_scanout_flush,
 .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
+.dpy_gl_wait_dmabuf  = gd_gl_wait_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
@@ -626,6 +640,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
 .dpy_gl_cursor_position  = gd_egl_cursor_position,
 .dpy_gl_update   = gd_egl_scanout_flush,
 .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
+.dpy_gl_wait_dmabuf  = gd_gl_wait_dmabuf,
 .dpy_has_dmabuf  = gd_has_dmabuf,
 };
 
-- 
2.30.2




[PATCH v1 2/5] ui: Add a helper to wait on a dmabuf sync object

2021-06-07 Thread Vivek Kasireddy
This will be called by virtio-gpu in the subsequent patches.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  5 +
 ui/console.c | 10 ++
 2 files changed, 15 insertions(+)

diff --git a/include/ui/console.h b/include/ui/console.h
index b30b63976a..c3dca61c31 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -240,6 +240,9 @@ typedef struct DisplayChangeListenerOps {
 /* optional */
 void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl,
   QemuDmaBuf *dmabuf);
+/* optional */
+void (*dpy_gl_wait_dmabuf)(DisplayChangeListener *dcl,
+   QemuDmaBuf *dmabuf);
 /* required if GL */
 void (*dpy_gl_update)(DisplayChangeListener *dcl,
   uint32_t x, uint32_t y, uint32_t w, uint32_t h);
@@ -312,6 +315,8 @@ void dpy_gl_cursor_position(QemuConsole *con,
 uint32_t pos_x, uint32_t pos_y);
 void dpy_gl_release_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
+void dpy_gl_wait_dmabuf(QemuConsole *con,
+QemuDmaBuf *dmabuf);
 void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 
diff --git a/ui/console.c b/ui/console.c
index 2de5f4105b..b0abfd2246 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1917,6 +1917,16 @@ void dpy_gl_release_dmabuf(QemuConsole *con,
 }
 }
 
+void dpy_gl_wait_dmabuf(QemuConsole *con,
+QemuDmaBuf *dmabuf)
+{
+assert(con->gl);
+
+if (con->gl->ops->dpy_gl_wait_dmabuf) {
+con->gl->ops->dpy_gl_wait_dmabuf(con->gl, dmabuf);
+}
+}
+
 void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
-- 
2.30.2




[PATCH v1 4/5] ui: Create sync objects only for blobs

2021-06-07 Thread Vivek Kasireddy
For now, create sync objects only for dmabufs that are blobs.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c |  2 ++
 include/ui/console.h|  1 +
 include/ui/egl-helpers.h|  1 +
 ui/gtk-egl.c| 10 ++
 ui/gtk-gl-area.c|  8 
 5 files changed, 22 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 3c01a415e7..33e329e8aa 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -185,6 +185,8 @@ static VGPUDMABuf
 dmabuf->buf.stride = fb->stride;
 dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 dmabuf->buf.fd = res->dmabuf_fd;
+dmabuf->buf.blob = true;
+dmabuf->buf.sync = NULL;
 
 dmabuf->scanout_id = scanout_id;
 QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
diff --git a/include/ui/console.h b/include/ui/console.h
index a89f739f10..310d34c67a 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -170,6 +170,7 @@ typedef struct QemuDmaBuf {
 bool  y0_top;
 void  *sync;
 int   fence_fd;
+bool  blob;
 } QemuDmaBuf;
 
 typedef struct DisplayState DisplayState;
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index 5a7575dc13..1bc0e31b03 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -19,6 +19,7 @@ typedef struct egl_fb {
 GLuint texture;
 GLuint framebuffer;
 bool delete_texture;
+QemuDmaBuf *dmabuf;
 } egl_fb;
 
 void egl_fb_destroy(egl_fb *fb);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index b671181272..b748f51b0b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -209,6 +209,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
 {
 #ifdef CONFIG_GBM
+VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
 egl_dmabuf_import_texture(dmabuf);
 if (!dmabuf->texture) {
 return;
@@ -217,6 +219,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_egl_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->blob) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
@@ -281,6 +287,10 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 egl_fb_blit(>gfx.win_fb, >gfx.guest_fb, !vc->gfx.y0_top);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 }
 
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index dd5783fec7..94f3b87c42 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -71,6 +71,10 @@ void gd_gl_area_draw(VirtualConsole *vc)
 surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 }
 
+if (vc->gfx.guest_fb.dmabuf) {
+egl_dmabuf_create_sync(vc->gfx.guest_fb.dmabuf);
+}
+
 glFlush();
 graphic_hw_gl_flushed(vc->gfx.dcl.con);
 }
@@ -231,6 +235,10 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
 gd_gl_area_scanout_texture(dcl, dmabuf->texture,
false, dmabuf->width, dmabuf->height,
0, 0, dmabuf->width, dmabuf->height);
+
+if (dmabuf->blob) {
+vc->gfx.guest_fb.dmabuf = dmabuf;
+}
 #endif
 }
 
-- 
2.30.2




[PATCH v1 5/5] virtio-gpu: Make resource_flush wait on the sync object for blobs

2021-06-07 Thread Vivek Kasireddy
To make sure that the Guest would not use the backing storage
associated with a blob resource before or at the same time when
the Host does a blit with it, resource_flush needs to wait on
the sync object associated with the blob. Doing this would prevent
tearing/flickering or other issues when using blob resources.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
Signed-off-by: Dongwon Kim 
---
 hw/display/virtio-gpu-udmabuf.c | 28 
 hw/display/virtio-gpu.c |  1 +
 include/hw/virtio/virtio-gpu.h  |  2 ++
 3 files changed, 31 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 33e329e8aa..8c1b6f8763 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -167,6 +167,34 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, 
VGPUDMABuf *dmabuf)
 g_free(dmabuf);
 }
 
+static VGPUDMABuf
+*virtio_gpu_find_dmabuf(VirtIOGPU *g,
+struct virtio_gpu_simple_resource *res)
+{
+VGPUDMABuf *dmabuf, *tmp;
+
+QTAILQ_FOREACH_SAFE(dmabuf, >dmabuf.bufs, next, tmp) {
+if (dmabuf->buf.fd == res->dmabuf_fd) {
+return dmabuf;
+}
+}
+
+return NULL;
+}
+
+void virtio_gpu_resource_wait_sync(VirtIOGPU *g,
+   struct virtio_gpu_simple_resource *res)
+{
+struct virtio_gpu_scanout *scanout;
+VGPUDMABuf *dmabuf;
+
+dmabuf = virtio_gpu_find_dmabuf(g, res);
+if (dmabuf && dmabuf->buf.sync) {
+scanout = >parent_obj.scanout[dmabuf->scanout_id];
+dpy_gl_wait_dmabuf(scanout->con, >buf);
+}
+}
+
 static VGPUDMABuf
 *virtio_gpu_create_dmabuf(VirtIOGPU *g,
   uint32_t scanout_id,
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 4d549377cb..dd037137e9 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -523,6 +523,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 console_has_gl(scanout->con)) {
 dpy_gl_update(scanout->con, 0, 0, scanout->width,
   scanout->height);
+virtio_gpu_resource_wait_sync(g, res);
 return;
 }
 }
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index bcf54d970f..9b9b499d06 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -274,6 +274,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_resource_wait_sync(VirtIOGPU *g,
+   struct virtio_gpu_simple_resource *res);
 
 /* virtio-gpu-3d.c */
 void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
-- 
2.30.2




[PATCH v1 0/5] virtio-gpu: Add implicit (and default) sync mechanism for blobs

2021-06-07 Thread Vivek Kasireddy
When the Guest and Host are using Blob resources, there is a chance
that they may use the underlying storage associated with a Blob at
the same time leading to glitches such as flickering or tearing.
To prevent these from happening, the Host needs to ensure that it
waits until its Blit is completed by the Host GPU before returning
control back to the Guest from resource_flush().

This should be the default behavior regardless of the type of Guest
that is using Blob resources but would be particularly useful for 
Guests that are using frontbuffer rendering such as Linux with X
or Windows 10, etc.

Cc: Gerd Hoffmann 
Cc: Dongwon Kim 
Cc: Tina Zhang 

Vivek Kasireddy (5):
  ui/gtk: Create a common release_dmabuf helper
  ui: Add a helper to wait on a dmabuf sync object
  ui/egl: Add egl helpers to help with synchronization
  ui: Create sync objects only for blobs
  virtio-gpu: Make resource_flush wait on the sync object for blobs

 hw/display/virtio-gpu-udmabuf.c | 30 ++
 hw/display/virtio-gpu.c |  1 +
 include/hw/virtio/virtio-gpu.h  |  2 ++
 include/ui/console.h|  8 ++
 include/ui/egl-helpers.h|  4 +++
 include/ui/gtk.h|  2 --
 ui/console.c| 10 
 ui/egl-helpers.c| 44 +
 ui/gtk-egl.c| 18 --
 ui/gtk-gl-area.c|  8 ++
 ui/gtk.c| 26 ++-
 11 files changed, 142 insertions(+), 11 deletions(-)

-- 
2.30.2




[PATCH v7 09/14] virtio-gpu: Add virtio_gpu_resource_create_blob

2021-05-26 Thread Vivek Kasireddy
This API allows Qemu to register the blob allocated by the Guest
as a new resource and map its backing storage.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/trace-events  |  1 +
 hw/display/virtio-gpu.c  | 73 ++--
 include/hw/virtio/virtio-gpu-bswap.h |  9 
 3 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/hw/display/trace-events b/hw/display/trace-events
index 9fccca18a1..f3f77b6984 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -32,6 +32,7 @@ virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d"
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, 
uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
+virtio_gpu_cmd_res_create_blob(uint32_t res, uint64_t size) "res 0x%x, size %" 
PRId64
 virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x"
 virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x"
 virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x"
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index f77a7fc7dd..788b4540d5 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -137,7 +137,7 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t 
resource_id,
 }
 
 if (require_backing) {
-if (!res->iov || !res->image) {
+if (!res->iov || (!res->image && !res->blob)) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: no backing storage %d\n",
   caller, resource_id);
 if (error) {
@@ -313,6 +313,62 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
 g->hostmem += res->hostmem;
 }
 
+static void virtio_gpu_resource_create_blob(VirtIOGPU *g,
+struct virtio_gpu_ctrl_command 
*cmd)
+{
+struct virtio_gpu_simple_resource *res;
+struct virtio_gpu_resource_create_blob cblob;
+int ret;
+
+VIRTIO_GPU_FILL_CMD(cblob);
+virtio_gpu_create_blob_bswap();
+trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size);
+
+if (cblob.resource_id == 0) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+  __func__);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+return;
+}
+
+res = virtio_gpu_find_resource(g, cblob.resource_id);
+if (res) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+  __func__, cblob.resource_id);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+return;
+}
+
+res = g_new0(struct virtio_gpu_simple_resource, 1);
+res->resource_id = cblob.resource_id;
+res->blob_size = cblob.size;
+
+if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_GUEST &&
+cblob.blob_flags != VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid memory type\n",
+  __func__);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+g_free(res);
+return;
+}
+
+if (res->iov) {
+cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+return;
+}
+
+ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob),
+cmd, >addrs, >iov,
+>iov_cnt);
+if (ret != 0) {
+cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+return;
+}
+
+virtio_gpu_init_udmabuf(res);
+QTAILQ_INSERT_HEAD(>reslist, res, next);
+}
+
 static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
 {
 struct virtio_gpu_scanout *scanout = >parent_obj.scanout[scanout_id];
@@ -390,7 +446,7 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, t2d.resource_id, true,
  __func__, >error);
-if (!res) {
+if (!res || res->blob) {
 return;
 }
 
@@ -446,7 +502,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, rf.resource_id, false,
  __func__, >error);
-if (!res) {
+if (!res || res->blob) {
 return;
 }
 
@@ -715,6 +771,10 @@ static void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
 res->iov_cnt = 0;
 g_free(res->addrs);
 res->addrs = NULL;
+
+if (res->blob) {
+virtio_gpu_fini_udmabuf(res);
+}
 }
 
 static void
@@ -

[PATCH v7 07/14] virtio-gpu: Refactor virtio_gpu_create_mapping_iov

2021-05-26 Thread Vivek Kasireddy
Instead of passing the attach_backing object to extract nr_entries
and offset, explicitly pass these as arguments to this function.
This will be helpful when adding create_blob API.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-virgl.c  |  3 ++-
 hw/display/virtio-gpu.c| 19 +--
 include/hw/virtio/virtio-gpu.h |  2 +-
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 72c14d9132..092c6dc380 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -289,7 +289,8 @@ static void virgl_resource_attach_backing(VirtIOGPU *g,
 VIRTIO_GPU_FILL_CMD(att_rb);
 trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
 
-ret = virtio_gpu_create_mapping_iov(g, _rb, cmd, NULL, _iovs, 
_niov);
+ret = virtio_gpu_create_mapping_iov(g, att_rb.nr_entries, sizeof(att_rb),
+cmd, NULL, _iovs, _niov);
 if (ret != 0) {
 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 return;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index fdcedfc61e..7a0db3a860 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -615,7 +615,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
 }
 
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
-  struct virtio_gpu_resource_attach_backing 
*ab,
+  uint32_t nr_entries, uint32_t offset,
   struct virtio_gpu_ctrl_command *cmd,
   uint64_t **addr, struct iovec **iov,
   uint32_t *niov)
@@ -624,17 +624,17 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
 size_t esize, s;
 int e, v;
 
-if (ab->nr_entries > 16384) {
+if (nr_entries > 16384) {
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: nr_entries is too big (%d > 16384)\n",
-  __func__, ab->nr_entries);
+  __func__, nr_entries);
 return -1;
 }
 
-esize = sizeof(*ents) * ab->nr_entries;
+esize = sizeof(*ents) * nr_entries;
 ents = g_malloc(esize);
 s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
-   sizeof(*ab), ents, esize);
+   offset, ents, esize);
 if (s != esize) {
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: command data size incorrect %zu vs %zu\n",
@@ -647,7 +647,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
 if (addr) {
 *addr = NULL;
 }
-for (e = 0, v = 0; e < ab->nr_entries; e++) {
+for (e = 0, v = 0; e < nr_entries; e++) {
 uint64_t a = le64_to_cpu(ents[e].addr);
 uint32_t l = le32_to_cpu(ents[e].length);
 hwaddr len;
@@ -659,8 +659,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
  a, , DMA_DIRECTION_TO_DEVICE);
 if (!map) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory 
for"
-  " resource %d element %d\n",
-  __func__, ab->resource_id, e);
+  " element %d\n", __func__, e);
 virtio_gpu_cleanup_mapping_iov(g, *iov, v);
 g_free(ents);
 *iov = NULL;
@@ -743,8 +742,8 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
 return;
 }
 
-ret = virtio_gpu_create_mapping_iov(g, , cmd, >addrs,
->iov, >iov_cnt);
+ret = virtio_gpu_create_mapping_iov(g, ab.nr_entries, sizeof(ab), cmd,
+>addrs, >iov, >iov_cnt);
 if (ret != 0) {
 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 return;
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index b83a91a67f..dad9a1d221 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -238,7 +238,7 @@ void virtio_gpu_get_display_info(VirtIOGPU *g,
 void virtio_gpu_get_edid(VirtIOGPU *g,
  struct virtio_gpu_ctrl_command *cmd);
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
-  struct virtio_gpu_resource_attach_backing 
*ab,
+  uint32_t nr_entries, uint32_t offset,
   struct virtio_gpu_ctrl_command *cmd,
   uint64_t **addr, struct iovec **iov,
   uint32_t *niov);
-- 
2.30.2




[PATCH v7 08/14] virtio-gpu: Add initial definitions for blob resources

2021-05-26 Thread Vivek Kasireddy
Add the property bit, configuration flag and other relevant
macros and definitions associated with this feature.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-base.c   |  3 +++
 hw/display/virtio-gpu.c| 14 ++
 include/hw/virtio/virtio-gpu.h |  3 +++
 3 files changed, 20 insertions(+)

diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index afb3ee7d9a..dd294276cb 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -208,6 +208,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t 
features,
 if (virtio_gpu_edid_enabled(g->conf)) {
 features |= (1 << VIRTIO_GPU_F_EDID);
 }
+if (virtio_gpu_blob_enabled(g->conf)) {
+features |= (1 << VIRTIO_GPU_F_RESOURCE_BLOB);
+}
 
 return features;
 }
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 7a0db3a860..f77a7fc7dd 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1108,6 +1108,18 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error 
**errp)
 VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
 VirtIOGPU *g = VIRTIO_GPU(qdev);
 
+if (virtio_gpu_blob_enabled(g->parent_obj.conf)) {
+if (!virtio_gpu_have_udmabuf()) {
+error_setg(errp, "cannot enable blob resources without udmabuf");
+return;
+}
+
+if (virtio_gpu_virgl_enabled(g->parent_obj.conf)) {
+error_setg(errp, "blobs and virgl are not compatible (yet)");
+return;
+}
+}
+
 if (!virtio_gpu_base_device_realize(qdev,
 virtio_gpu_handle_ctrl_cb,
 virtio_gpu_handle_cursor_cb,
@@ -1201,6 +1213,8 @@ static Property virtio_gpu_properties[] = {
 VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf),
 DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem,
  256 * MiB),
+DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
+VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index dad9a1d221..66e7aaad0e 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -89,6 +89,7 @@ enum virtio_gpu_base_conf_flags {
 VIRTIO_GPU_FLAG_STATS_ENABLED,
 VIRTIO_GPU_FLAG_EDID_ENABLED,
 VIRTIO_GPU_FLAG_DMABUF_ENABLED,
+VIRTIO_GPU_FLAG_BLOB_ENABLED,
 };
 
 #define virtio_gpu_virgl_enabled(_cfg) \
@@ -99,6 +100,8 @@ enum virtio_gpu_base_conf_flags {
 (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED))
 #define virtio_gpu_dmabuf_enabled(_cfg) \
 (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED))
+#define virtio_gpu_blob_enabled(_cfg) \
+(_cfg.flags & (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED))
 
 struct virtio_gpu_base_conf {
 uint32_t max_outputs;
-- 
2.30.2




[PATCH v7 01/14] ui: Get the fd associated with udmabuf driver

2021-05-26 Thread Vivek Kasireddy
Try to open the udmabuf dev node for the first time or return the
fd if the device was previously opened.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/console.h |  3 +++
 ui/meson.build   |  1 +
 ui/udmabuf.c | 40 
 3 files changed, 44 insertions(+)
 create mode 100644 ui/udmabuf.c

diff --git a/include/ui/console.h b/include/ui/console.h
index ca3c7af6a6..b30b63976a 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -471,4 +471,7 @@ bool vnc_display_reload_certs(const char *id,  Error 
**errp);
 /* input.c */
 int index_from_key(const char *key, size_t key_length);
 
+/* udmabuf.c */
+int udmabuf_fd(void);
+
 #endif
diff --git a/ui/meson.build b/ui/meson.build
index b5aed14886..a3a187d633 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -12,6 +12,7 @@ softmmu_ss.add(files(
   'kbd-state.c',
   'keymaps.c',
   'qemu-pixman.c',
+  'udmabuf.c',
 ))
 softmmu_ss.add([spice_headers, files('spice-module.c')])
 softmmu_ss.add(when: spice_protocol, if_true: files('vdagent.c'))
diff --git a/ui/udmabuf.c b/ui/udmabuf.c
new file mode 100644
index 00..e6234fd86f
--- /dev/null
+++ b/ui/udmabuf.c
@@ -0,0 +1,40 @@
+/*
+ * udmabuf helper functions.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "ui/console.h"
+
+#ifdef CONFIG_LINUX
+
+#include 
+#include 
+
+int udmabuf_fd(void)
+{
+static bool first = true;
+static int udmabuf;
+
+if (!first) {
+return udmabuf;
+}
+first = false;
+
+udmabuf = open("/dev/udmabuf", O_RDWR);
+if (udmabuf < 0) {
+warn_report("open /dev/udmabuf: %s", strerror(errno));
+}
+return udmabuf;
+}
+
+#else
+
+int udmabuf_fd(void)
+{
+return -1;
+}
+
+#endif
-- 
2.30.2




[PATCH v7 05/14] virtio-gpu: Add virtio_gpu_find_check_resource

2021-05-26 Thread Vivek Kasireddy
Move finding the resource and validating its backing storage into one
function.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 66 +
 1 file changed, 47 insertions(+), 19 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index db56f0454a..7b5296f0d0 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -35,6 +35,10 @@
 
 static struct virtio_gpu_simple_resource*
 virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+static struct virtio_gpu_simple_resource *
+virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id,
+   bool require_backing,
+   const char *caller, uint32_t *error);
 
 static void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
struct virtio_gpu_simple_resource *res);
@@ -46,7 +50,8 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 struct virtio_gpu_simple_resource *res;
 uint32_t pixels;
 
-res = virtio_gpu_find_resource(g, resource_id);
+res = virtio_gpu_find_check_resource(g, resource_id, false,
+ __func__, NULL);
 if (!res) {
 return;
 }
@@ -114,6 +119,37 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t 
resource_id)
 return NULL;
 }
 
+static struct virtio_gpu_simple_resource *
+virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id,
+   bool require_backing,
+   const char *caller, uint32_t *error)
+{
+struct virtio_gpu_simple_resource *res;
+
+res = virtio_gpu_find_resource(g, resource_id);
+if (!res) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid resource specified %d\n",
+  caller, resource_id);
+if (error) {
+*error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+}
+return NULL;
+}
+
+if (require_backing) {
+if (!res->iov || !res->image) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: no backing storage %d\n",
+  caller, resource_id);
+if (error) {
+*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+}
+return NULL;
+}
+}
+
+return res;
+}
+
 void virtio_gpu_ctrl_response(VirtIOGPU *g,
   struct virtio_gpu_ctrl_command *cmd,
   struct virtio_gpu_ctrl_hdr *resp,
@@ -352,11 +388,9 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
 virtio_gpu_t2d_bswap();
 trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
 
-res = virtio_gpu_find_resource(g, t2d.resource_id);
-if (!res || !res->iov) {
-qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
-  __func__, t2d.resource_id);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+res = virtio_gpu_find_check_resource(g, t2d.resource_id, true,
+ __func__, >error);
+if (!res) {
 return;
 }
 
@@ -410,11 +444,9 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 trace_virtio_gpu_cmd_res_flush(rf.resource_id,
rf.r.width, rf.r.height, rf.r.x, rf.r.y);
 
-res = virtio_gpu_find_resource(g, rf.resource_id);
+res = virtio_gpu_find_check_resource(g, rf.resource_id, false,
+ __func__, >error);
 if (!res) {
-qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
-  __func__, rf.resource_id);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
 return;
 }
 
@@ -497,11 +529,9 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
 }
 
 /* create a surface for this scanout */
-res = virtio_gpu_find_resource(g, ss.resource_id);
+res = virtio_gpu_find_check_resource(g, ss.resource_id, true,
+ __func__, >error);
 if (!res) {
-qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
-  __func__, ss.resource_id);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
 return;
 }
 
@@ -709,11 +739,9 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g,
 virtio_gpu_bswap_32(, sizeof(detach));
 trace_virtio_gpu_cmd_res_back_detach(detach.resource_id);
 
-res = virtio_gpu_find_resource(g, detach.resource_id);
-if (!res || !res->iov) {
-qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
-  __func__, detach.resource_id);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+res = virtio_gpu_find_check_resource(g, detach.resource_id, true,

[PATCH v7 06/14] virtio-gpu: Refactor virtio_gpu_set_scanout

2021-05-26 Thread Vivek Kasireddy
Store the meta-data associated with a FB in a new object
(struct virtio_gpu_framebuffer) and pass the object to set_scanout.
Also move code in set_scanout into a do_set_scanout function.
This will be helpful when adding set_scanout_blob API.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c| 151 +++--
 include/hw/virtio/virtio-gpu.h |   8 ++
 2 files changed, 95 insertions(+), 64 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 7b5296f0d0..fdcedfc61e 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -500,95 +500,118 @@ static void virtio_unref_resource(pixman_image_t *image, 
void *data)
 pixman_image_unref(data);
 }
 
-static void virtio_gpu_set_scanout(VirtIOGPU *g,
-   struct virtio_gpu_ctrl_command *cmd)
+static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
+  uint32_t scanout_id,
+  struct virtio_gpu_framebuffer *fb,
+  struct virtio_gpu_simple_resource *res,
+  struct virtio_gpu_rect *r,
+  uint32_t *error)
 {
-struct virtio_gpu_simple_resource *res, *ores;
+struct virtio_gpu_simple_resource *ores;
 struct virtio_gpu_scanout *scanout;
-pixman_format_code_t format;
-uint32_t offset;
-int bpp;
-struct virtio_gpu_set_scanout ss;
+uint8_t *data;
 
-VIRTIO_GPU_FILL_CMD(ss);
-virtio_gpu_bswap_32(, sizeof(ss));
-trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
- ss.r.width, ss.r.height, ss.r.x, ss.r.y);
-
-if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
+if (scanout_id >= g->parent_obj.conf.max_outputs) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
-  __func__, ss.scanout_id);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+  __func__, scanout_id);
+*error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
 return;
 }
-
-g->parent_obj.enable = 1;
-if (ss.resource_id == 0) {
-virtio_gpu_disable_scanout(g, ss.scanout_id);
-return;
-}
-
-/* create a surface for this scanout */
-res = virtio_gpu_find_check_resource(g, ss.resource_id, true,
- __func__, >error);
-if (!res) {
-return;
-}
-
-if (ss.r.x > res->width ||
-ss.r.y > res->height ||
-ss.r.width < 16 ||
-ss.r.height < 16 ||
-ss.r.width > res->width ||
-ss.r.height > res->height ||
-ss.r.x + ss.r.width > res->width ||
-ss.r.y + ss.r.height > res->height) {
+scanout = >parent_obj.scanout[scanout_id];
+
+if (r->x > fb->width ||
+r->y > fb->height ||
+r->width < 16 ||
+r->height < 16 ||
+r->width > fb->width ||
+r->height > fb->height ||
+r->x + r->width > fb->width ||
+r->y + r->height > fb->height) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
-  " resource %d, (%d,%d)+%d,%d vs %d %d\n",
-  __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y,
-  ss.r.width, ss.r.height, res->width, res->height);
-cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+  " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n",
+  __func__, scanout_id, res->resource_id,
+  r->x, r->y, r->width, r->height,
+  fb->width, fb->height);
+*error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
 return;
 }
 
-scanout = >parent_obj.scanout[ss.scanout_id];
+g->parent_obj.enable = 1;
+data = (uint8_t *)pixman_image_get_data(res->image);
 
-format = pixman_image_get_format(res->image);
-bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
-offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image);
-if (!scanout->ds || surface_data(scanout->ds)
-!= ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
-scanout->width != ss.r.width ||
-scanout->height != ss.r.height) {
+/* create a surface for this scanout */
+if (!scanout->ds ||
+surface_data(scanout->ds) != data + fb->offset ||
+scanout->width != r->width ||
+scanout->height != r->height) {
 pixman_image_t *rect;
-void *ptr = (uint8_t *)pixman_image_get_data(res->image) + offset;
-

[PATCH v7 14/14] virtio-gpu: Update cursor data using blob

2021-05-26 Thread Vivek Kasireddy
If a blob is available for the cursor, copy the data from the blob.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 19 ++-
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 8cee6cb3e5..4d549377cb 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -49,6 +49,7 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 {
 struct virtio_gpu_simple_resource *res;
 uint32_t pixels;
+void *data;
 
 res = virtio_gpu_find_check_resource(g, resource_id, false,
  __func__, NULL);
@@ -56,14 +57,22 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 return;
 }
 
-if (pixman_image_get_width(res->image)  != s->current_cursor->width ||
-pixman_image_get_height(res->image) != s->current_cursor->height) {
-return;
+if (res->blob_size) {
+if (res->blob_size < (s->current_cursor->width *
+  s->current_cursor->height * 4)) {
+return;
+}
+data = res->blob;
+} else {
+if (pixman_image_get_width(res->image)  != s->current_cursor->width ||
+pixman_image_get_height(res->image) != s->current_cursor->height) {
+return;
+}
+data = pixman_image_get_data(res->image);
 }
 
 pixels = s->current_cursor->width * s->current_cursor->height;
-memcpy(s->current_cursor->data,
-   pixman_image_get_data(res->image),
+memcpy(s->current_cursor->data, data,
pixels * sizeof(uint32_t));
 }
 
-- 
2.30.2




[PATCH v7 10/14] ui/pixman: Add qemu_pixman_to_drm_format()

2021-05-26 Thread Vivek Kasireddy
This new function to get the drm_format associated with a pixman
format will be useful while creating a dmabuf.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/qemu-pixman.h |  1 +
 ui/qemu-pixman.c | 35 ---
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 87737a6f16..806ddcd7cd 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -62,6 +62,7 @@ typedef struct PixelFormat {
 PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
 pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
 pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
+uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman);
 int qemu_pixman_get_type(int rshift, int gshift, int bshift);
 pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf);
 bool qemu_pixman_check_format(DisplayChangeListener *dcl,
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 85f2945e88..3ab7e2e958 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -89,21 +89,34 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, 
bool native_endian)
 }
 
 /* Note: drm is little endian, pixman is native endian */
+static const struct {
+uint32_t drm_format;
+pixman_format_code_t pixman_format;
+} drm_format_pixman_map[] = {
+{ DRM_FORMAT_RGB888,   PIXMAN_LE_r8g8b8   },
+{ DRM_FORMAT_ARGB, PIXMAN_LE_a8r8g8b8 },
+{ DRM_FORMAT_XRGB, PIXMAN_LE_x8r8g8b8 }
+};
+
 pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format)
 {
-static const struct {
-uint32_t drm_format;
-pixman_format_code_t pixman;
-} map[] = {
-{ DRM_FORMAT_RGB888,   PIXMAN_LE_r8g8b8   },
-{ DRM_FORMAT_ARGB, PIXMAN_LE_a8r8g8b8 },
-{ DRM_FORMAT_XRGB, PIXMAN_LE_x8r8g8b8 }
-};
 int i;
 
-for (i = 0; i < ARRAY_SIZE(map); i++) {
-if (drm_format == map[i].drm_format) {
-return map[i].pixman;
+for (i = 0; i < ARRAY_SIZE(drm_format_pixman_map); i++) {
+if (drm_format == drm_format_pixman_map[i].drm_format) {
+return drm_format_pixman_map[i].pixman_format;
+}
+}
+return 0;
+}
+
+uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format)
+{
+int i;
+
+for (i = 0; i < ARRAY_SIZE(drm_format_pixman_map); i++) {
+if (pixman_format == drm_format_pixman_map[i].pixman_format) {
+return drm_format_pixman_map[i].drm_format;
 }
 }
 return 0;
-- 
2.30.2




[PATCH v7 04/14] stubs: Add stubs for udmabuf helpers

2021-05-26 Thread Vivek Kasireddy
This is needed to ensure that virtio-gpu device works for
non-linux builds.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 meson.build|  2 +-
 stubs/meson.build  |  1 +
 stubs/virtio-gpu-udmabuf.c | 18 ++
 3 files changed, 20 insertions(+), 1 deletion(-)
 create mode 100644 stubs/virtio-gpu-udmabuf.c

diff --git a/meson.build b/meson.build
index 632b380738..9b6d93b8bb 100644
--- a/meson.build
+++ b/meson.build
@@ -1899,7 +1899,7 @@ util_ss.add_all(trace_ss)
 util_ss = util_ss.apply(config_all, strict: false)
 libqemuutil = static_library('qemuutil',
  sources: util_ss.sources() + stub_ss.sources() + 
genh,
- dependencies: [util_ss.dependencies(), m, glib, 
socket, malloc])
+ dependencies: [util_ss.dependencies(), m, glib, 
socket, malloc, pixman])
 qemuutil = declare_dependency(link_with: libqemuutil,
   sources: genh + version_res)
 
diff --git a/stubs/meson.build b/stubs/meson.build
index 3faef16892..9915251c4f 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -40,6 +40,7 @@ stub_ss.add(files('target-monitor-defs.c'))
 stub_ss.add(files('tpm.c'))
 stub_ss.add(files('trace-control.c'))
 stub_ss.add(files('uuid.c'))
+stub_ss.add(files('virtio-gpu-udmabuf.c'))
 stub_ss.add(files('vmgenid.c'))
 stub_ss.add(files('vmstate.c'))
 stub_ss.add(files('vm-stop.c'))
diff --git a/stubs/virtio-gpu-udmabuf.c b/stubs/virtio-gpu-udmabuf.c
new file mode 100644
index 00..e962e00d86
--- /dev/null
+++ b/stubs/virtio-gpu-udmabuf.c
@@ -0,0 +1,18 @@
+#include "qemu/osdep.h"
+#include "hw/virtio/virtio-gpu.h"
+
+bool virtio_gpu_have_udmabuf(void)
+{
+/* nothing (stub) */
+return false;
+}
+
+void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
+{
+/* nothing (stub) */
+}
+
+void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res)
+{
+/* nothing (stub) */
+}
-- 
2.30.2




[PATCH v7 12/14] virtio-gpu: Factor out update scanout

2021-05-26 Thread Vivek Kasireddy
Creating a small helper function for updating the scanout
will be useful in the next patch where this needs to be
done early in do_set_scanout before returning.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 35 +++
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 788b4540d5..0af08edde8 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -556,6 +556,28 @@ static void virtio_unref_resource(pixman_image_t *image, 
void *data)
 pixman_image_unref(data);
 }
 
+static void virtio_gpu_update_scanout(VirtIOGPU *g,
+  uint32_t scanout_id,
+  struct virtio_gpu_simple_resource *res,
+  struct virtio_gpu_rect *r)
+{
+struct virtio_gpu_simple_resource *ores;
+struct virtio_gpu_scanout *scanout;
+
+scanout = >parent_obj.scanout[scanout_id];
+ores = virtio_gpu_find_resource(g, scanout->resource_id);
+if (ores) {
+ores->scanout_bitmask &= ~(1 << scanout_id);
+}
+
+res->scanout_bitmask |= (1 << scanout_id);
+scanout->resource_id = res->resource_id;
+scanout->x = r->x;
+scanout->y = r->y;
+scanout->width = r->width;
+scanout->height = r->height;
+}
+
 static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
   uint32_t scanout_id,
   struct virtio_gpu_framebuffer *fb,
@@ -563,7 +585,6 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
   struct virtio_gpu_rect *r,
   uint32_t *error)
 {
-struct virtio_gpu_simple_resource *ores;
 struct virtio_gpu_scanout *scanout;
 uint8_t *data;
 
@@ -623,17 +644,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
 scanout->ds);
 }
 
-ores = virtio_gpu_find_resource(g, scanout->resource_id);
-if (ores) {
-ores->scanout_bitmask &= ~(1 << scanout_id);
-}
-
-res->scanout_bitmask |= (1 << scanout_id);
-scanout->resource_id = res->resource_id;
-scanout->x = r->x;
-scanout->y = r->y;
-scanout->width = r->width;
-scanout->height = r->height;
+virtio_gpu_update_scanout(g, scanout_id, res, r);
 }
 
 static void virtio_gpu_set_scanout(VirtIOGPU *g,
-- 
2.30.2




[PATCH v7 13/14] virtio-gpu: Add virtio_gpu_set_scanout_blob

2021-05-26 Thread Vivek Kasireddy
This API allows Qemu to set the blob allocated by the Guest as
the scanout buffer. If Opengl support is available, then the
scanout buffer would be submitted as a dmabuf to the UI; if not,
a pixman image is created from the scanout buffer and is
submitted to the UI via the display surface.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/trace-events  |   1 +
 hw/display/virtio-gpu.c  | 102 +--
 include/hw/virtio/virtio-gpu-bswap.h |   7 ++
 3 files changed, 103 insertions(+), 7 deletions(-)

diff --git a/hw/display/trace-events b/hw/display/trace-events
index f3f77b6984..e47264af5d 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -30,6 +30,7 @@ virtio_gpu_features(bool virgl) "virgl %d"
 virtio_gpu_cmd_get_display_info(void) ""
 virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d"
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, 
uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
+virtio_gpu_cmd_set_scanout_blob(uint32_t id, uint32_t res, uint32_t w, 
uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
 virtio_gpu_cmd_res_create_blob(uint32_t res, uint64_t size) "res 0x%x, size %" 
PRId64
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 0af08edde8..8cee6cb3e5 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -403,7 +403,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g,
 }
 }
 
-pixman_image_unref(res->image);
+qemu_pixman_image_unref(res->image);
 virtio_gpu_cleanup_mapping(g, res);
 QTAILQ_REMOVE(>reslist, res, next);
 g->hostmem -= res->hostmem;
@@ -492,6 +492,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 {
 struct virtio_gpu_simple_resource *res;
 struct virtio_gpu_resource_flush rf;
+struct virtio_gpu_scanout *scanout;
 pixman_region16_t flush_region;
 int i;
 
@@ -502,16 +503,29 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, rf.resource_id, false,
  __func__, >error);
-if (!res || res->blob) {
+if (!res) {
 return;
 }
 
-if (rf.r.x > res->width ||
+if (res->blob) {
+for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+scanout = >parent_obj.scanout[i];
+if (scanout->resource_id == res->resource_id &&
+console_has_gl(scanout->con)) {
+dpy_gl_update(scanout->con, 0, 0, scanout->width,
+  scanout->height);
+return;
+}
+}
+}
+
+if (!res->blob &&
+(rf.r.x > res->width ||
 rf.r.y > res->height ||
 rf.r.width > res->width ||
 rf.r.height > res->height ||
 rf.r.x + rf.r.width > res->width ||
-rf.r.y + rf.r.height > res->height) {
+rf.r.y + rf.r.height > res->height)) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource"
   " bounds for resource %d: %d %d %d %d vs %d %d\n",
   __func__, rf.resource_id, rf.r.x, rf.r.y,
@@ -523,7 +537,6 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 pixman_region_init_rect(_region,
 rf.r.x, rf.r.y, rf.r.width, rf.r.height);
 for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
-struct virtio_gpu_scanout *scanout;
 pixman_region16_t region, finalregion;
 pixman_box16_t *extents;
 
@@ -614,10 +627,23 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
 }
 
 g->parent_obj.enable = 1;
-data = (uint8_t *)pixman_image_get_data(res->image);
+
+if (res->blob) {
+if (console_has_gl(scanout->con)) {
+if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb)) {
+virtio_gpu_update_scanout(g, scanout_id, res, r);
+return;
+}
+}
+
+data = res->blob;
+} else {
+data = (uint8_t *)pixman_image_get_data(res->image);
+}
 
 /* create a surface for this scanout */
-if (!scanout->ds ||
+if ((res->blob && !console_has_gl(scanout->con)) ||
+!scanout->ds ||
 surface_data(scanout->ds) != data + fb->offset ||
 scanout->width != r->width ||
 scanout->height != r->h

[PATCH v7 00/14] virtio-gpu: Add support for Blob resources feature

2021-05-26 Thread Vivek Kasireddy
Enabling this feature would eliminate data copies from the resource
object in the Guest to the shadow resource in Qemu. This patch series
however adds support only for Blobs of type
VIRTIO_GPU_BLOB_MEM_GUEST with property VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE.

Most of the patches in this series are a rebased, refactored and bugfixed 
versions of Gerd Hoffmann's patches located here:
https://gitlab.freedesktop.org/virgl/qemu/-/commits/virtio-gpu-next

v2:
- Moved dpy_gl_update from set_scanout to resource_flush
- Dropped the modifier
- Rebase and other minor refactoring

v3:
- Rebased on top of Gerd's virgl device split series
- Split the udmabuf helpers patch from the previous 
  version into two (Gerd)
- Added explicit flush feature (last 7 patches)

v4 (Gerd):
- Dropped explicit flush feature patches from the series
- Slightly refactored udmabuf helpers patch (#3) to introduce
  and use blob and blob_size fields
- Fixed indentation issues and made other small changes in
  set_scanout_blob patch (#12)

v5:
- Rebase (only #6 - Refactor virtio_gpu_create_mapping_iov)

v6:
- Fixed compilation error seen in patch #5 with !linux config
- Fixed uninitialized variable usage warning in patch #10

v7:
- Added a file in stubs for udmabuf helpers
- Included the header qemu/memfd.h so that F_GET_SEALS gets
  a definition if it is not defined in standard headers

Cc: Gerd Hoffmann 

Vivek Kasireddy (14):
  ui: Get the fd associated with udmabuf driver
  headers: Add udmabuf.h
  virtio-gpu: Add udmabuf helpers
  stubs: Add stubs for udmabuf helpers
  virtio-gpu: Add virtio_gpu_find_check_resource
  virtio-gpu: Refactor virtio_gpu_set_scanout
  virtio-gpu: Refactor virtio_gpu_create_mapping_iov
  virtio-gpu: Add initial definitions for blob resources
  virtio-gpu: Add virtio_gpu_resource_create_blob
  ui/pixman: Add qemu_pixman_to_drm_format()
  virtio-gpu: Add helpers to create and destroy dmabuf objects
  virtio-gpu: Factor out update scanout
  virtio-gpu: Add virtio_gpu_set_scanout_blob
  virtio-gpu: Update cursor data using blob

 hw/display/meson.build   |   1 +
 hw/display/trace-events  |   2 +
 hw/display/virtio-gpu-base.c |   3 +
 hw/display/virtio-gpu-udmabuf.c  | 223 
 hw/display/virtio-gpu-virgl.c|   3 +-
 hw/display/virtio-gpu.c  | 431 ++-
 include/hw/virtio/virtio-gpu-bswap.h |  16 +
 include/hw/virtio/virtio-gpu.h   |  39 +-
 include/standard-headers/linux/udmabuf.h |  32 ++
 include/ui/console.h |   3 +
 include/ui/qemu-pixman.h |   1 +
 meson.build  |   2 +-
 scripts/update-linux-headers.sh  |   3 +
 stubs/meson.build|   1 +
 stubs/virtio-gpu-udmabuf.c   |  27 ++
 ui/meson.build   |   1 +
 ui/qemu-pixman.c |  35 +-
 ui/udmabuf.c |  40 +++
 18 files changed, 753 insertions(+), 110 deletions(-)
 create mode 100644 hw/display/virtio-gpu-udmabuf.c
 create mode 100644 include/standard-headers/linux/udmabuf.h
 create mode 100644 stubs/virtio-gpu-udmabuf.c
 create mode 100644 ui/udmabuf.c

-- 
2.30.2




[PATCH v7 03/14] virtio-gpu: Add udmabuf helpers

2021-05-26 Thread Vivek Kasireddy
Add helper functions to create a dmabuf for a resource and mmap it.
Also, introduce the fields blob and blob_size so that these helpers
can start to use them but the full picture will emerge only after
adding create_blob API in patch 8 of this series.

To be able to create a dmabuf using the udmabuf driver, Qemu needs
to be lauched with the memfd memory backend like this:

qemu-system-x86_64 -m 8192m -object memory-backend-memfd,id=mem1,size=8192M
-machine memory-backend=mem1

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/meson.build  |   1 +
 hw/display/virtio-gpu-udmabuf.c | 158 
 include/hw/virtio/virtio-gpu.h  |  11 +++
 3 files changed, 170 insertions(+)
 create mode 100644 hw/display/virtio-gpu-udmabuf.c

diff --git a/hw/display/meson.build b/hw/display/meson.build
index aaf797c5e9..e1f473c1df 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -56,6 +56,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
   virtio_gpu_ss = ss.source_set()
   virtio_gpu_ss.add(when: 'CONFIG_VIRTIO_GPU',
 if_true: [files('virtio-gpu-base.c', 'virtio-gpu.c'), 
pixman])
+  virtio_gpu_ss.add(when: 'CONFIG_LINUX', if_true: 
files('virtio-gpu-udmabuf.c'))
   virtio_gpu_ss.add(when: 'CONFIG_VHOST_USER_GPU', if_true: 
files('vhost-user-gpu.c'))
   hw_display_modules += {'virtio-gpu': virtio_gpu_ss}
 
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
new file mode 100644
index 00..71c4672e32
--- /dev/null
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -0,0 +1,158 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie 
+ * Gerd Hoffmann 
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-gpu-pixman.h"
+#include "trace.h"
+#include "exec/ramblock.h"
+#include "sysemu/hostmem.h"
+#include 
+#include 
+#include 
+#include "qemu/memfd.h"
+#include "standard-headers/linux/udmabuf.h"
+
+static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res)
+{
+struct udmabuf_create_list *list;
+RAMBlock *rb;
+ram_addr_t offset;
+int udmabuf, i;
+
+udmabuf = udmabuf_fd();
+if (udmabuf < 0) {
+return;
+}
+
+list = g_malloc0(sizeof(struct udmabuf_create_list) +
+ sizeof(struct udmabuf_create_item) * res->iov_cnt);
+
+for (i = 0; i < res->iov_cnt; i++) {
+rcu_read_lock();
+rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, );
+rcu_read_unlock();
+
+if (!rb || rb->fd < 0) {
+g_free(list);
+return;
+}
+
+list->list[i].memfd  = rb->fd;
+list->list[i].offset = offset;
+list->list[i].size   = res->iov[i].iov_len;
+}
+
+list->count = res->iov_cnt;
+list->flags = UDMABUF_FLAGS_CLOEXEC;
+
+res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list);
+if (res->dmabuf_fd < 0) {
+warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__,
+strerror(errno));
+}
+g_free(list);
+}
+
+static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res)
+{
+res->remapped = mmap(NULL, res->blob_size, PROT_READ,
+ MAP_SHARED, res->dmabuf_fd, 0);
+if (res->remapped == MAP_FAILED) {
+warn_report("%s: dmabuf mmap failed: %s", __func__,
+strerror(errno));
+res->remapped = NULL;
+}
+}
+
+static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res)
+{
+if (res->remapped) {
+munmap(res->remapped, res->blob_size);
+res->remapped = NULL;
+}
+if (res->dmabuf_fd >= 0) {
+close(res->dmabuf_fd);
+res->dmabuf_fd = -1;
+}
+}
+
+static int find_memory_backend_type(Object *obj, void *opaque)
+{
+bool *memfd_backend = opaque;
+int ret;
+
+if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
+HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+RAMBlock *rb = backend->mr.ram_block;
+
+if (rb && rb->fd > 0) {
+ret = fcntl(rb->fd, F_GET_SEALS);
+if (ret > 0) {
+*memfd_backend = true;
+}
+}
+}
+
+return 0;
+}
+
+bool virtio_gpu_have_udmabuf(void)
+{
+Object *memdev_root;
+int udmabuf;
+bool memfd_backend = false;
+
+udmabuf = udmabuf_fd();
+

[PATCH v7 11/14] virtio-gpu: Add helpers to create and destroy dmabuf objects

2021-05-26 Thread Vivek Kasireddy
These helpers can be useful for creating dmabuf objects from blobs
and submitting them to the UI.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-udmabuf.c | 65 +
 include/hw/virtio/virtio-gpu.h  | 15 
 stubs/virtio-gpu-udmabuf.c  |  9 +
 3 files changed, 89 insertions(+)

diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index 71c4672e32..3c01a415e7 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -156,3 +156,68 @@ void virtio_gpu_fini_udmabuf(struct 
virtio_gpu_simple_resource *res)
 virtio_gpu_destroy_udmabuf(res);
 }
 }
+
+static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
+{
+struct virtio_gpu_scanout *scanout;
+
+scanout = >parent_obj.scanout[dmabuf->scanout_id];
+dpy_gl_release_dmabuf(scanout->con, >buf);
+QTAILQ_REMOVE(>dmabuf.bufs, dmabuf, next);
+g_free(dmabuf);
+}
+
+static VGPUDMABuf
+*virtio_gpu_create_dmabuf(VirtIOGPU *g,
+  uint32_t scanout_id,
+  struct virtio_gpu_simple_resource *res,
+  struct virtio_gpu_framebuffer *fb)
+{
+VGPUDMABuf *dmabuf;
+
+if (res->dmabuf_fd < 0) {
+return NULL;
+}
+
+dmabuf = g_new0(VGPUDMABuf, 1);
+dmabuf->buf.width = fb->width;
+dmabuf->buf.height = fb->height;
+dmabuf->buf.stride = fb->stride;
+dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
+dmabuf->buf.fd = res->dmabuf_fd;
+
+dmabuf->scanout_id = scanout_id;
+QTAILQ_INSERT_HEAD(>dmabuf.bufs, dmabuf, next);
+
+return dmabuf;
+}
+
+int virtio_gpu_update_dmabuf(VirtIOGPU *g,
+ uint32_t scanout_id,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb)
+{
+struct virtio_gpu_scanout *scanout = >parent_obj.scanout[scanout_id];
+VGPUDMABuf *new_primary, *old_primary = NULL;
+
+new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb);
+if (!new_primary) {
+return -EINVAL;
+}
+
+if (g->dmabuf.primary) {
+old_primary = g->dmabuf.primary;
+}
+
+g->dmabuf.primary = new_primary;
+qemu_console_resize(scanout->con,
+new_primary->buf.width,
+new_primary->buf.height);
+dpy_gl_scanout_dmabuf(scanout->con, _primary->buf);
+
+if (old_primary) {
+virtio_gpu_free_dmabuf(g, old_primary);
+}
+
+return 0;
+}
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 66e7aaad0e..bcf54d970f 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -150,6 +150,12 @@ struct VirtIOGPUBaseClass {
 DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024), \
 DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768)
 
+typedef struct VGPUDMABuf {
+QemuDmaBuf buf;
+uint32_t scanout_id;
+QTAILQ_ENTRY(VGPUDMABuf) next;
+} VGPUDMABuf;
+
 struct VirtIOGPU {
 VirtIOGPUBase parent_obj;
 
@@ -178,6 +184,11 @@ struct VirtIOGPU {
 uint32_t req_3d;
 uint32_t bytes_3d;
 } stats;
+
+struct {
+QTAILQ_HEAD(, VGPUDMABuf) bufs;
+VGPUDMABuf *primary;
+} dmabuf;
 };
 
 struct VirtIOGPUClass {
@@ -259,6 +270,10 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 bool virtio_gpu_have_udmabuf(void);
 void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res);
 void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res);
+int virtio_gpu_update_dmabuf(VirtIOGPU *g,
+ uint32_t scanout_id,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb);
 
 /* virtio-gpu-3d.c */
 void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
diff --git a/stubs/virtio-gpu-udmabuf.c b/stubs/virtio-gpu-udmabuf.c
index e962e00d86..81f661441a 100644
--- a/stubs/virtio-gpu-udmabuf.c
+++ b/stubs/virtio-gpu-udmabuf.c
@@ -16,3 +16,12 @@ void virtio_gpu_fini_udmabuf(struct 
virtio_gpu_simple_resource *res)
 {
 /* nothing (stub) */
 }
+
+int virtio_gpu_update_dmabuf(VirtIOGPU *g,
+ uint32_t scanout_id,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb)
+{
+/* nothing (stub) */
+return 0;
+}
-- 
2.30.2




[PATCH v7 02/14] headers: Add udmabuf.h

2021-05-26 Thread Vivek Kasireddy
This adds udmabuf header to standard headers so that the
relevant udmabuf objects can be accessed in subsequent
patches.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/standard-headers/linux/udmabuf.h | 32 
 scripts/update-linux-headers.sh  |  3 +++
 2 files changed, 35 insertions(+)
 create mode 100644 include/standard-headers/linux/udmabuf.h

diff --git a/include/standard-headers/linux/udmabuf.h 
b/include/standard-headers/linux/udmabuf.h
new file mode 100644
index 00..e19eb5b5ce
--- /dev/null
+++ b/include/standard-headers/linux/udmabuf.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_UDMABUF_H
+#define _LINUX_UDMABUF_H
+
+#include "standard-headers/linux/types.h"
+
+#define UDMABUF_FLAGS_CLOEXEC  0x01
+
+struct udmabuf_create {
+   uint32_t memfd;
+   uint32_t flags;
+   uint64_t offset;
+   uint64_t size;
+};
+
+struct udmabuf_create_item {
+   uint32_t memfd;
+   uint32_t __pad;
+   uint64_t offset;
+   uint64_t size;
+};
+
+struct udmabuf_create_list {
+   uint32_t flags;
+   uint32_t count;
+   struct udmabuf_create_item list[];
+};
+
+#define UDMABUF_CREATE   _IOW('u', 0x42, struct udmabuf_create)
+#define UDMABUF_CREATE_LIST  _IOW('u', 0x43, struct udmabuf_create_list)
+
+#endif /* _LINUX_UDMABUF_H */
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index 1050e36169..fea4d6eb65 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -34,6 +34,7 @@ cp_portable() {
 if
 grep '#include' "$f" | grep -v -e 'linux/virtio' \
  -e 'linux/types' \
+ -e 'linux/ioctl' \
  -e 'stdint' \
  -e 'linux/if_ether' \
  -e 'input-event-codes' \
@@ -66,6 +67,7 @@ cp_portable() {
 -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \
 -e '/\"drm.h\"/d' \
 -e '/sys\/ioctl.h/d' \
+-e '/linux\/ioctl.h/d' \
 -e 's/SW_MAX/SW_MAX_/' \
 -e 's/atomic_t/int/' \
 -e 's/__kernel_long_t/long/' \
@@ -190,6 +192,7 @@ for i in "$tmpdir"/include/linux/*virtio*.h \
  "$tmpdir/include/linux/fuse.h" \
  "$tmpdir/include/linux/input.h" \
  "$tmpdir/include/linux/input-event-codes.h" \
+ "$tmpdir/include/linux/udmabuf.h" \
  "$tmpdir/include/linux/pci_regs.h" \
  "$tmpdir/include/linux/ethtool.h" \
  "$tmpdir/include/linux/const.h" \
-- 
2.30.2




[PATCH v6 09/13] ui/pixman: Add qemu_pixman_to_drm_format()

2021-05-24 Thread Vivek Kasireddy
This new function to get the drm_format associated with a pixman
format will be useful while creating a dmabuf.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/ui/qemu-pixman.h |  1 +
 ui/qemu-pixman.c | 35 ---
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 87737a6f16..806ddcd7cd 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -62,6 +62,7 @@ typedef struct PixelFormat {
 PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
 pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
 pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
+uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman);
 int qemu_pixman_get_type(int rshift, int gshift, int bshift);
 pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf);
 bool qemu_pixman_check_format(DisplayChangeListener *dcl,
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 85f2945e88..3ab7e2e958 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -89,21 +89,34 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, 
bool native_endian)
 }
 
 /* Note: drm is little endian, pixman is native endian */
+static const struct {
+uint32_t drm_format;
+pixman_format_code_t pixman_format;
+} drm_format_pixman_map[] = {
+{ DRM_FORMAT_RGB888,   PIXMAN_LE_r8g8b8   },
+{ DRM_FORMAT_ARGB, PIXMAN_LE_a8r8g8b8 },
+{ DRM_FORMAT_XRGB, PIXMAN_LE_x8r8g8b8 }
+};
+
 pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format)
 {
-static const struct {
-uint32_t drm_format;
-pixman_format_code_t pixman;
-} map[] = {
-{ DRM_FORMAT_RGB888,   PIXMAN_LE_r8g8b8   },
-{ DRM_FORMAT_ARGB, PIXMAN_LE_a8r8g8b8 },
-{ DRM_FORMAT_XRGB, PIXMAN_LE_x8r8g8b8 }
-};
 int i;
 
-for (i = 0; i < ARRAY_SIZE(map); i++) {
-if (drm_format == map[i].drm_format) {
-return map[i].pixman;
+for (i = 0; i < ARRAY_SIZE(drm_format_pixman_map); i++) {
+if (drm_format == drm_format_pixman_map[i].drm_format) {
+return drm_format_pixman_map[i].pixman_format;
+}
+}
+return 0;
+}
+
+uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format)
+{
+int i;
+
+for (i = 0; i < ARRAY_SIZE(drm_format_pixman_map); i++) {
+if (pixman_format == drm_format_pixman_map[i].pixman_format) {
+return drm_format_pixman_map[i].drm_format;
 }
 }
 return 0;
-- 
2.30.2




[PATCH v6 13/13] virtio-gpu: Update cursor data using blob

2021-05-24 Thread Vivek Kasireddy
If a blob is available for the cursor, copy the data from the blob.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 19 ++-
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 8cee6cb3e5..4d549377cb 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -49,6 +49,7 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 {
 struct virtio_gpu_simple_resource *res;
 uint32_t pixels;
+void *data;
 
 res = virtio_gpu_find_check_resource(g, resource_id, false,
  __func__, NULL);
@@ -56,14 +57,22 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g,
 return;
 }
 
-if (pixman_image_get_width(res->image)  != s->current_cursor->width ||
-pixman_image_get_height(res->image) != s->current_cursor->height) {
-return;
+if (res->blob_size) {
+if (res->blob_size < (s->current_cursor->width *
+  s->current_cursor->height * 4)) {
+return;
+}
+data = res->blob;
+} else {
+if (pixman_image_get_width(res->image)  != s->current_cursor->width ||
+pixman_image_get_height(res->image) != s->current_cursor->height) {
+return;
+}
+data = pixman_image_get_data(res->image);
 }
 
 pixels = s->current_cursor->width * s->current_cursor->height;
-memcpy(s->current_cursor->data,
-   pixman_image_get_data(res->image),
+memcpy(s->current_cursor->data, data,
pixels * sizeof(uint32_t));
 }
 
-- 
2.30.2




[PATCH v6 08/13] virtio-gpu: Add virtio_gpu_resource_create_blob

2021-05-24 Thread Vivek Kasireddy
This API allows Qemu to register the blob allocated by the Guest
as a new resource and map its backing storage.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/trace-events  |  1 +
 hw/display/virtio-gpu.c  | 73 ++--
 include/hw/virtio/virtio-gpu-bswap.h |  9 
 3 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/hw/display/trace-events b/hw/display/trace-events
index 9fccca18a1..f3f77b6984 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -32,6 +32,7 @@ virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d"
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, 
uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
+virtio_gpu_cmd_res_create_blob(uint32_t res, uint64_t size) "res 0x%x, size %" 
PRId64
 virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x"
 virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x"
 virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x"
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index f77a7fc7dd..788b4540d5 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -137,7 +137,7 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t 
resource_id,
 }
 
 if (require_backing) {
-if (!res->iov || !res->image) {
+if (!res->iov || (!res->image && !res->blob)) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: no backing storage %d\n",
   caller, resource_id);
 if (error) {
@@ -313,6 +313,62 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
 g->hostmem += res->hostmem;
 }
 
+static void virtio_gpu_resource_create_blob(VirtIOGPU *g,
+struct virtio_gpu_ctrl_command 
*cmd)
+{
+struct virtio_gpu_simple_resource *res;
+struct virtio_gpu_resource_create_blob cblob;
+int ret;
+
+VIRTIO_GPU_FILL_CMD(cblob);
+virtio_gpu_create_blob_bswap();
+trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size);
+
+if (cblob.resource_id == 0) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+  __func__);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+return;
+}
+
+res = virtio_gpu_find_resource(g, cblob.resource_id);
+if (res) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+  __func__, cblob.resource_id);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+return;
+}
+
+res = g_new0(struct virtio_gpu_simple_resource, 1);
+res->resource_id = cblob.resource_id;
+res->blob_size = cblob.size;
+
+if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_GUEST &&
+cblob.blob_flags != VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid memory type\n",
+  __func__);
+cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+g_free(res);
+return;
+}
+
+if (res->iov) {
+cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+return;
+}
+
+ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob),
+cmd, >addrs, >iov,
+>iov_cnt);
+if (ret != 0) {
+cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+return;
+}
+
+virtio_gpu_init_udmabuf(res);
+QTAILQ_INSERT_HEAD(>reslist, res, next);
+}
+
 static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
 {
 struct virtio_gpu_scanout *scanout = >parent_obj.scanout[scanout_id];
@@ -390,7 +446,7 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, t2d.resource_id, true,
  __func__, >error);
-if (!res) {
+if (!res || res->blob) {
 return;
 }
 
@@ -446,7 +502,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, rf.resource_id, false,
  __func__, >error);
-if (!res) {
+if (!res || res->blob) {
 return;
 }
 
@@ -715,6 +771,10 @@ static void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
 res->iov_cnt = 0;
 g_free(res->addrs);
 res->addrs = NULL;
+
+if (res->blob) {
+virtio_gpu_fini_udmabuf(res);
+}
 }
 
 static void
@@ -

[PATCH v6 12/13] virtio-gpu: Add virtio_gpu_set_scanout_blob

2021-05-24 Thread Vivek Kasireddy
This API allows Qemu to set the blob allocated by the Guest as
the scanout buffer. If Opengl support is available, then the
scanout buffer would be submitted as a dmabuf to the UI; if not,
a pixman image is created from the scanout buffer and is
submitted to the UI via the display surface.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/trace-events  |   1 +
 hw/display/virtio-gpu.c  | 102 +--
 include/hw/virtio/virtio-gpu-bswap.h |   7 ++
 3 files changed, 103 insertions(+), 7 deletions(-)

diff --git a/hw/display/trace-events b/hw/display/trace-events
index f3f77b6984..e47264af5d 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -30,6 +30,7 @@ virtio_gpu_features(bool virgl) "virgl %d"
 virtio_gpu_cmd_get_display_info(void) ""
 virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d"
 virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, 
uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
+virtio_gpu_cmd_set_scanout_blob(uint32_t id, uint32_t res, uint32_t w, 
uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
 virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h) "res 0x%x, fmt 0x%x, w %d, h %d"
 virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t 
h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
 virtio_gpu_cmd_res_create_blob(uint32_t res, uint64_t size) "res 0x%x, size %" 
PRId64
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 0af08edde8..8cee6cb3e5 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -403,7 +403,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g,
 }
 }
 
-pixman_image_unref(res->image);
+qemu_pixman_image_unref(res->image);
 virtio_gpu_cleanup_mapping(g, res);
 QTAILQ_REMOVE(>reslist, res, next);
 g->hostmem -= res->hostmem;
@@ -492,6 +492,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 {
 struct virtio_gpu_simple_resource *res;
 struct virtio_gpu_resource_flush rf;
+struct virtio_gpu_scanout *scanout;
 pixman_region16_t flush_region;
 int i;
 
@@ -502,16 +503,29 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 
 res = virtio_gpu_find_check_resource(g, rf.resource_id, false,
  __func__, >error);
-if (!res || res->blob) {
+if (!res) {
 return;
 }
 
-if (rf.r.x > res->width ||
+if (res->blob) {
+for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+scanout = >parent_obj.scanout[i];
+if (scanout->resource_id == res->resource_id &&
+console_has_gl(scanout->con)) {
+dpy_gl_update(scanout->con, 0, 0, scanout->width,
+  scanout->height);
+return;
+}
+}
+}
+
+if (!res->blob &&
+(rf.r.x > res->width ||
 rf.r.y > res->height ||
 rf.r.width > res->width ||
 rf.r.height > res->height ||
 rf.r.x + rf.r.width > res->width ||
-rf.r.y + rf.r.height > res->height) {
+rf.r.y + rf.r.height > res->height)) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource"
   " bounds for resource %d: %d %d %d %d vs %d %d\n",
   __func__, rf.resource_id, rf.r.x, rf.r.y,
@@ -523,7 +537,6 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 pixman_region_init_rect(_region,
 rf.r.x, rf.r.y, rf.r.width, rf.r.height);
 for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
-struct virtio_gpu_scanout *scanout;
 pixman_region16_t region, finalregion;
 pixman_box16_t *extents;
 
@@ -614,10 +627,23 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
 }
 
 g->parent_obj.enable = 1;
-data = (uint8_t *)pixman_image_get_data(res->image);
+
+if (res->blob) {
+if (console_has_gl(scanout->con)) {
+if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb)) {
+virtio_gpu_update_scanout(g, scanout_id, res, r);
+return;
+}
+}
+
+data = res->blob;
+} else {
+data = (uint8_t *)pixman_image_get_data(res->image);
+}
 
 /* create a surface for this scanout */
-if (!scanout->ds ||
+if ((res->blob && !console_has_gl(scanout->con)) ||
+!scanout->ds ||
 surface_data(scanout->ds) != data + fb->offset ||
 scanout->width != r->width ||
 scanout->height != r->h

[PATCH v6 07/13] virtio-gpu: Add initial definitions for blob resources

2021-05-24 Thread Vivek Kasireddy
Add the property bit, configuration flag and other relevant
macros and definitions associated with this feature.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-base.c   |  3 +++
 hw/display/virtio-gpu.c| 14 ++
 include/hw/virtio/virtio-gpu.h |  3 +++
 3 files changed, 20 insertions(+)

diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
index afb3ee7d9a..dd294276cb 100644
--- a/hw/display/virtio-gpu-base.c
+++ b/hw/display/virtio-gpu-base.c
@@ -208,6 +208,9 @@ virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t 
features,
 if (virtio_gpu_edid_enabled(g->conf)) {
 features |= (1 << VIRTIO_GPU_F_EDID);
 }
+if (virtio_gpu_blob_enabled(g->conf)) {
+features |= (1 << VIRTIO_GPU_F_RESOURCE_BLOB);
+}
 
 return features;
 }
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 7a0db3a860..f77a7fc7dd 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1108,6 +1108,18 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error 
**errp)
 VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
 VirtIOGPU *g = VIRTIO_GPU(qdev);
 
+if (virtio_gpu_blob_enabled(g->parent_obj.conf)) {
+if (!virtio_gpu_have_udmabuf()) {
+error_setg(errp, "cannot enable blob resources without udmabuf");
+return;
+}
+
+if (virtio_gpu_virgl_enabled(g->parent_obj.conf)) {
+error_setg(errp, "blobs and virgl are not compatible (yet)");
+return;
+}
+}
+
 if (!virtio_gpu_base_device_realize(qdev,
 virtio_gpu_handle_ctrl_cb,
 virtio_gpu_handle_cursor_cb,
@@ -1201,6 +1213,8 @@ static Property virtio_gpu_properties[] = {
 VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf),
 DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem,
  256 * MiB),
+DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
+VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index dad9a1d221..66e7aaad0e 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -89,6 +89,7 @@ enum virtio_gpu_base_conf_flags {
 VIRTIO_GPU_FLAG_STATS_ENABLED,
 VIRTIO_GPU_FLAG_EDID_ENABLED,
 VIRTIO_GPU_FLAG_DMABUF_ENABLED,
+VIRTIO_GPU_FLAG_BLOB_ENABLED,
 };
 
 #define virtio_gpu_virgl_enabled(_cfg) \
@@ -99,6 +100,8 @@ enum virtio_gpu_base_conf_flags {
 (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED))
 #define virtio_gpu_dmabuf_enabled(_cfg) \
 (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED))
+#define virtio_gpu_blob_enabled(_cfg) \
+(_cfg.flags & (1 << VIRTIO_GPU_FLAG_BLOB_ENABLED))
 
 struct virtio_gpu_base_conf {
 uint32_t max_outputs;
-- 
2.30.2




[PATCH v6 11/13] virtio-gpu: Factor out update scanout

2021-05-24 Thread Vivek Kasireddy
Creating a small helper function for updating the scanout
will be useful in the next patch where this needs to be
done early in do_set_scanout before returning.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu.c | 35 +++
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 788b4540d5..0af08edde8 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -556,6 +556,28 @@ static void virtio_unref_resource(pixman_image_t *image, 
void *data)
 pixman_image_unref(data);
 }
 
+static void virtio_gpu_update_scanout(VirtIOGPU *g,
+  uint32_t scanout_id,
+  struct virtio_gpu_simple_resource *res,
+  struct virtio_gpu_rect *r)
+{
+struct virtio_gpu_simple_resource *ores;
+struct virtio_gpu_scanout *scanout;
+
+scanout = >parent_obj.scanout[scanout_id];
+ores = virtio_gpu_find_resource(g, scanout->resource_id);
+if (ores) {
+ores->scanout_bitmask &= ~(1 << scanout_id);
+}
+
+res->scanout_bitmask |= (1 << scanout_id);
+scanout->resource_id = res->resource_id;
+scanout->x = r->x;
+scanout->y = r->y;
+scanout->width = r->width;
+scanout->height = r->height;
+}
+
 static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
   uint32_t scanout_id,
   struct virtio_gpu_framebuffer *fb,
@@ -563,7 +585,6 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
   struct virtio_gpu_rect *r,
   uint32_t *error)
 {
-struct virtio_gpu_simple_resource *ores;
 struct virtio_gpu_scanout *scanout;
 uint8_t *data;
 
@@ -623,17 +644,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
 scanout->ds);
 }
 
-ores = virtio_gpu_find_resource(g, scanout->resource_id);
-if (ores) {
-ores->scanout_bitmask &= ~(1 << scanout_id);
-}
-
-res->scanout_bitmask |= (1 << scanout_id);
-scanout->resource_id = res->resource_id;
-scanout->x = r->x;
-scanout->y = r->y;
-scanout->width = r->width;
-scanout->height = r->height;
+virtio_gpu_update_scanout(g, scanout_id, res, r);
 }
 
 static void virtio_gpu_set_scanout(VirtIOGPU *g,
-- 
2.30.2




[PATCH v6 06/13] virtio-gpu: Refactor virtio_gpu_create_mapping_iov

2021-05-24 Thread Vivek Kasireddy
Instead of passing the attach_backing object to extract nr_entries
and offset, explicitly pass these as arguments to this function.
This will be helpful when adding create_blob API.

Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 hw/display/virtio-gpu-virgl.c  |  3 ++-
 hw/display/virtio-gpu.c| 19 +--
 include/hw/virtio/virtio-gpu.h |  2 +-
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 72c14d9132..092c6dc380 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -289,7 +289,8 @@ static void virgl_resource_attach_backing(VirtIOGPU *g,
 VIRTIO_GPU_FILL_CMD(att_rb);
 trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
 
-ret = virtio_gpu_create_mapping_iov(g, _rb, cmd, NULL, _iovs, 
_niov);
+ret = virtio_gpu_create_mapping_iov(g, att_rb.nr_entries, sizeof(att_rb),
+cmd, NULL, _iovs, _niov);
 if (ret != 0) {
 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 return;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index fdcedfc61e..7a0db3a860 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -615,7 +615,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
 }
 
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
-  struct virtio_gpu_resource_attach_backing 
*ab,
+  uint32_t nr_entries, uint32_t offset,
   struct virtio_gpu_ctrl_command *cmd,
   uint64_t **addr, struct iovec **iov,
   uint32_t *niov)
@@ -624,17 +624,17 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
 size_t esize, s;
 int e, v;
 
-if (ab->nr_entries > 16384) {
+if (nr_entries > 16384) {
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: nr_entries is too big (%d > 16384)\n",
-  __func__, ab->nr_entries);
+  __func__, nr_entries);
 return -1;
 }
 
-esize = sizeof(*ents) * ab->nr_entries;
+esize = sizeof(*ents) * nr_entries;
 ents = g_malloc(esize);
 s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
-   sizeof(*ab), ents, esize);
+   offset, ents, esize);
 if (s != esize) {
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: command data size incorrect %zu vs %zu\n",
@@ -647,7 +647,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
 if (addr) {
 *addr = NULL;
 }
-for (e = 0, v = 0; e < ab->nr_entries; e++) {
+for (e = 0, v = 0; e < nr_entries; e++) {
 uint64_t a = le64_to_cpu(ents[e].addr);
 uint32_t l = le32_to_cpu(ents[e].length);
 hwaddr len;
@@ -659,8 +659,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
  a, , DMA_DIRECTION_TO_DEVICE);
 if (!map) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory 
for"
-  " resource %d element %d\n",
-  __func__, ab->resource_id, e);
+  " element %d\n", __func__, e);
 virtio_gpu_cleanup_mapping_iov(g, *iov, v);
 g_free(ents);
 *iov = NULL;
@@ -743,8 +742,8 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g,
 return;
 }
 
-ret = virtio_gpu_create_mapping_iov(g, , cmd, >addrs,
->iov, >iov_cnt);
+ret = virtio_gpu_create_mapping_iov(g, ab.nr_entries, sizeof(ab), cmd,
+>addrs, >iov, >iov_cnt);
 if (ret != 0) {
 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
 return;
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index b83a91a67f..dad9a1d221 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -238,7 +238,7 @@ void virtio_gpu_get_display_info(VirtIOGPU *g,
 void virtio_gpu_get_edid(VirtIOGPU *g,
  struct virtio_gpu_ctrl_command *cmd);
 int virtio_gpu_create_mapping_iov(VirtIOGPU *g,
-  struct virtio_gpu_resource_attach_backing 
*ab,
+  uint32_t nr_entries, uint32_t offset,
   struct virtio_gpu_ctrl_command *cmd,
   uint64_t **addr, struct iovec **iov,
   uint32_t *niov);
-- 
2.30.2




[PATCH v6 02/13] headers: Add udmabuf.h

2021-05-24 Thread Vivek Kasireddy
This adds udmabuf header to standard headers so that the
relevant udmabuf objects can be accessed in subsequent
patches.

Based-on-patch-by: Gerd Hoffmann 
Cc: Gerd Hoffmann 
Signed-off-by: Vivek Kasireddy 
---
 include/standard-headers/linux/udmabuf.h | 32 
 scripts/update-linux-headers.sh  |  3 +++
 2 files changed, 35 insertions(+)
 create mode 100644 include/standard-headers/linux/udmabuf.h

diff --git a/include/standard-headers/linux/udmabuf.h 
b/include/standard-headers/linux/udmabuf.h
new file mode 100644
index 00..e19eb5b5ce
--- /dev/null
+++ b/include/standard-headers/linux/udmabuf.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_UDMABUF_H
+#define _LINUX_UDMABUF_H
+
+#include "standard-headers/linux/types.h"
+
+#define UDMABUF_FLAGS_CLOEXEC  0x01
+
+struct udmabuf_create {
+   uint32_t memfd;
+   uint32_t flags;
+   uint64_t offset;
+   uint64_t size;
+};
+
+struct udmabuf_create_item {
+   uint32_t memfd;
+   uint32_t __pad;
+   uint64_t offset;
+   uint64_t size;
+};
+
+struct udmabuf_create_list {
+   uint32_t flags;
+   uint32_t count;
+   struct udmabuf_create_item list[];
+};
+
+#define UDMABUF_CREATE   _IOW('u', 0x42, struct udmabuf_create)
+#define UDMABUF_CREATE_LIST  _IOW('u', 0x43, struct udmabuf_create_list)
+
+#endif /* _LINUX_UDMABUF_H */
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index 1050e36169..fea4d6eb65 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -34,6 +34,7 @@ cp_portable() {
 if
 grep '#include' "$f" | grep -v -e 'linux/virtio' \
  -e 'linux/types' \
+ -e 'linux/ioctl' \
  -e 'stdint' \
  -e 'linux/if_ether' \
  -e 'input-event-codes' \
@@ -66,6 +67,7 @@ cp_portable() {
 -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \
 -e '/\"drm.h\"/d' \
 -e '/sys\/ioctl.h/d' \
+-e '/linux\/ioctl.h/d' \
 -e 's/SW_MAX/SW_MAX_/' \
 -e 's/atomic_t/int/' \
 -e 's/__kernel_long_t/long/' \
@@ -190,6 +192,7 @@ for i in "$tmpdir"/include/linux/*virtio*.h \
  "$tmpdir/include/linux/fuse.h" \
  "$tmpdir/include/linux/input.h" \
  "$tmpdir/include/linux/input-event-codes.h" \
+ "$tmpdir/include/linux/udmabuf.h" \
  "$tmpdir/include/linux/pci_regs.h" \
  "$tmpdir/include/linux/ethtool.h" \
  "$tmpdir/include/linux/const.h" \
-- 
2.30.2




  1   2   >