diff options
author | Benjamin Otte <otte@redhat.com> | 2018-04-05 12:58:46 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2018-04-05 14:58:43 +0200 |
commit | a5cb6aa36576d5441cbb1b68d3e920f2eecc6aa3 (patch) | |
tree | 7715d91fbe7329d45337e7064900336dd7ccd304 | |
parent | 6de4c63a67ba8b200ee9c79d985d9fa2c252a279 (diff) | |
download | gtk+-wip/otte/snapshot.tar.gz |
inspector: Add an fps overlaywip/otte/snapshot
-rw-r--r-- | gtk/inspector/fpsoverlay.c | 286 | ||||
-rw-r--r-- | gtk/inspector/fpsoverlay.h | 34 | ||||
-rw-r--r-- | gtk/inspector/meson.build | 1 | ||||
-rw-r--r-- | gtk/inspector/visual.c | 40 | ||||
-rw-r--r-- | gtk/inspector/visual.ui | 27 |
5 files changed, 386 insertions, 2 deletions
diff --git a/gtk/inspector/fpsoverlay.c b/gtk/inspector/fpsoverlay.c new file mode 100644 index 0000000000..a3764099af --- /dev/null +++ b/gtk/inspector/fpsoverlay.c @@ -0,0 +1,286 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#include "config.h" + +#include "fpsoverlay.h" + +#include "gtkintl.h" +#include "gtkwidget.h" +#include "gtkwindow.h" + +/* duration before we start fading in us */ +#define GDK_FPS_OVERLAY_LINGER_DURATION (1000 * 1000) +/* duration when fade is finished in us */ +#define GDK_FPS_OVERLAY_FADE_DURATION (500 * 1000) + +typedef struct _GtkFpsInfo { + gint64 last_frame; + GskRenderNode *last_node; +} GtkFpsInfo; + +struct _GtkFpsOverlay +{ + GtkInspectorOverlay parent_instance; + + GHashTable *infos; /* GtkWidget => GtkFpsInfo */ +}; + +struct _GtkFpsOverlayClass +{ + GtkInspectorOverlayClass parent_class; +}; + +G_DEFINE_TYPE (GtkFpsOverlay, gtk_fps_overlay, GTK_TYPE_INSPECTOR_OVERLAY) + +static void +gtk_fps_info_free (gpointer data) +{ + GtkFpsInfo *info = data; + + gsk_render_node_unref (info->last_node); + + g_slice_free (GtkFpsInfo, info); +} + +static gint64 +guess_refresh_interval (GdkFrameClock *frame_clock) +{ + gint64 interval; + gint64 i; + + interval = G_MAXINT64; + + for (i = gdk_frame_clock_get_history_start (frame_clock); + i < gdk_frame_clock_get_frame_counter (frame_clock); + i++) + { + GdkFrameTimings *t, *before; + gint64 ts, before_ts; + + t = gdk_frame_clock_get_timings (frame_clock, i); + before = gdk_frame_clock_get_timings (frame_clock, i - 1); + if (t == NULL || before == NULL) + continue; + + ts = gdk_frame_timings_get_frame_time (t); + before_ts = gdk_frame_timings_get_frame_time (before); + if (ts == 0 || before_ts == 0) + continue; + + interval = MIN (interval, ts - before_ts); + } + + if (interval == G_MAXINT64) + return 0; + + return interval; +} + +static double +gtk_fps_overlay_get_fps (GtkWidget *widget) +{ + GdkFrameClock *frame_clock; + GdkFrameTimings *start, *end; + gint64 start_counter, end_counter; + gint64 start_timestamp, end_timestamp; + gint64 interval; + + frame_clock = gtk_widget_get_frame_clock (widget); + if (frame_clock == NULL) + return 0.0; + + start_counter = gdk_frame_clock_get_history_start (frame_clock); + end_counter = gdk_frame_clock_get_frame_counter (frame_clock); + start = gdk_frame_clock_get_timings (frame_clock, start_counter); + for (end = gdk_frame_clock_get_timings (frame_clock, end_counter); + end_counter > start_counter && end != NULL && !gdk_frame_timings_get_complete (end); + end = gdk_frame_clock_get_timings (frame_clock, end_counter)) + end_counter--; + if (end_counter - start_counter < 4) + return 0.0; + + start_timestamp = gdk_frame_timings_get_presentation_time (start); + end_timestamp = gdk_frame_timings_get_presentation_time (end); + if (start_timestamp == 0 || end_timestamp == 0) + { + start_timestamp = gdk_frame_timings_get_frame_time (start); + end_timestamp = gdk_frame_timings_get_frame_time (end); + } + + interval = gdk_frame_timings_get_refresh_interval (end); + if (interval == 0) + { + interval = guess_refresh_interval (frame_clock); + if (interval == 0) + return 0.0; + } + + return ((double) end_counter - start_counter) * G_USEC_PER_SEC / (end_timestamp - start_timestamp); +} + +static gboolean +gtk_fps_overlay_force_redraw (GtkWidget *widget, + GdkFrameClock *clock, + gpointer unused) +{ + gdk_surface_queue_expose (gtk_widget_get_surface (widget)); + + return G_SOURCE_REMOVE; +} + +static void +gtk_fps_overlay_snapshot (GtkInspectorOverlay *overlay, + GtkSnapshot *snapshot, + GskRenderNode *node, + GtkWidget *widget) +{ + GtkFpsOverlay *self = GTK_FPS_OVERLAY (overlay); + GtkFpsInfo *info; + PangoLayout *layout; + gint64 now; + double fps; + char *fps_string; + graphene_rect_t bounds; + int width, height; + double overlay_opacity; + + now = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)); + info = g_hash_table_lookup (self->infos, widget); + if (info == NULL) + { + info = g_slice_new0 (GtkFpsInfo); + g_hash_table_insert (self->infos, widget, info); + } + if (info->last_node != node) + { + g_clear_pointer (&info->last_node, gsk_render_node_unref); + info->last_node = gsk_render_node_ref (node); + info->last_frame = now; + overlay_opacity = 1.0; + } + else + { + if (now - info->last_frame > GDK_FPS_OVERLAY_LINGER_DURATION + GDK_FPS_OVERLAY_FADE_DURATION) + { + g_hash_table_remove (self->infos, widget); + return; + } + else if (now - info->last_frame > GDK_FPS_OVERLAY_LINGER_DURATION) + { + overlay_opacity = 1.0 - (double) (now - info->last_frame - GDK_FPS_OVERLAY_LINGER_DURATION) + / GDK_FPS_OVERLAY_FADE_DURATION; + } + else + { + overlay_opacity = 1.0; + } + } + + fps = gtk_fps_overlay_get_fps (widget); + if (fps == 0.0) + fps_string = g_strdup ("--- fps"); + else + fps_string = g_strdup_printf ("%.2f fps", fps); + + if (GTK_IS_WINDOW (widget)) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); + if (child) + gtk_widget_compute_bounds (child, widget, &bounds); + else + gtk_widget_compute_bounds (widget, widget, &bounds); + } + else + { + gtk_widget_compute_bounds (widget, widget, &bounds); + } + + layout = gtk_widget_create_pango_layout (widget, fps_string); + pango_layout_get_pixel_size (layout, &width, &height); + + gtk_snapshot_offset (snapshot, bounds.origin.x + bounds.size.width - width, bounds.origin.y); + if (overlay_opacity < 1.0) + gtk_snapshot_push_opacity (snapshot, overlay_opacity, "Fps Overlay Opacity: %g", overlay_opacity); + gtk_snapshot_append_color (snapshot, + &(GdkRGBA) { 0, 0, 0, 0.5 }, + &GRAPHENE_RECT_INIT (-1, -1, width + 2, height + 2), + "Fps Overlay background"); + gtk_snapshot_append_layout (snapshot, + layout, + &(GdkRGBA) { 1, 1, 1, 1 }, + "Fps Overlay: %s", fps_string); + if (overlay_opacity < 1.0) + gtk_snapshot_pop (snapshot); + gtk_snapshot_offset (snapshot, - bounds.origin.x - bounds.size.width + width, - bounds.origin.y); + g_free (fps_string); + + gtk_widget_add_tick_callback (widget, gtk_fps_overlay_force_redraw, NULL, NULL); +} + +static void +gtk_fps_overlay_queue_draw (GtkInspectorOverlay *overlay) +{ + GtkFpsOverlay *self = GTK_FPS_OVERLAY (overlay); + GHashTableIter iter; + gpointer widget; + + g_hash_table_iter_init (&iter, self->infos); + while (g_hash_table_iter_next (&iter, &widget, NULL)) + gdk_surface_queue_expose (gtk_widget_get_surface (widget)); +} + +static void +gtk_fps_overlay_dispose (GObject *object) +{ + GtkFpsOverlay *self = GTK_FPS_OVERLAY (object); + + g_hash_table_unref (self->infos); + + G_OBJECT_CLASS (gtk_fps_overlay_parent_class)->dispose (object); +} + +static void +gtk_fps_overlay_class_init (GtkFpsOverlayClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkInspectorOverlayClass *overlay_class = GTK_INSPECTOR_OVERLAY_CLASS (klass); + + overlay_class->snapshot = gtk_fps_overlay_snapshot; + overlay_class->queue_draw = gtk_fps_overlay_queue_draw; + + gobject_class->dispose = gtk_fps_overlay_dispose; +} + +static void +gtk_fps_overlay_init (GtkFpsOverlay *self) +{ + self->infos = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gtk_fps_info_free); +} + +GtkInspectorOverlay * +gtk_fps_overlay_new (void) +{ + GtkFpsOverlay *self; + + self = g_object_new (GTK_TYPE_FPS_OVERLAY, NULL); + + return GTK_INSPECTOR_OVERLAY (self); +} + diff --git a/gtk/inspector/fpsoverlay.h b/gtk/inspector/fpsoverlay.h new file mode 100644 index 0000000000..2def5cde86 --- /dev/null +++ b/gtk/inspector/fpsoverlay.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#ifndef __GTK_FPS_OVERLAY_H__ +#define __GTK_FPS_OVERLAY_H__ + +#include "inspectoroverlay.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_FPS_OVERLAY (gtk_fps_overlay_get_type ()) +G_DECLARE_FINAL_TYPE (GtkFpsOverlay, gtk_fps_overlay, GTK, FPS_OVERLAY, GtkInspectorOverlay) + +GtkInspectorOverlay * gtk_fps_overlay_new (void); + +G_END_DECLS + +#endif /* __GTK_FPS_OVERLAY_H__ */ diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 585855c422..3f241585f5 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -5,6 +5,7 @@ inspector_sources = files( 'css-editor.c', 'css-node-tree.c', 'data-list.c', + 'fpsoverlay.c', 'general.c', 'gestures.c', 'graphdata.c', diff --git a/gtk/inspector/visual.c b/gtk/inspector/visual.c index 4af9b9a8a2..c2ea6aca11 100644 --- a/gtk/inspector/visual.c +++ b/gtk/inspector/visual.c @@ -20,6 +20,7 @@ #include "visual.h" +#include "fpsoverlay.h" #include "updatesoverlay.h" #include "window.h" @@ -68,7 +69,6 @@ struct _GtkInspectorVisualPrivate GtkWidget *debug_box; GtkWidget *rendering_mode_combo; - GtkWidget *updates_switch; GtkWidget *baselines_switch; GtkWidget *layout_switch; GtkWidget *touchscreen_switch; @@ -80,6 +80,7 @@ struct _GtkInspectorVisualPrivate GtkAdjustment *focus_adjustment; + GtkInspectorOverlay *fps_overlay; GtkInspectorOverlay *updates_overlay; }; @@ -227,6 +228,41 @@ font_scale_entry_activated (GtkEntry *entry, } static void +fps_activate (GtkSwitch *sw, + GParamSpec *pspec, + GtkInspectorVisual *vis) +{ + GtkInspectorVisualPrivate *priv = vis->priv; + GtkInspectorWindow *iw; + gboolean fps; + + fps = gtk_switch_get_active (sw); + iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (vis))); + if (iw == NULL) + return; + + if (fps) + { + if (priv->fps_overlay == NULL) + { + priv->fps_overlay = gtk_fps_overlay_new (); + gtk_inspector_window_add_overlay (iw, priv->fps_overlay); + g_object_unref (priv->fps_overlay); + } + } + else + { + if (priv->fps_overlay != NULL) + { + gtk_inspector_window_remove_overlay (iw, priv->fps_overlay); + priv->fps_overlay = NULL; + } + } + + redraw_everything (); +} + +static void updates_activate (GtkSwitch *sw, GParamSpec *pspec, GtkInspectorVisual *vis) @@ -901,7 +937,6 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/visual.ui"); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, rendering_mode_combo); - gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, updates_switch); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, direction_combo); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, baselines_switch); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, layout_switch); @@ -927,6 +962,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment); + gtk_widget_class_bind_template_callback (widget_class, fps_activate); gtk_widget_class_bind_template_callback (widget_class, updates_activate); gtk_widget_class_bind_template_callback (widget_class, direction_changed); gtk_widget_class_bind_template_callback (widget_class, rendering_mode_changed); diff --git a/gtk/inspector/visual.ui b/gtk/inspector/visual.ui index 14433754b6..4cd7913bf4 100644 --- a/gtk/inspector/visual.ui +++ b/gtk/inspector/visual.ui @@ -413,6 +413,33 @@ <property name="margin">10</property> <property name="spacing">40</property> <child> + <object class="GtkLabel" id="fps_label"> + <property name="label" translatable="yes">Show fps overlay</property> + <property name="halign">start</property> + <property name="valign">baseline</property> + <property name="xalign">0.0</property> + </object> + </child> + <child> + <object class="GtkSwitch" id="fps_switch"> + <property name="halign">end</property> + <property name="valign">baseline</property> + <property name="hexpand">1</property> + <signal name="notify::active" handler="fps_activate"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkListBoxRow"> + <property name="activatable">0</property> + <child> + <object class="GtkBox"> + <property name="margin">10</property> + <property name="spacing">40</property> + <child> <object class="GtkLabel" id="updates_label"> <property name="label" translatable="yes">Show Graphic Updates</property> <property name="halign">start</property> |