@@ -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);
@@ -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);
@@ -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:
@@ -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 */
@@ -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);
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.kim@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(-)