From 398d1c91ed58fb7fe3dd3e9596519678c816bb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 16 Feb 2022 17:35:28 +0400 Subject: [PATCH 01/12] ui/console: move check for compatible GL context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move GL context compatibility check in dpy_compatible_with(), and use recommended error reporting. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/console.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/ui/console.c b/ui/console.c index 365a2c14b8..57e431d9e6 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1482,6 +1482,12 @@ static bool dpy_compatible_with(QemuConsole *con, flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; + if (console_has_gl(con) && con->gl->ops->compatible_dcl != dcl->ops) { + error_setg(errp, "Display %s is incompatible with the GL context", + dcl->ops->dpy_name); + return false; + } + if (flags & GRAPHIC_FLAGS_GL && !console_has_gl(con)) { error_setg(errp, "The console requires a GL context."); @@ -1509,27 +1515,12 @@ void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) con->gl = gl; } -static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl) -{ - if (!con->gl) { - return true; - } - - return con->gl->ops->compatible_dcl == dcl->ops; -} - void register_displaychangelistener(DisplayChangeListener *dcl) { QemuConsole *con; assert(!dcl->ds); - if (dcl->con && !dpy_gl_compatible_with(dcl->con, dcl)) { - error_report("Display %s is incompatible with the GL context", - dcl->ops->dpy_name); - exit(1); - } - if (dcl->con) { dpy_compatible_with(dcl->con, dcl, &error_fatal); } From a62c4a178fb154a3b810870502cb4c63a6b4cf28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 16 Feb 2022 19:33:37 +0400 Subject: [PATCH 02/12] ui/console: move dcl compatiblity check to a callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As expected from the "compatible_dcl" comment, a simple comparison of ops isn't enough. The following patch will fix a regression introduced by this limited check by extending the compatibility callback for egl-headless. For now, this patch simply replaces the the "compatible_dcl" ops pointer with a "dpy_gl_ctx_is_compatible_ctx" callback. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- include/ui/console.h | 9 ++------- ui/console.c | 3 ++- ui/dbus.c | 9 ++++++++- ui/egl-headless.c | 9 ++++++++- ui/gtk.c | 18 ++++++++++++++++-- ui/sdl2.c | 9 ++++++++- ui/spice-display.c | 9 ++++++++- 7 files changed, 52 insertions(+), 14 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index f590819880..18a10c0b7d 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -282,13 +282,8 @@ struct DisplayChangeListener { }; typedef struct DisplayGLCtxOps { - /* - * We only check if the GLCtx is compatible with a DCL via ops. A natural - * evolution of this would be a callback to check some runtime requirements - * and allow various DCL kinds. - */ - const DisplayChangeListenerOps *compatible_dcl; - + bool (*dpy_gl_ctx_is_compatible_dcl)(DisplayGLCtx *dgc, + DisplayChangeListener *dcl); QEMUGLContext (*dpy_gl_ctx_create)(DisplayGLCtx *dgc, QEMUGLParams *params); void (*dpy_gl_ctx_destroy)(DisplayGLCtx *dgc, diff --git a/ui/console.c b/ui/console.c index 57e431d9e6..c931855287 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1482,7 +1482,8 @@ static bool dpy_compatible_with(QemuConsole *con, flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; - if (console_has_gl(con) && con->gl->ops->compatible_dcl != dcl->ops) { + if (console_has_gl(con) && + !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { error_setg(errp, "Display %s is incompatible with the GL context", dcl->ops->dpy_name); return false; diff --git a/ui/dbus.c b/ui/dbus.c index 0074424c1f..f00a44421c 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -48,8 +48,15 @@ static QEMUGLContext dbus_create_context(DisplayGLCtx *dgc, return qemu_egl_create_context(dgc, params); } +static bool +dbus_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dbus_gl_dcl_ops; +} + static const DisplayGLCtxOps dbus_gl_ops = { - .compatible_dcl = &dbus_gl_dcl_ops, + .dpy_gl_ctx_is_compatible_dcl = dbus_is_compatible_dcl, .dpy_gl_ctx_create = dbus_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 94082a9da9..9aff115280 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -166,8 +166,15 @@ static const DisplayChangeListenerOps egl_ops = { .dpy_gl_update = egl_scanout_flush, }; +static bool +egl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &egl_ops; +} + static const DisplayGLCtxOps eglctx_ops = { - .compatible_dcl = &egl_ops, + .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl, .dpy_gl_ctx_create = egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, diff --git a/ui/gtk.c b/ui/gtk.c index a8567b9ddc..1b24a67d79 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -614,8 +614,15 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_has_dmabuf = gd_has_dmabuf, }; +static bool +gd_gl_area_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_gl_area_ops; +} + static const DisplayGLCtxOps gl_area_ctx_ops = { - .compatible_dcl = &dcl_gl_area_ops, + .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl, .dpy_gl_ctx_create = gd_gl_area_create_context, .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, .dpy_gl_ctx_make_current = gd_gl_area_make_current, @@ -641,8 +648,15 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_has_dmabuf = gd_has_dmabuf, }; +static bool +gd_egl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_egl_ops; +} + static const DisplayGLCtxOps egl_ctx_ops = { - .compatible_dcl = &dcl_egl_ops, + .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl, .dpy_gl_ctx_create = gd_egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = gd_egl_make_current, diff --git a/ui/sdl2.c b/ui/sdl2.c index 46a252d7d9..d3741f9b75 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -788,8 +788,15 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_gl_update = sdl2_gl_scanout_flush, }; +static bool +sdl2_gl_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &dcl_gl_ops; +} + static const DisplayGLCtxOps gl_ctx_ops = { - .compatible_dcl = &dcl_gl_ops, + .dpy_gl_ctx_is_compatible_dcl = sdl2_gl_is_compatible_dcl, .dpy_gl_ctx_create = sdl2_gl_create_context, .dpy_gl_ctx_destroy = sdl2_gl_destroy_context, .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, diff --git a/ui/spice-display.c b/ui/spice-display.c index a3078adf91..494168e7fe 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1125,8 +1125,15 @@ static const DisplayChangeListenerOps display_listener_gl_ops = { .dpy_gl_update = qemu_spice_gl_update, }; +static bool +qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc, + DisplayChangeListener *dcl) +{ + return dcl->ops == &display_listener_gl_ops; +} + static const DisplayGLCtxOps gl_ctx_ops = { - .compatible_dcl = &display_listener_gl_ops, + .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl, .dpy_gl_ctx_create = qemu_spice_gl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, From cd19c25fbfaeee8865a2f8dcb532d758615bc8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 16 Feb 2022 19:42:40 +0400 Subject: [PATCH 03/12] ui/console: egl-headless is compatible with non-gl listeners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a regression introduced by commit 5e79d516e ("ui: split the GL context in a different object"). Reported-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/egl-headless.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 9aff115280..7a30fd9777 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -170,6 +170,14 @@ static bool egl_is_compatible_dcl(DisplayGLCtx *dgc, DisplayChangeListener *dcl) { + if (!dcl->ops->dpy_gl_update) { + /* + * egl-headless is compatible with all 2d listeners, as it blits the GL + * updates on the 2d console surface. + */ + return true; + } + return dcl->ops == &egl_ops; } From 417a231947fb19b842fec3922713ffe3142b2c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 16 Feb 2022 22:52:14 +0400 Subject: [PATCH 04/12] ui/dbus: associate the DBusDisplayConsole listener with the given console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DBusDisplayConsole is specific to a given QemuConsole. Fixes: commit 142ca628 ("ui: add a D-Bus display backend") Reported-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/dbus-console.c | 27 +++++++++++++-------------- ui/dbus.c | 2 +- ui/dbus.h | 3 +++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/ui/dbus-console.c b/ui/dbus-console.c index e062f721d7..898a4ac8a5 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -36,7 +36,6 @@ struct _DBusDisplayConsole { DisplayChangeListener dcl; DBusDisplay *display; - QemuConsole *con; GHashTable *listeners; QemuDBusDisplay1Console *iface; @@ -118,7 +117,7 @@ dbus_gl_scanout_update(DisplayChangeListener *dcl, { } -static const DisplayChangeListenerOps dbus_console_dcl_ops = { +const DisplayChangeListenerOps dbus_console_dcl_ops = { .dpy_name = "dbus-console", .dpy_gfx_switch = dbus_gfx_switch, .dpy_gfx_update = dbus_gfx_update, @@ -191,7 +190,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, .height = arg_height, }; - if (!dpy_ui_info_supported(ddc->con)) { + if (!dpy_ui_info_supported(ddc->dcl.con)) { g_dbus_method_invocation_return_error(invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_UNSUPPORTED, @@ -199,7 +198,7 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - dpy_set_ui_info(ddc->con, &info, false); + dpy_set_ui_info(ddc->dcl.con, &info, false); qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation); return DBUS_METHOD_INVOCATION_HANDLED; } @@ -335,8 +334,8 @@ dbus_mouse_rel_motion(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - qemu_input_queue_rel(ddc->con, INPUT_AXIS_X, dx); - qemu_input_queue_rel(ddc->con, INPUT_AXIS_Y, dy); + qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx); + qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse, @@ -362,8 +361,8 @@ dbus_mouse_set_pos(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - width = qemu_console_get_width(ddc->con, 0); - height = qemu_console_get_height(ddc->con, 0); + width = qemu_console_get_width(ddc->dcl.con, 0); + height = qemu_console_get_height(ddc->dcl.con, 0); if (x >= width || y >= height) { g_dbus_method_invocation_return_error( invocation, DBUS_DISPLAY_ERROR, @@ -371,8 +370,8 @@ dbus_mouse_set_pos(DBusDisplayConsole *ddc, "Invalid mouse position"); return DBUS_METHOD_INVOCATION_HANDLED; } - qemu_input_queue_abs(ddc->con, INPUT_AXIS_X, x, 0, width); - qemu_input_queue_abs(ddc->con, INPUT_AXIS_Y, y, 0, height); + qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width); + qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse, @@ -388,7 +387,7 @@ dbus_mouse_press(DBusDisplayConsole *ddc, { trace_dbus_mouse_press(button); - qemu_input_queue_btn(ddc->con, button, true); + qemu_input_queue_btn(ddc->dcl.con, button, true); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation); @@ -403,7 +402,7 @@ dbus_mouse_release(DBusDisplayConsole *ddc, { trace_dbus_mouse_release(button); - qemu_input_queue_btn(ddc->con, button, false); + qemu_input_queue_btn(ddc->dcl.con, button, false); qemu_input_event_sync(); qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation); @@ -424,7 +423,7 @@ dbus_mouse_mode_change(Notifier *notify, void *data) int dbus_display_console_get_index(DBusDisplayConsole *ddc) { - return qemu_console_get_index(ddc->con); + return qemu_console_get_index(ddc->dcl.con); } DBusDisplayConsole * @@ -446,7 +445,7 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) "g-object-path", path, NULL); ddc->display = display; - ddc->con = con; + ddc->dcl.con = con; /* handle errors, and skip non graphics? */ qemu_console_fill_device_address( con, device_addr, sizeof(device_addr), NULL); diff --git a/ui/dbus.c b/ui/dbus.c index f00a44421c..22c82d2f32 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -52,7 +52,7 @@ static bool dbus_is_compatible_dcl(DisplayGLCtx *dgc, DisplayChangeListener *dcl) { - return dcl->ops == &dbus_gl_dcl_ops; + return dcl->ops == &dbus_gl_dcl_ops || dcl->ops == &dbus_console_dcl_ops; } static const DisplayGLCtxOps dbus_gl_ops = { diff --git a/ui/dbus.h b/ui/dbus.h index 64c77cab44..5f5c1f759c 100644 --- a/ui/dbus.h +++ b/ui/dbus.h @@ -79,6 +79,9 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con); int dbus_display_console_get_index(DBusDisplayConsole *ddc); + +extern const DisplayChangeListenerOps dbus_console_dcl_ops; + #define DBUS_DISPLAY_TYPE_LISTENER dbus_display_listener_get_type() G_DECLARE_FINAL_TYPE(DBusDisplayListener, dbus_display_listener, From 4b7b661d8feccb8d12289bce7814e89ef827ecce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 16 Feb 2022 20:16:55 +0400 Subject: [PATCH 05/12] ui/console: move console compatibility check to dcl_display_console() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current checks are done at registration time only. However, if a DCL has no specific console specified, it may be switched dynamically with console_select() later on. Let's move the checks when displaychangelistener_display_console() is called, which includes registration time and remains fatal if the specified console is incompatible. Note: we may want to display the compatibility error to the DCL, this is left for a future improvement. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/console.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ui/console.c b/ui/console.c index c931855287..d3ecbb2157 100644 --- a/ui/console.c +++ b/ui/console.c @@ -148,6 +148,8 @@ static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); +static bool console_compatible_with(QemuConsole *con, + DisplayChangeListener *dcl, Error **errp); static void gui_update(void *opaque) { @@ -1057,13 +1059,14 @@ static void console_putchar(QemuConsole *s, int ch) } static void displaychangelistener_display_console(DisplayChangeListener *dcl, - QemuConsole *con) + QemuConsole *con, + Error **errp) { static const char nodev[] = "This VM has no graphic display device."; static DisplaySurface *dummy; - if (!con) { + if (!con || !console_compatible_with(con, dcl, errp)) { if (!dcl->ops->dpy_gfx_switch) { return; } @@ -1114,7 +1117,7 @@ void console_select(unsigned int index) if (dcl->con != NULL) { continue; } - displaychangelistener_display_console(dcl, s); + displaychangelistener_display_console(dcl, s, NULL); } } if (ds->have_text) { @@ -1475,8 +1478,8 @@ static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) return false; } -static bool dpy_compatible_with(QemuConsole *con, - DisplayChangeListener *dcl, Error **errp) +static bool console_compatible_with(QemuConsole *con, + DisplayChangeListener *dcl, Error **errp) { int flags; @@ -1522,10 +1525,6 @@ void register_displaychangelistener(DisplayChangeListener *dcl) assert(!dcl->ds); - if (dcl->con) { - dpy_compatible_with(dcl->con, dcl, &error_fatal); - } - trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); dcl->ds = get_alloc_displaystate(); QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); @@ -1536,7 +1535,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl) } else { con = active_console; } - displaychangelistener_display_console(dcl, con); + displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); text_console_update_cursor(NULL); } From 8fe496adacc03fe37fba6765d34618a390fabdc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 17 Feb 2022 11:36:45 +0400 Subject: [PATCH 06/12] ui/shader: fix potential leak of shader on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Value of 0 for program and shaders are silently ignored and indicate error. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/shader.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/shader.c b/ui/shader.c index e8b8d321b7..4c80fc831f 100644 --- a/ui/shader.c +++ b/ui/shader.c @@ -130,15 +130,17 @@ static GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag) static GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src, const GLchar *frag_src) { - GLuint vert_shader, frag_shader, program; + GLuint vert_shader, frag_shader, program = 0; vert_shader = qemu_gl_create_compile_shader(GL_VERTEX_SHADER, vert_src); frag_shader = qemu_gl_create_compile_shader(GL_FRAGMENT_SHADER, frag_src); if (!vert_shader || !frag_shader) { - return 0; + goto end; } program = qemu_gl_create_link_program(vert_shader, frag_shader); + +end: glDeleteShader(vert_shader); glDeleteShader(frag_shader); From 532042d57387d9144f38498a7210a5a50bb9bc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 17 Feb 2022 11:37:47 +0400 Subject: [PATCH 07/12] ui/shader: free associated programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/shader.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/shader.c b/ui/shader.c index 4c80fc831f..ab448c41d4 100644 --- a/ui/shader.c +++ b/ui/shader.c @@ -172,5 +172,8 @@ void qemu_gl_fini_shader(QemuGLShader *gls) if (!gls) { return; } + glDeleteProgram(gls->texture_blit_prog); + glDeleteProgram(gls->texture_blit_flip_prog); + glDeleteProgram(gls->texture_blit_vao); g_free(gls); } From 26b032b9b746782a1ee5156a70cc074b6de5af0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 17 Feb 2022 12:42:20 +0400 Subject: [PATCH 08/12] ui/console: add a dpy_gfx_switch callback helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slight code improvement. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/console.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ui/console.c b/ui/console.c index d3ecbb2157..102fcf0a50 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1058,6 +1058,15 @@ static void console_putchar(QemuConsole *s, int ch) } } +static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, + struct DisplaySurface *new_surface) +{ + if (dcl->ops->dpy_gfx_switch) { + dcl->ops->dpy_gfx_switch(dcl, new_surface); + } +} + + static void displaychangelistener_display_console(DisplayChangeListener *dcl, QemuConsole *con, Error **errp) @@ -1067,13 +1076,10 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, static DisplaySurface *dummy; if (!con || !console_compatible_with(con, dcl, errp)) { - if (!dcl->ops->dpy_gfx_switch) { - return; - } if (!dummy) { dummy = qemu_create_placeholder_surface(640, 480, nodev); } - dcl->ops->dpy_gfx_switch(dcl, dummy); + displaychangelistener_gfx_switch(dcl, dummy); return; } @@ -1091,9 +1097,8 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.y, con->scanout.texture.width, con->scanout.texture.height); - } else if (con->scanout.kind == SCANOUT_SURFACE && - dcl->ops->dpy_gfx_switch) { - dcl->ops->dpy_gfx_switch(dcl, con->surface); + } else if (con->scanout.kind == SCANOUT_SURFACE) { + displaychangelistener_gfx_switch(dcl, con->surface); } dcl->ops->dpy_gfx_update(dcl, 0, 0, @@ -1677,9 +1682,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, if (con != (dcl->con ? dcl->con : active_console)) { continue; } - if (dcl->ops->dpy_gfx_switch) { - dcl->ops->dpy_gfx_switch(dcl, surface); - } + displaychangelistener_gfx_switch(dcl, surface); } qemu_free_displaysurface(old_surface); } From c84ab0a500a84851debd1d4485e72325da98c778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 20 Feb 2022 23:31:58 +0400 Subject: [PATCH 09/12] ui/console: optionally update after gfx switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When switching to the dummy surface, we should also call gfx_update. But when using GL, we shouldn't call it. By making it an argument to displaychangelistener_gfx_switch(), it will be explicit, and cannot be forgotten that easily. Fixes: commit ebced091 ("console: save current scanout details") Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/console.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ui/console.c b/ui/console.c index 102fcf0a50..06ba82db61 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1059,11 +1059,18 @@ static void console_putchar(QemuConsole *s, int ch) } static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, - struct DisplaySurface *new_surface) + struct DisplaySurface *new_surface, + bool update) { if (dcl->ops->dpy_gfx_switch) { dcl->ops->dpy_gfx_switch(dcl, new_surface); } + + if (update && dcl->ops->dpy_gfx_update) { + dcl->ops->dpy_gfx_update(dcl, 0, 0, + surface_width(new_surface), + surface_height(new_surface)); + } } @@ -1079,7 +1086,7 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, if (!dummy) { dummy = qemu_create_placeholder_surface(640, 480, nodev); } - displaychangelistener_gfx_switch(dcl, dummy); + displaychangelistener_gfx_switch(dcl, dummy, TRUE); return; } @@ -1098,12 +1105,8 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.width, con->scanout.texture.height); } else if (con->scanout.kind == SCANOUT_SURFACE) { - displaychangelistener_gfx_switch(dcl, con->surface); + displaychangelistener_gfx_switch(dcl, con->surface, TRUE); } - - dcl->ops->dpy_gfx_update(dcl, 0, 0, - qemu_console_get_width(con, 0), - qemu_console_get_height(con, 0)); } void console_select(unsigned int index) @@ -1682,7 +1685,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, if (con != (dcl->con ? dcl->con : active_console)) { continue; } - displaychangelistener_gfx_switch(dcl, surface); + displaychangelistener_gfx_switch(dcl, surface, FALSE); } qemu_free_displaysurface(old_surface); } From 589089feee5bf5110f21fd582f9c2c479ae718a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 17 Feb 2022 15:07:21 +0400 Subject: [PATCH 10/12] ui/dbus: fix texture sharing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DBus listener naively create, update and destroy textures without taking into account other listeners. The texture were shared, but texture update was unnecessarily duplicated. Teach DisplayGLCtx to do optionally shared texture handling. This is only implemented for DBus display at this point, however the same infrastructure could potentially be used for other future combinations. Reported-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- include/ui/console.h | 10 ++++++++++ ui/console.c | 28 ++++++++++++++++++++++++++++ ui/dbus-listener.c | 11 ----------- ui/dbus.c | 26 ++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 18a10c0b7d..0f84861933 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -290,10 +290,20 @@ typedef struct DisplayGLCtxOps { QEMUGLContext ctx); int (*dpy_gl_ctx_make_current)(DisplayGLCtx *dgc, QEMUGLContext ctx); + void (*dpy_gl_ctx_create_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface); + void (*dpy_gl_ctx_destroy_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface); + void (*dpy_gl_ctx_update_texture)(DisplayGLCtx *dgc, + DisplaySurface *surface, + int x, int y, int w, int h); } DisplayGLCtxOps; struct DisplayGLCtx { const DisplayGLCtxOps *ops; +#ifdef CONFIG_OPENGL + QemuGLShader *gls; /* optional shared shader */ +#endif }; DisplayState *init_displaystate(void); diff --git a/ui/console.c b/ui/console.c index 06ba82db61..3b56645356 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1073,6 +1073,27 @@ static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, } } +static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) { + con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface); + } +} + +static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) { + con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface); + } +} + +static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface, + int x, int y, int w, int h) +{ + if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) { + con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h); + } +} static void displaychangelistener_display_console(DisplayChangeListener *dcl, QemuConsole *con, @@ -1086,6 +1107,9 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, if (!dummy) { dummy = qemu_create_placeholder_surface(640, 480, nodev); } + if (con) { + dpy_gfx_create_texture(con, dummy); + } displaychangelistener_gfx_switch(dcl, dummy, TRUE); return; } @@ -1105,6 +1129,7 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.width, con->scanout.texture.height); } else if (con->scanout.kind == SCANOUT_SURFACE) { + dpy_gfx_create_texture(con, con->surface); displaychangelistener_gfx_switch(dcl, con->surface, TRUE); } } @@ -1637,6 +1662,7 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) if (!qemu_console_is_visible(con)) { return; } + dpy_gfx_update_texture(con, con->surface, x, y, w, h); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; @@ -1681,12 +1707,14 @@ void dpy_gfx_replace_surface(QemuConsole *con, con->scanout.kind = SCANOUT_SURFACE; con->surface = surface; + dpy_gfx_create_texture(con, surface); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; } displaychangelistener_gfx_switch(dcl, surface, FALSE); } + dpy_gfx_destroy_texture(con, old_surface); qemu_free_displaysurface(old_surface); } diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 81c119b13a..a287edd2fc 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -42,7 +42,6 @@ struct _DBusDisplayListener { DisplayChangeListener dcl; DisplaySurface *ds; - QemuGLShader *gls; int gl_updates; }; @@ -240,10 +239,6 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - if (ddl->ds) { - surface_gl_update_texture(ddl->gls, ddl->ds, x, y, w, h); - } - ddl->gl_updates++; } @@ -285,15 +280,11 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - if (ddl->ds) { - surface_gl_destroy_texture(ddl->gls, ddl->ds); - } ddl->ds = new_surface; if (ddl->ds) { int width = surface_width(ddl->ds); int height = surface_height(ddl->ds); - surface_gl_create_texture(ddl->gls, ddl->ds); /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, width, height, 0, 0, width, height); @@ -403,7 +394,6 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->conn); g_clear_pointer(&ddl->bus_name, g_free); g_clear_object(&ddl->proxy); - g_clear_pointer(&ddl->gls, qemu_gl_fini_shader); G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); } @@ -414,7 +404,6 @@ dbus_display_listener_constructed(GObject *object) DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object); if (display_opengl) { - ddl->gls = qemu_gl_init_shader(); ddl->dcl.ops = &dbus_gl_dcl_ops; } else { ddl->dcl.ops = &dbus_dcl_ops; diff --git a/ui/dbus.c b/ui/dbus.c index 22c82d2f32..7a87612379 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -55,11 +55,33 @@ dbus_is_compatible_dcl(DisplayGLCtx *dgc, return dcl->ops == &dbus_gl_dcl_ops || dcl->ops == &dbus_console_dcl_ops; } +static void +dbus_create_texture(DisplayGLCtx *ctx, DisplaySurface *surface) +{ + surface_gl_create_texture(ctx->gls, surface); +} + +static void +dbus_destroy_texture(DisplayGLCtx *ctx, DisplaySurface *surface) +{ + surface_gl_destroy_texture(ctx->gls, surface); +} + +static void +dbus_update_texture(DisplayGLCtx *ctx, DisplaySurface *surface, + int x, int y, int w, int h) +{ + surface_gl_update_texture(ctx->gls, surface, x, y, w, h); +} + static const DisplayGLCtxOps dbus_gl_ops = { .dpy_gl_ctx_is_compatible_dcl = dbus_is_compatible_dcl, .dpy_gl_ctx_create = dbus_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, + .dpy_gl_ctx_create_texture = dbus_create_texture, + .dpy_gl_ctx_destroy_texture = dbus_destroy_texture, + .dpy_gl_ctx_update_texture = dbus_update_texture, }; static NotifierList dbus_display_notifiers = @@ -90,6 +112,9 @@ dbus_display_init(Object *o) g_autoptr(GDBusObjectSkeleton) vm = NULL; dd->glctx.ops = &dbus_gl_ops; + if (display_opengl) { + dd->glctx.gls = qemu_gl_init_shader(); + } dd->iface = qemu_dbus_display1_vm_skeleton_new(); dd->consoles = g_ptr_array_new_with_free_func(g_object_unref); @@ -126,6 +151,7 @@ dbus_display_finalize(Object *o) g_clear_object(&dd->iface); g_free(dd->dbus_addr); g_free(dd->audiodev); + g_clear_pointer(&dd->glctx.gls, qemu_gl_fini_shader); dbus_display = NULL; } From 2fa2386e38387f592a0abf871eabb16e7a43dcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 20 Feb 2022 23:57:00 +0400 Subject: [PATCH 11/12] ui/dbus: do not send 2d scanout until gfx_update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gfx_switch() is called to set the new_surface, not necessarily to display it. It should be displayed after gfx_update(). Send the whole scanout only in this case. Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/dbus-listener.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index a287edd2fc..f9fc8eda51 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -255,6 +255,26 @@ static void dbus_gfx_update(DisplayChangeListener *dcl, trace_dbus_update(x, y, w, h); + if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { + v_data = g_variant_new_from_data( + G_VARIANT_TYPE("ay"), + surface_data(ddl->ds), + surface_stride(ddl->ds) * surface_height(ddl->ds), + TRUE, + (GDestroyNotify)pixman_image_unref, + pixman_image_ref(ddl->ds->image)); + qemu_dbus_display1_listener_call_scanout( + ddl->proxy, + surface_width(ddl->ds), + surface_height(ddl->ds), + surface_stride(ddl->ds), + surface_format(ddl->ds), + v_data, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); + return; + } + /* make a copy, since gvariant only handles linear data */ img = pixman_image_create_bits(surface_format(ddl->ds), w, h, NULL, stride); @@ -295,29 +315,12 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); - GVariant *v_data = NULL; ddl->ds = new_surface; if (!ddl->ds) { /* why not call disable instead? */ return; } - - v_data = g_variant_new_from_data( - G_VARIANT_TYPE("ay"), - surface_data(ddl->ds), - surface_stride(ddl->ds) * surface_height(ddl->ds), - TRUE, - (GDestroyNotify)pixman_image_unref, - pixman_image_ref(ddl->ds->image)); - qemu_dbus_display1_listener_call_scanout(ddl->proxy, - surface_width(ddl->ds), - surface_height(ddl->ds), - surface_stride(ddl->ds), - surface_format(ddl->ds), - v_data, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); } static void dbus_mouse_set(DisplayChangeListener *dcl, From e1c676a254b012779db87166a1f26db6886a8bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 20 Feb 2022 23:45:38 +0400 Subject: [PATCH 12/12] ui/console: call gfx_switch() even if the current scanout is GL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit egl-headless depends on the backing surface to be set before texture are set and updated. Display it (update=true) iff the current scanout kind is SURFACE. Reported-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Acked-by: Gerd Hoffmann --- ui/console.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/console.c b/ui/console.c index 3b56645356..da434ce1b2 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1114,6 +1114,10 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, return; } + dpy_gfx_create_texture(con, con->surface); + displaychangelistener_gfx_switch(dcl, con->surface, + con->scanout.kind == SCANOUT_SURFACE); + if (con->scanout.kind == SCANOUT_DMABUF && displaychangelistener_has_dmabuf(dcl)) { dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf); @@ -1128,9 +1132,6 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, con->scanout.texture.y, con->scanout.texture.width, con->scanout.texture.height); - } else if (con->scanout.kind == SCANOUT_SURFACE) { - dpy_gfx_create_texture(con, con->surface); - displaychangelistener_gfx_switch(dcl, con->surface, TRUE); } }