hw/display/virtio-gpu-gl.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
From: Benno Fünfstück <benno.fuenfstueck@neodyme.io>
This is a fix for a bug in virglrenderer, where if the guest uses hardware mouse cursor acceleration then the mouse cursor can end up being a resource with the flag Y_0_TOP set.
But virglrenderer does not honor this flag when reading the cursor data.
This has been fixed in current virglrenderer master.
For older virglrenderer, we need to make sure to flip the cursor data such that display clients render it correctly.
For example, this fixes mouse cursor rendering for wlroots-based window managers (like Sway) in Linux guests.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2315
Signed-off-by: Benno Fünfstück <benno.fuenfstueck@neodyme.io>
---
Now with a version check, since the fix for this is in virlglrenderer master so I think it is any version newer than the current one (which is 1.3.0).
I copied the code from virtio-gpu-virgl.c for the version check for simplicty, if I should instead move that code for some DRY feel free to say so.
hw/display/virtio-gpu-gl.c | 45 ++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
index 2b7a41c466..6505a84f5e 100644
--- a/hw/display/virtio-gpu-gl.c
+++ b/hw/display/virtio-gpu-gl.c
@@ -26,6 +26,25 @@
#include <virglrenderer.h>
+/*
+ * VIRGL_CHECK_VERSION available since libvirglrenderer 1.0.1 and was fixed
+ * in 1.1.0. Undefine bugged version of the macro and provide our own.
+ */
+#if defined(VIRGL_CHECK_VERSION) && \
+ VIRGL_VERSION_MAJOR == 1 && VIRGL_VERSION_MINOR < 1
+#undef VIRGL_CHECK_VERSION
+#endif
+
+#ifndef VIRGL_CHECK_VERSION
+#define VIRGL_CHECK_VERSION(major, minor, micro) \
+ (VIRGL_VERSION_MAJOR > (major) || \
+ VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR > (minor) || \
+ VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR == (minor) && \
+ VIRGL_VERSION_MICRO >= (micro))
+#endif
+
+#define VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX (!VIRGL_CHECK_VERSION(1, 3, 1))
+
static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id)
@@ -33,6 +52,11 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
uint32_t width, height;
uint32_t pixels, *data;
+#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
+ bool y_0_top = true;
+ int ret;
+ struct virgl_renderer_resource_info info;
+#endif
if (gl->renderer_state != RS_INITED) {
return;
@@ -49,8 +73,29 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
return;
}
+#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
+ memset(&info, 0, sizeof(info));
+ ret = virgl_renderer_resource_get_info(resource_id, &info);
+ if (ret == 0) {
+ y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+ }
+#endif
+
pixels = s->current_cursor->width * s->current_cursor->height;
+#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
+ if (y_0_top) {
+ memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
+ } else {
+ uint32_t *dst = s->current_cursor->data;
+ for (uint32_t y = 0; y < height; y++) {
+ memcpy(dst + y * width,
+ data + (height - 1 - y) * width,
+ width * sizeof(uint32_t));
+ }
+ }
+#else
memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
+#endif
free(data);
}
--
2.51.2
--
Neodyme AG
Sitz der Gesellschaft / Address: Dirnismaning 55 | Halle 13 |
85748 Garching b.München
Postanschrift: Rosenthaler Straße 72a | 10119
Berlin
Registergericht / Registry court: München, HRB 269168
Vorstand /
Management Board: Thomas Lambertz | Tobias Madl
Aufsichtsratsvorsitzender /
Chairman of the Supervisory Board: Hendrik Hofstadt
Hi
On Mon, Apr 27, 2026 at 2:57 PM Benno Fünstück
<benno.fuenfstueck@neodyme.io> wrote:
>
> From: Benno Fünfstück <benno.fuenfstueck@neodyme.io>
>
> This is a fix for a bug in virglrenderer, where if the guest uses hardware mouse cursor acceleration then the mouse cursor can end up being a resource with the flag Y_0_TOP set.
> But virglrenderer does not honor this flag when reading the cursor data.
> This has been fixed in current virglrenderer master.
> For older virglrenderer, we need to make sure to flip the cursor data such that display clients render it correctly.
>
> For example, this fixes mouse cursor rendering for wlroots-based window managers (like Sway) in Linux guests.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2315
> Signed-off-by: Benno Fünfstück <benno.fuenfstueck@neodyme.io>
In v1, Akihiko raised a valid concern that this might be backported
and fixed in earlier version of virgl:
https://patchew.org/QEMU/20260409144639.3610-1-benno.fuenfstueck@neodyme.io/#40fb4df5-20a7-49ce-a087-d9c048255d05@rsg.ci.i.u-tokyo.ac.jp
> ---
> Now with a version check, since the fix for this is in virlglrenderer master so I think it is any version newer than the current one (which is 1.3.0).
> I copied the code from virtio-gpu-virgl.c for the version check for simplicty, if I should instead move that code for some DRY feel free to say so.
>
> hw/display/virtio-gpu-gl.c | 45 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 45 insertions(+)
>
> diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
> index 2b7a41c466..6505a84f5e 100644
> --- a/hw/display/virtio-gpu-gl.c
> +++ b/hw/display/virtio-gpu-gl.c
> @@ -26,6 +26,25 @@
>
> #include <virglrenderer.h>
>
> +/*
> + * VIRGL_CHECK_VERSION available since libvirglrenderer 1.0.1 and was fixed
> + * in 1.1.0. Undefine bugged version of the macro and provide our own.
> + */
> +#if defined(VIRGL_CHECK_VERSION) && \
> + VIRGL_VERSION_MAJOR == 1 && VIRGL_VERSION_MINOR < 1
> +#undef VIRGL_CHECK_VERSION
> +#endif
> +
> +#ifndef VIRGL_CHECK_VERSION
> +#define VIRGL_CHECK_VERSION(major, minor, micro) \
> + (VIRGL_VERSION_MAJOR > (major) || \
> + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR > (minor) || \
> + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR == (minor) && \
> + VIRGL_VERSION_MICRO >= (micro))
> +#endif
> +
> +#define VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX (!VIRGL_CHECK_VERSION(1, 3, 1))
> +
> static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> struct virtio_gpu_scanout *s,
> uint32_t resource_id)
> @@ -33,6 +52,11 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
> uint32_t width, height;
> uint32_t pixels, *data;
> +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> + bool y_0_top = true;
> + int ret;
> + struct virgl_renderer_resource_info info;
> +#endif
>
> if (gl->renderer_state != RS_INITED) {
> return;
> @@ -49,8 +73,29 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> return;
> }
>
> +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> + memset(&info, 0, sizeof(info));
> + ret = virgl_renderer_resource_get_info(resource_id, &info);
> + if (ret == 0) {
> + y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
> + }
> +#endif
> +
> pixels = s->current_cursor->width * s->current_cursor->height;
> +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> + if (y_0_top) {
> + memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
> + } else {
> + uint32_t *dst = s->current_cursor->data;
> + for (uint32_t y = 0; y < height; y++) {
> + memcpy(dst + y * width,
> + data + (height - 1 - y) * width,
> + width * sizeof(uint32_t));
> + }
> + }
> +#else
> memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
> +#endif
> free(data);
> }
>
> --
> 2.51.2
>
>
> --
> Neodyme AG
> Sitz der Gesellschaft / Address: Dirnismaning 55 | Halle 13 |
> 85748 Garching b.München
> Postanschrift: Rosenthaler Straße 72a | 10119
> Berlin
>
> Registergericht / Registry court: München, HRB 269168
> Vorstand /
> Management Board: Thomas Lambertz | Tobias Madl
> Aufsichtsratsvorsitzender /
> Chairman of the Supervisory Board: Hendrik Hofstadt
>
>
>
>
>
--
Marc-André Lureau
On Mon, 27 Apr 2026 at 13:28, Marc-André Lureau <marcandre.lureau@gmail.com>
wrote:
>
> In v1, Akihiko raised a valid concern that this might be backported
> and fixed in earlier version of virgl:
>
> https://patchew.org/QEMU/20260409144639.3610-1-benno.fuenfstueck@neodyme.io/#40fb4df5-20a7-49ce-a087-d9c048255d05@rsg.ci.i.u-tokyo.ac.jp
>
>
That's a valid point, then feel free to just ignore the patch.
> > ---
> > Now with a version check, since the fix for this is in virlglrenderer
> master so I think it is any version newer than the current one (which is
> 1.3.0).
> > I copied the code from virtio-gpu-virgl.c for the version check for
> simplicty, if I should instead move that code for some DRY feel free to say
> so.
> >
> > hw/display/virtio-gpu-gl.c | 45 ++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 45 insertions(+)
> >
> > diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
> > index 2b7a41c466..6505a84f5e 100644
> > --- a/hw/display/virtio-gpu-gl.c
> > +++ b/hw/display/virtio-gpu-gl.c
> > @@ -26,6 +26,25 @@
> >
> > #include <virglrenderer.h>
> >
> > +/*
> > + * VIRGL_CHECK_VERSION available since libvirglrenderer 1.0.1 and was
> fixed
> > + * in 1.1.0. Undefine bugged version of the macro and provide our own.
> > + */
> > +#if defined(VIRGL_CHECK_VERSION) && \
> > + VIRGL_VERSION_MAJOR == 1 && VIRGL_VERSION_MINOR < 1
> > +#undef VIRGL_CHECK_VERSION
> > +#endif
> > +
> > +#ifndef VIRGL_CHECK_VERSION
> > +#define VIRGL_CHECK_VERSION(major, minor, micro) \
> > + (VIRGL_VERSION_MAJOR > (major) || \
> > + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR > (minor) ||
> \
> > + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR == (minor)
> && \
> > + VIRGL_VERSION_MICRO >= (micro))
> > +#endif
> > +
> > +#define VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX (!VIRGL_CHECK_VERSION(1, 3, 1))
> > +
> > static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> > struct virtio_gpu_scanout
> *s,
> > uint32_t resource_id)
> > @@ -33,6 +52,11 @@ static void
> virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> > VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
> > uint32_t width, height;
> > uint32_t pixels, *data;
> > +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> > + bool y_0_top = true;
> > + int ret;
> > + struct virgl_renderer_resource_info info;
> > +#endif
> >
> > if (gl->renderer_state != RS_INITED) {
> > return;
> > @@ -49,8 +73,29 @@ static void
> virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> > return;
> > }
> >
> > +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> > + memset(&info, 0, sizeof(info));
> > + ret = virgl_renderer_resource_get_info(resource_id, &info);
> > + if (ret == 0) {
> > + y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
> > + }
> > +#endif
> > +
> > pixels = s->current_cursor->width * s->current_cursor->height;
> > +#if VIRGL_NEEDS_CURSOR_DATA_FLIP_FIX
> > + if (y_0_top) {
> > + memcpy(s->current_cursor->data, data, pixels *
> sizeof(uint32_t));
> > + } else {
> > + uint32_t *dst = s->current_cursor->data;
> > + for (uint32_t y = 0; y < height; y++) {
> > + memcpy(dst + y * width,
> > + data + (height - 1 - y) * width,
> > + width * sizeof(uint32_t));
> > + }
> > + }
> > +#else
> > memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
> > +#endif
> > free(data);
> > }
> >
> > --
> > 2.51.2
> >
> >
> > --
> > Neodyme AG
> > Sitz der Gesellschaft / Address: Dirnismaning 55 | Halle 13 |
> > 85748 Garching b.München
> > Postanschrift: Rosenthaler Straße 72a | 10119
> > Berlin
> >
> > Registergericht / Registry court: München, HRB 269168
> > Vorstand /
> > Management Board: Thomas Lambertz | Tobias Madl
> > Aufsichtsratsvorsitzender /
> > Chairman of the Supervisory Board: Hendrik Hofstadt
> >
> >
> >
> >
> >
>
>
> --
> Marc-André Lureau
>
--
Neodyme AG
Sitz der Gesellschaft / Address: Dirnismaning 55 | Halle 13 |
85748 Garching b.München
Postanschrift: Rosenthaler Straße 72a | 10119
Berlin
Registergericht / Registry court: München, HRB 269168
Vorstand /
Management Board: Thomas Lambertz | Tobias Madl
Aufsichtsratsvorsitzender /
Chairman of the Supervisory Board: Hendrik Hofstadt
© 2016 - 2026 Red Hat, Inc.