From 333e7599a0d723801235f675719008ce43db93e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 19 Mar 2023 15:10:17 +0400 Subject: [PATCH 01/33] ui: return NULL when getting cursor without a console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VNC may try to get the current cursor even when there are no consoles and crashes. Simple reproducer is qemu with -nodefaults. Fixes: (again) https://gitlab.com/qemu-project/qemu/-/issues/1548 Fixes: commit 385ac97f8 ("ui: keep current cursor with QemuConsole") Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230428154807.2143652-1-marcandre.lureau@redhat.com> --- ui/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/console.c b/ui/console.c index e173731e20..7461446e71 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2306,7 +2306,7 @@ QEMUCursor *qemu_console_get_cursor(QemuConsole *con) if (con == NULL) { con = active_console; } - return con->cursor; + return con ? con->cursor : NULL; } bool qemu_console_is_visible(QemuConsole *con) From 72cbcead969de5b34709ac706a3881636010b6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 18 Apr 2023 11:32:37 +0400 Subject: [PATCH 02/33] egl: no need to lookup EGL functions manually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libepoxy handles loading the function pointer and dispatching the call, so you don't have to worry about it. Signed-off-by: Marc-André Lureau Message-Id: <20230515132455.1025608-1-marcandre.lureau@redhat.com> --- ui/egl-helpers.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 4203163ace..60385c1f48 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -439,10 +439,8 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, /* In practise any EGL 1.5 implementation would support the EXT extension */ if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) { - PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT = - (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); - if (getPlatformDisplayEXT && platform != 0) { - dpy = getPlatformDisplayEXT(platform, native, NULL); + if (platform != 0) { + dpy = eglGetPlatformDisplayEXT(platform, native, NULL); } } From 176e3783f2ab1476cdfd18a12bd2cfbe42e6b573 Mon Sep 17 00:00:00 2001 From: Antonio Caggiano Date: Mon, 12 Jun 2023 11:19:59 +0200 Subject: [PATCH 03/33] ui/sdl2: OpenGL window context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When OpenGL is enabled, create only the OpenGL context, ignoring the SDL renderer as it is unused anyway. Signed-off-by: Antonio Caggiano Reviewed-by: Marc-André Lureau Message-Id: <20230612091959.2983-1-quic_acaggian@quicinc.com> --- ui/sdl2.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index 9d703200bf..0d91b555e3 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -113,11 +113,11 @@ void sdl2_window_create(struct sdl2_console *scon) SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver); SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); - } - scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); - if (scon->opengl) { scon->winctx = SDL_GL_CreateContext(scon->real_window); + } else { + /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */ + scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); } sdl_update_caption(scon); } @@ -128,10 +128,14 @@ void sdl2_window_destroy(struct sdl2_console *scon) return; } - SDL_GL_DeleteContext(scon->winctx); - scon->winctx = NULL; - SDL_DestroyRenderer(scon->real_renderer); - scon->real_renderer = NULL; + if (scon->winctx) { + SDL_GL_DeleteContext(scon->winctx); + scon->winctx = NULL; + } + if (scon->real_renderer) { + SDL_DestroyRenderer(scon->real_renderer); + scon->real_renderer = NULL; + } SDL_DestroyWindow(scon->real_window); scon->real_window = NULL; } From b097b80bc919fca5f45663c20722e57792a093ea Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 12 Jun 2023 10:13:58 +0800 Subject: [PATCH 04/33] virtio-gpu: Optimize 2D resource data transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following points sometimes can reduce much data to copy: 1. When width matches, we can transfer data with one call of iov_to_buf(). 2. Only the required height need to transfer, not whole image. Signed-off-by: Keqian Zhu Reviewed-by: Marc-André Lureau Message-Id: <20230612021358.25068-1-zhukeqian1@huawei.com> --- hw/display/virtio-gpu.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 66cddd94d9..af31018ab0 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -438,11 +438,11 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_simple_resource *res; - int h; + int h, bpp; uint32_t src_offset, dst_offset, stride; - int bpp; pixman_format_code_t format; struct virtio_gpu_transfer_to_host_2d t2d; + void *img_data; VIRTIO_GPU_FILL_CMD(t2d); virtio_gpu_t2d_bswap(&t2d); @@ -471,23 +471,23 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, format = pixman_image_get_format(res->image); bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); stride = pixman_image_get_stride(res->image); + img_data = pixman_image_get_data(res->image); - if (t2d.offset || t2d.r.x || t2d.r.y || - t2d.r.width != pixman_image_get_width(res->image)) { - void *img_data = pixman_image_get_data(res->image); + if (t2d.r.x || t2d.r.width != pixman_image_get_width(res->image)) { for (h = 0; h < t2d.r.height; h++) { src_offset = t2d.offset + stride * h; dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp); iov_to_buf(res->iov, res->iov_cnt, src_offset, - (uint8_t *)img_data - + dst_offset, t2d.r.width * bpp); + (uint8_t *)img_data + dst_offset, + t2d.r.width * bpp); } } else { - iov_to_buf(res->iov, res->iov_cnt, 0, - pixman_image_get_data(res->image), - pixman_image_get_stride(res->image) - * pixman_image_get_height(res->image)); + src_offset = t2d.offset; + dst_offset = t2d.r.y * stride + t2d.r.x * bpp; + iov_to_buf(res->iov, res->iov_cnt, src_offset, + (uint8_t *)img_data + dst_offset, + stride * t2d.r.height); } } From 1e0c544673f43cb7ed97020cb9b3b1a915811bac Mon Sep 17 00:00:00 2001 From: Zhang Huasen Date: Thu, 15 Jun 2023 18:35:00 +0800 Subject: [PATCH 05/33] chardev/char-win-stdio: Support VT sequences on Windows 11 host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the monitor or the serial port use STDIO as backend on Windows 11 host, e.g. -nographic options is used, the monitor or the guest Linux do not response to arrow keys. When Windows creates a console, ENABLE_VIRTUAL_PROCESS_INPUT is disabled by default. Arrow keys cannot be retrieved by ReadFile or ReadConsoleInput functions. Add ENABLE_VIRTUAL_PROCESS_INPUT to the flag which is passed to SetConsoleMode, when opening stdio console. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1674 Signed-off-by: Zhang Huasen Reviewed-by: Marc-André Lureau Message-Id: --- chardev/char-win-stdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index eb830eabd9..1a18999e78 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -190,7 +190,7 @@ static void qemu_chr_open_stdio(Chardev *chr, } } - dwMode |= ENABLE_LINE_INPUT; + dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT; if (is_console) { /* set the terminal in raw mode */ From b6596785987d0174d6f413b7a5b695de6aa14483 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Mon, 19 Jun 2023 11:53:36 +0200 Subject: [PATCH 06/33] ui/touch: Move event handling to a common helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To share code between the GTK and DBus UI bakcends see the next commit for details Signed-off-by: Bilal Elmoussaoui Reviewed-by: Marc-André Lureau Message-Id: <20230619095337.9899-2-belmouss@redhat.com> --- include/ui/console.h | 15 ++++++++++ ui/console.c | 65 ++++++++++++++++++++++++++++++++++++++++++++ ui/gtk.c | 61 ++++------------------------------------- 3 files changed, 85 insertions(+), 56 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index ae5ec466c1..2093e2a3ba 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -5,6 +5,7 @@ #include "qom/object.h" #include "qemu/notify.h" #include "qapi/qapi-types-ui.h" +#include "ui/input.h" #ifdef CONFIG_OPENGL # include @@ -95,6 +96,20 @@ bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl); void kbd_put_string_console(QemuConsole *s, const char *str, int len); void kbd_put_keysym(int keysym); +/* Touch devices */ +typedef struct touch_slot { + int x; + int y; + int tracking_id; +} touch_slot; + +void console_handle_touch_event(QemuConsole *con, + struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], + uint64_t num_slot, + int width, int height, + double x, double y, + InputMultiTouchType type, + Error **errp); /* consoles */ #define TYPE_QEMU_CONSOLE "qemu-console" diff --git a/ui/console.c b/ui/console.c index 7461446e71..cfaa43e970 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1635,6 +1635,71 @@ static bool console_compatible_with(QemuConsole *con, return true; } +void console_handle_touch_event(QemuConsole *con, + struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], + uint64_t num_slot, + int width, int height, + double x, double y, + InputMultiTouchType type, + Error **errp) +{ + struct touch_slot *slot; + bool needs_sync = false; + int update; + int i; + + if (num_slot >= INPUT_EVENT_SLOTS_MAX) { + error_setg(errp, + "Unexpected touch slot number: % " PRId64" >= %d", + num_slot, INPUT_EVENT_SLOTS_MAX); + return; + } + + slot = &touch_slots[num_slot]; + slot->x = x; + slot->y = y; + + if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) { + slot->tracking_id = num_slot; + } + + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) { + if (i == num_slot) { + update = type; + } else { + update = INPUT_MULTI_TOUCH_TYPE_UPDATE; + } + + slot = &touch_slots[i]; + + if (slot->tracking_id == -1) { + continue; + } + + if (update == INPUT_MULTI_TOUCH_TYPE_END) { + slot->tracking_id = -1; + qemu_input_queue_mtt(con, update, i, slot->tracking_id); + needs_sync = true; + } else { + qemu_input_queue_mtt(con, update, i, slot->tracking_id); + qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true); + qemu_input_queue_mtt_abs(con, + INPUT_AXIS_X, (int) slot->x, + 0, width, + i, slot->tracking_id); + qemu_input_queue_mtt_abs(con, + INPUT_AXIS_Y, (int) slot->y, + 0, height, + i, slot->tracking_id); + needs_sync = true; + } + } + + if (needs_sync) { + qemu_input_event_sync(); + } +} + void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) { /* display has opengl support */ diff --git a/ui/gtk.c b/ui/gtk.c index e50f950f2b..e09e164482 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -130,11 +130,6 @@ typedef struct VCChardev VCChardev; DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, TYPE_CHARDEV_VC) -struct touch_slot { - int x; - int y; - int tracking_id; -}; static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; bool gtk_use_gl_area; @@ -1068,27 +1063,12 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, void *opaque) { VirtualConsole *vc = opaque; - struct touch_slot *slot; uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence); - bool needs_sync = false; - int update; int type = -1; - int i; - - if (num_slot >= INPUT_EVENT_SLOTS_MAX) { - warn_report("gtk: unexpected touch slot number: % " PRId64" >= %d\n", - num_slot, INPUT_EVENT_SLOTS_MAX); - return FALSE; - } - - slot = &touch_slots[num_slot]; - slot->x = touch->x; - slot->y = touch->y; switch (touch->type) { case GDK_TOUCH_BEGIN: type = INPUT_MULTI_TOUCH_TYPE_BEGIN; - slot->tracking_id = num_slot; break; case GDK_TOUCH_UPDATE: type = INPUT_MULTI_TOUCH_TYPE_UPDATE; @@ -1099,44 +1079,13 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, break; default: warn_report("gtk: unexpected touch event type\n"); + return FALSE; } - for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) { - if (i == num_slot) { - update = type; - } else { - update = INPUT_MULTI_TOUCH_TYPE_UPDATE; - } - - slot = &touch_slots[i]; - - if (slot->tracking_id == -1) { - continue; - } - - if (update == INPUT_MULTI_TOUCH_TYPE_END) { - slot->tracking_id = -1; - qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id); - needs_sync = true; - } else { - qemu_input_queue_mtt(vc->gfx.dcl.con, update, i, slot->tracking_id); - qemu_input_queue_btn(vc->gfx.dcl.con, INPUT_BUTTON_TOUCH, true); - qemu_input_queue_mtt_abs(vc->gfx.dcl.con, - INPUT_AXIS_X, (int) slot->x, - 0, surface_width(vc->gfx.ds), - i, slot->tracking_id); - qemu_input_queue_mtt_abs(vc->gfx.dcl.con, - INPUT_AXIS_Y, (int) slot->y, - 0, surface_height(vc->gfx.ds), - i, slot->tracking_id); - needs_sync = true; - } - } - - if (needs_sync) { - qemu_input_event_sync(); - } - + console_handle_touch_event(vc->gfx.dcl.con, touch_slots, + num_slot, surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds), touch->x, + touch->y, type, &error_warn); return TRUE; } From de9f844ce27ef8540bb9e8b4dd2c159b676d78a5 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Mon, 19 Jun 2023 11:53:37 +0200 Subject: [PATCH 07/33] ui/dbus: Expose a touch device interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that clients making use of the DBus backend could send touch events through the new org.qemu.Display1.Touch interface Signed-off-by: Bilal Elmoussaoui Reviewed-by: Marc-André Lureau Message-Id: <20230619095337.9899-3-belmouss@redhat.com> --- ui/dbus-console.c | 59 +++++++++++++++++++++++++++++++++++++++++++- ui/dbus-display1.xml | 45 +++++++++++++++++++++++++++++++-- ui/trace-events | 1 + 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/ui/dbus-console.c b/ui/dbus-console.c index f77bc49d2e..bc97614fec 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -32,6 +32,8 @@ #include "dbus.h" +static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX]; + struct _DBusDisplayConsole { GDBusObjectSkeleton parent_instance; DisplayChangeListener dcl; @@ -44,6 +46,7 @@ struct _DBusDisplayConsole { QKbdState *kbd; QemuDBusDisplay1Mouse *iface_mouse; + QemuDBusDisplay1MultiTouch *iface_touch; gboolean last_set; guint last_x; guint last_y; @@ -345,6 +348,46 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +static gboolean +dbus_touch_send_event(DBusDisplayConsole *ddc, + GDBusMethodInvocation *invocation, + guint kind, uint64_t num_slot, + double x, double y) +{ + Error *error = NULL; + int width, height; + trace_dbus_touch_send_event(kind, num_slot, x, y); + + if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN && + kind != INPUT_MULTI_TOUCH_TYPE_UPDATE && + kind != INPUT_MULTI_TOUCH_TYPE_CANCEL && + kind != INPUT_MULTI_TOUCH_TYPE_END) + { + g_dbus_method_invocation_return_error( + invocation, DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_INVALID, + "Invalid touch event kind"); + return DBUS_METHOD_INVOCATION_HANDLED; + } + width = qemu_console_get_width(ddc->dcl.con, 0); + height = qemu_console_get_height(ddc->dcl.con, 0); + + console_handle_touch_event(ddc->dcl.con, touch_slots, + num_slot, width, height, + x, y, kind, &error); + if (error != NULL) { + g_dbus_method_invocation_return_error( + invocation, DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_INVALID, + error_get_pretty(error), NULL); + error_free(error); + } else { + qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch, + invocation); + } + return DBUS_METHOD_INVOCATION_HANDLED; +} + static gboolean dbus_mouse_set_pos(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, @@ -440,7 +483,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) g_autofree char *label = NULL; char device_addr[256] = ""; DBusDisplayConsole *ddc; - int idx; + int idx, i; assert(display); assert(con); @@ -495,6 +538,20 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse)); + ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new(); + g_object_connect(ddc->iface_touch, + "swapped-signal::handle-send-event", dbus_touch_send_event, ddc, + NULL); + qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch, + INPUT_EVENT_SLOTS_MAX); + g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc), + G_DBUS_INTERFACE_SKELETON(ddc->iface_touch)); + + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { + struct touch_slot *slot = &touch_slots[i]; + slot->tracking_id = -1; + } + register_displaychangelistener(&ddc->dcl); ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier); diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index c3b2293376..cc0c9b68bf 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -39,8 +39,9 @@ "Text" (see :dbus:prop:`Type` and other properties). Interactions with a console may be done with - :dbus:iface:`org.qemu.Display1.Keyboard` and - :dbus:iface:`org.qemu.Display1.Mouse` interfaces when available. + :dbus:iface:`org.qemu.Display1.Keyboard`, + :dbus:iface:`org.qemu.Display1.Mouse` and + :dbus:iface:`org.qemu.Display1.MultiTouch` interfaces when available. --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -760,7 +782,13 @@ The current handler, if any, will be replaced. --> + + + + + + + + + + + + @@ -171,7 +200,8 @@ + + + + + + + + From 09b4c198b80c3f5c9c051bc8d8935668cdd206e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 6 Jun 2023 15:56:46 +0400 Subject: [PATCH 21/33] console/win32: allocate shareable display surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce qemu_win32_map_alloc() and qemu_win32_map_free() to allocate shared memory mapping. The handle can be used to share the mapping with another process. Teach qemu_create_displaysurface() to allocate shared memory. Following patches will introduce other places for shared memory allocation. Other patches for -display dbus will share the memory when possible with the client, to avoid expensive memory copy between the processes. Signed-off-by: Marc-André Lureau Message-Id: <20230606115658.677673-10-marcandre.lureau@redhat.com> --- include/sysemu/os-win32.h | 3 ++ include/ui/console.h | 8 ++++++ ui/console.c | 59 ++++++++++++++++++++++++++++++++++----- ui/qemu-pixman.c | 1 + ui/trace-events | 2 +- util/oslib-win32.c | 33 ++++++++++++++++++++++ util/trace-events | 4 +++ 7 files changed, 102 insertions(+), 8 deletions(-) diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index 65f6c9ea57..91aa0d7ec0 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -263,6 +263,9 @@ EXCEPTION_DISPOSITION win32_close_exception_handler(struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*); +void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp); +void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp); + #ifdef __cplusplus } #endif diff --git a/include/ui/console.h b/include/ui/console.h index 2093e2a3ba..2ab0c7112a 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -143,6 +143,10 @@ typedef struct DisplaySurface { GLenum gltype; GLuint texture; #endif +#ifdef WIN32 + HANDLE handle; + uint32_t handle_offset; +#endif } DisplaySurface; typedef struct QemuUIInfo { @@ -329,6 +333,10 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image); DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg); +#ifdef WIN32 +void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, + HANDLE h, uint32_t offset); +#endif PixelFormat qemu_default_pixelformat(int bpp); DisplaySurface *qemu_create_displaysurface(int width, int height); diff --git a/ui/console.c b/ui/console.c index cfaa43e970..4957110723 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1513,18 +1513,59 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, return s; } +#ifdef WIN32 +void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, + HANDLE h, uint32_t offset) +{ + assert(!surface->handle); + + surface->handle = h; + surface->handle_offset = offset; +} + +static void +win32_pixman_image_destroy(pixman_image_t *image, void *data) +{ + DisplaySurface *surface = data; + + if (!surface->handle) { + return; + } + + assert(surface->handle_offset == 0); + + qemu_win32_map_free( + pixman_image_get_data(surface->image), + surface->handle, + &error_warn + ); +} +#endif + DisplaySurface *qemu_create_displaysurface(int width, int height) { - DisplaySurface *surface = g_new0(DisplaySurface, 1); + DisplaySurface *surface; + void *bits = NULL; +#ifdef WIN32 + HANDLE handle = NULL; +#endif - trace_displaysurface_create(surface, width, height); - surface->format = PIXMAN_x8r8g8b8; - surface->image = pixman_image_create_bits(surface->format, - width, height, - NULL, width * 4); - assert(surface->image != NULL); + trace_displaysurface_create(width, height); + +#ifdef WIN32 + bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); +#endif + + surface = qemu_create_displaysurface_from( + width, height, + PIXMAN_x8r8g8b8, + width * 4, bits + ); surface->flags = QEMU_ALLOCATED_FLAG; +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(surface, handle, 0); +#endif return surface; } @@ -1540,6 +1581,10 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, width, height, (void *)data, linesize); assert(surface->image != NULL); +#ifdef WIN32 + pixman_image_set_destroy_function(surface->image, + win32_pixman_image_destroy, surface); +#endif return surface; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 3ab7e2e958..e4f024a85e 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -6,6 +6,7 @@ #include "qemu/osdep.h" #include "ui/console.h" #include "standard-headers/drm/drm_fourcc.h" +#include "trace.h" PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) { diff --git a/ui/trace-events b/ui/trace-events index 138a09cc03..a71895c479 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -9,7 +9,7 @@ console_putchar_unhandled(int ch) "unhandled escape character '%c'" console_txt_new(int w, int h) "%dx%d" console_select(int nr) "%d" console_refresh(int interval) "interval %d ms" -displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d" +displaysurface_create(int w, int h) "%dx%d" displaysurface_create_from(void *display_surface, int w, int h, uint32_t format) "surface=%p, %dx%d, format 0x%x" displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" diff --git a/util/oslib-win32.c b/util/oslib-win32.c index fafbab80b4..429542face 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -835,3 +835,36 @@ int qemu_msync(void *addr, size_t length, int fd) */ return qemu_fdatasync(fd); } + +void *qemu_win32_map_alloc(size_t size, HANDLE *h, Error **errp) +{ + void *bits; + + trace_win32_map_alloc(size); + + *h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + size, NULL); + if (*h == NULL) { + error_setg_win32(errp, GetLastError(), "Failed to CreateFileMapping"); + return NULL; + } + + bits = MapViewOfFile(*h, FILE_MAP_ALL_ACCESS, 0, 0, size); + if (bits == NULL) { + error_setg_win32(errp, GetLastError(), "Failed to MapViewOfFile"); + CloseHandle(*h); + return NULL; + } + + return bits; +} + +void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp) +{ + trace_win32_map_free(ptr, h); + + if (UnmapViewOfFile(ptr) == 0) { + error_setg_win32(errp, GetLastError(), "Failed to UnmapViewOfFile"); + } + CloseHandle(h); +} diff --git a/util/trace-events b/util/trace-events index 3f7e766683..49a4962e18 100644 --- a/util/trace-events +++ b/util/trace-events @@ -52,6 +52,10 @@ qemu_anon_ram_alloc(size_t size, void *ptr) "size %zu ptr %p" qemu_vfree(void *ptr) "ptr %p" qemu_anon_ram_free(void *ptr, size_t size) "ptr %p size %zu" +# oslib-win32.c +win32_map_alloc(size_t size) "size:%zd" +win32_map_free(void *ptr, void *h) "ptr:%p handle:%p" + # hbitmap.c hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 From 9462ff4695aa0d086fd63f7f2efafe5a05f2a243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 6 Jun 2023 15:56:47 +0400 Subject: [PATCH 22/33] virtio-gpu/win32: allocate shareable 2d resources/images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allocate pixman bits for scanouts with qemu_win32_map_alloc() so we can set a shareable handle on the associated display surface. Note: when bits are provided to pixman_image_create_bits(), you must also give the rowstride (the argument is ignored when bits is NULL) Signed-off-by: Marc-André Lureau Message-Id: <20230606115658.677673-11-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 46 +++++++++++++++++++++++++++++++--- include/hw/virtio/virtio-gpu.h | 3 +++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 1f8a5b16c6..347e17d490 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -258,6 +258,16 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, return height * stride; } +#ifdef WIN32 +static void +win32_pixman_image_destroy(pixman_image_t *image, void *data) +{ + HANDLE handle = data; + + qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); +} +#endif + static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -304,12 +314,27 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height); if (res->hostmem + g->hostmem < g->conf_max_hostmem) { + void *bits = NULL; +#ifdef WIN32 + bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); + if (!bits) { + goto end; + } +#endif res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, - NULL, 0); + bits, res->hostmem / c2d.height); +#ifdef WIN32 + if (res->image) { + pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); + } +#endif } +#ifdef WIN32 +end: +#endif if (!res->image) { qemu_log_mask(LOG_GUEST_ERROR, "%s: resource creation failed %d %d %d\n", @@ -685,6 +710,9 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g, *error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; } +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset); +#endif pixman_image_unref(rect); dpy_gfx_replace_surface(g->parent_obj.scanout[scanout_id].con, @@ -1228,6 +1256,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, struct virtio_gpu_simple_resource *res; struct virtio_gpu_scanout *scanout; uint32_t resource_id, pformat; + void *bits = NULL; int i; g->hostmem = 0; @@ -1252,15 +1281,23 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, g_free(res); return -EINVAL; } + + res->hostmem = calc_image_hostmem(pformat, res->width, res->height); +#ifdef WIN32 + bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); + if (!bits) { + g_free(res); + return -EINVAL; + } +#endif res->image = pixman_image_create_bits(pformat, res->width, res->height, - NULL, 0); + bits, res->hostmem / res->height); if (!res->image) { g_free(res); return -EINVAL; } - res->hostmem = calc_image_hostmem(pformat, res->width, res->height); res->addrs = g_new(uint64_t, res->iov_cnt); res->iov = g_new(struct iovec, res->iov_cnt); @@ -1321,6 +1358,9 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, if (!scanout->ds) { return -EINVAL; } +#ifdef WIN32 + qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); +#endif dpy_gfx_replace_surface(scanout->con, scanout->ds); dpy_gfx_update_full(scanout->con); diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 2e28507efe..7a5f8056ea 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -48,6 +48,9 @@ struct virtio_gpu_simple_resource { unsigned int iov_cnt; uint32_t scanout_bitmask; pixman_image_t *image; +#ifdef WIN32 + HANDLE handle; +#endif uint64_t hostmem; uint64_t blob_size; From 48dddba1763b2134e81ae6765b19184fd907f142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 6 Jun 2023 15:56:48 +0400 Subject: [PATCH 23/33] ui/dbus: use shared memory when possible on win32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the display surface has an associated HANDLE, we can duplicate it to the client process and let it map the memory to avoid expensive copies. Introduce two new win32-specific methods ScanoutMap and UpdateMap. The first is used to inform the listener about the a shared map availability, and the second for display updates. Signed-off-by: Marc-André Lureau Message-Id: <20230606115658.677673-12-marcandre.lureau@redhat.com> --- ui/dbus-display1.xml | 48 ++++++++++++- ui/dbus-listener.c | 165 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 208 insertions(+), 5 deletions(-) diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index 06e8779c04..7233286b28 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -370,9 +370,7 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +