With a display option, "show-fps=on", qemu adds a status bar and print following performance numbers on the bar,
ups = update per seconds - the rate the guest scanout is updated. fps = frame per seconds - the frame rate of VC's GL drawing area One function, gd_gl_count_frame is added to count # frames and calculate ups and fps every 100 frames or guest scanout updates. Signed-off-by: Dongwon Kim <dongwon....@intel.com> --- include/ui/console.h | 4 +++- include/ui/gtk.h | 2 ++ qapi/ui.json | 6 +++++- ui/console.c | 6 ++++++ ui/gtk.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index b30b63976a..3ca8d07220 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -243,7 +243,8 @@ typedef struct DisplayChangeListenerOps { /* required if GL */ void (*dpy_gl_update)(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); - + /* optional */ + void (*dpy_gl_count_frame)(DisplayChangeListener *dcl, bool ups); } DisplayChangeListenerOps; struct DisplayChangeListener { @@ -314,6 +315,7 @@ void dpy_gl_release_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf); void dpy_gl_update(QemuConsole *con, uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void dpy_gl_count_frame(QemuConsole *con, bool ups); QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, QEMUGLParams *params); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 9516670ebc..8d1a6c2cef 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -118,6 +118,7 @@ struct GtkDisplayState { GtkWidget *show_tabs_item; GtkWidget *untabify_item; GtkWidget *show_menubar_item; + GtkWidget *status_bar; GtkWidget *vbox; GtkWidget *notebook; @@ -152,6 +153,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_gl_count_frame(DisplayChangeListener *dcl, bool ups); /* ui/gtk-egl.c */ void gd_egl_init(VirtualConsole *vc); diff --git a/qapi/ui.json b/qapi/ui.json index 1052ca9c38..a0882d6428 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1035,13 +1035,17 @@ # assuming the guest will resize the display to match # the window size then. Otherwise it defaults to "off". # Since 3.1 +# @show-fps: Enable showing Guest Scanout's update rate (UPS) and +# Surface render swap rate (FPS) on a status bar (default: off). +# Since 6.0 # # Since: 2.12 # ## { 'struct' : 'DisplayGTK', 'data' : { '*grab-on-hover' : 'bool', - '*zoom-to-fit' : 'bool' } } + '*zoom-to-fit' : 'bool', + '*show-fps' : 'bool' } } ## # @DisplayEGLHeadless: diff --git a/ui/console.c b/ui/console.c index 2de5f4105b..31efdb0512 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1924,6 +1924,12 @@ void dpy_gl_update(QemuConsole *con, con->gl->ops->dpy_gl_update(con->gl, x, y, w, h); } +void dpy_gl_count_frame(QemuConsole *con, bool ups) +{ + assert(con->gl); + con->gl->ops->dpy_gl_count_frame(con->gl, ups); +} + /***********************************************************/ /* register display */ diff --git a/ui/gtk.c b/ui/gtk.c index 98046f577b..091da08028 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -549,6 +549,47 @@ static void gd_switch(DisplayChangeListener *dcl, } } +void gd_gl_count_frame(DisplayChangeListener *dcl, bool ups) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + gchar ups_fps_str[100]; + static guint prev, curr; + static guint ups_cnt, fps_cnt, status_bar_id; + struct timeval tv; + + if (!vc->s->opts->show_fps) { + return; + } + + if (prev == 0) { + gettimeofday(&tv, NULL); + prev = tv.tv_sec * 1000000 + tv.tv_usec; + } + + if (ups) { + ups_cnt++; + } else { + fps_cnt++; + } + + /* update rate is calculated for every 200 frames */ + if (ups_cnt == 200 || fps_cnt == 200) { + gettimeofday(&tv, NULL); + curr = tv.tv_sec * 1000000 + tv.tv_usec; + prev = curr - prev; + sprintf(ups_fps_str, "UPS : %0.2f u/s FPS : %0.2f f/s", + ups_cnt * 1000000/(gfloat)prev, fps_cnt * 1000000/(gfloat)prev); + + status_bar_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(vc->s->status_bar), + "ups_fps_info"); + gtk_statusbar_pop(GTK_STATUSBAR(vc->s->status_bar), status_bar_id); + gtk_statusbar_push(GTK_STATUSBAR(vc->s->status_bar), status_bar_id, ups_fps_str); + prev = curr; + fps_cnt = 0; + ups_cnt = 0; + } +} + static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "gtk", .dpy_gfx_update = gd_update, @@ -594,6 +635,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_has_dmabuf = gd_has_dmabuf, + .dpy_gl_count_frame = gd_gl_count_frame, }; #ifdef CONFIG_X11 @@ -618,6 +660,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_gl_release_dmabuf = gd_egl_release_dmabuf, .dpy_gl_update = gd_egl_scanout_flush, .dpy_has_dmabuf = gd_has_dmabuf, + .dpy_gl_count_frame = gd_gl_count_frame, }; #endif @@ -2208,6 +2251,10 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) s->notebook = gtk_notebook_new(); s->menu_bar = gtk_menu_bar_new(); + if (opts->show_fps) { + s->status_bar = gtk_statusbar_new(); + } + s->free_scale = FALSE; /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For @@ -2248,6 +2295,10 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); + if (opts->show_fps) { + gtk_box_pack_start(GTK_BOX(s->vbox), s->status_bar, FALSE, TRUE, 0); + } + gtk_container_add(GTK_CONTAINER(s->window), s->vbox); gtk_widget_show_all(s->window); -- 2.17.1