diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2016-08-03 16:48:49 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2016-10-18 11:49:10 +0100 |
commit | 005fea59b69dad080b0d8b957db770d0f5a0e572 (patch) | |
tree | e1b5275ab5e30c689245c5ca7c54c60913f5dc0b /gsk | |
parent | 137ff8ede74423cf11cadd7c3a2b21752d6334d0 (diff) | |
download | gtk+-005fea59b69dad080b0d8b957db770d0f5a0e572.tar.gz |
gsk: Add profiler object
The GskProfiler holds counters and timers, and will be used by the
renderer implementations to acquire frame timings.
Diffstat (limited to 'gsk')
-rw-r--r-- | gsk/Makefile.am | 2 | ||||
-rw-r--r-- | gsk/gskprofiler.c | 470 | ||||
-rw-r--r-- | gsk/gskprofilerprivate.h | 48 |
3 files changed, 520 insertions, 0 deletions
diff --git a/gsk/Makefile.am b/gsk/Makefile.am index 4c33dadad5..920c858d3e 100644 --- a/gsk/Makefile.am +++ b/gsk/Makefile.am @@ -41,6 +41,7 @@ gsk_private_source_h = \ gskglprofilerprivate.h \ gskglrendererprivate.h \ gskprivate.h \ + gskprofilerprivate.h \ gskrendererprivate.h \ gskrendernodeprivate.h \ gskshaderbuilderprivate.h @@ -58,6 +59,7 @@ gsk_source_c = \ gskgldriver.c \ gskglprofiler.c \ gskglrenderer.c \ + gskprofiler.c \ gskrenderer.c \ gskrendernode.c \ gskrendernodeiter.c \ diff --git a/gsk/gskprofiler.c b/gsk/gskprofiler.c new file mode 100644 index 0000000000..f3b2bd53f6 --- /dev/null +++ b/gsk/gskprofiler.c @@ -0,0 +1,470 @@ +#include "config.h" + +#include "gskprofilerprivate.h" + +#define MAX_SAMPLES 32 + +typedef struct { + GQuark id; + char *description; + gint64 value; + gint64 n_samples; + gboolean can_reset : 1; +} NamedCounter; + +typedef struct { + GQuark id; + char *description; + gint64 value; + gint64 start_time; + gint64 min_value; + gint64 max_value; + gint64 avg_value; + gint64 n_samples; + gboolean in_flight : 1; + gboolean can_reset : 1; + gboolean invert : 1; +} NamedTimer; + +typedef struct { + GQuark id; + gint64 value; +} Sample; + +struct _GskProfiler +{ + GObject parent_instance; + + GHashTable *counters; + GHashTable *timers; + + Sample timer_samples[MAX_SAMPLES]; + guint last_sample; +}; + +G_DEFINE_TYPE (GskProfiler, gsk_profiler, G_TYPE_OBJECT) + +static void +named_counter_free (gpointer data) +{ + NamedCounter *counter = data; + + if (data == NULL) + return; + + g_free (counter->description); + + g_slice_free (NamedCounter, counter); +} + +static void +named_timer_free (gpointer data) +{ + NamedTimer *timer = data; + + if (data == NULL) + return; + + g_free (timer->description); + + g_slice_free (NamedTimer, timer); +} + +static void +gsk_profiler_finalize (GObject *gobject) +{ + GskProfiler *self = GSK_PROFILER (gobject); + + g_clear_pointer (&self->counters, g_hash_table_unref); + g_clear_pointer (&self->timers, g_hash_table_unref); + + G_OBJECT_CLASS (gsk_profiler_parent_class)->finalize (gobject); +} + +static void +gsk_profiler_class_init (GskProfilerClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = gsk_profiler_finalize; +} + +static void +gsk_profiler_init (GskProfiler *self) +{ + self->counters = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + named_counter_free); + self->timers = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + named_timer_free); +} + +GskProfiler * +gsk_profiler_new (void) +{ + return g_object_new (GSK_TYPE_PROFILER, NULL); +} + +static NamedCounter * +named_counter_new (GQuark id, + const char *description, + gboolean can_reset) +{ + NamedCounter *res = g_slice_new0 (NamedCounter); + + res->id = id; + res->description = g_strdup (description); + res->can_reset = can_reset; + + return res; +} + +static NamedCounter * +gsk_profiler_get_counter (GskProfiler *profiler, + GQuark id) +{ + return g_hash_table_lookup (profiler->counters, GINT_TO_POINTER (id)); +} + +GQuark +gsk_profiler_add_counter (GskProfiler *profiler, + const char *counter_name, + const char *description, + gboolean can_reset) +{ + NamedCounter *counter; + GQuark id; + + g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0); + + id = g_quark_from_string (counter_name); + counter = gsk_profiler_get_counter (profiler, id); + if (counter != NULL) + { + g_critical ("Cannot add a counter '%s' as one already exists.", counter_name); + return counter->id; + } + + counter = named_counter_new (id, description, can_reset); + g_hash_table_insert (profiler->counters, GINT_TO_POINTER (id), counter); + + return counter->id; +} + +static NamedTimer * +named_timer_new (GQuark id, + const char *description, + gboolean invert, + gboolean can_reset) +{ + NamedTimer *res = g_slice_new0 (NamedTimer); + + res->id = id; + res->description = g_strdup (description); + res->invert = invert; + res->can_reset = can_reset; + + return res; +} + +static NamedTimer * +gsk_profiler_get_timer (GskProfiler *profiler, + GQuark id) +{ + return g_hash_table_lookup (profiler->timers, GINT_TO_POINTER (id)); +} + +GQuark +gsk_profiler_add_timer (GskProfiler *profiler, + const char *timer_name, + const char *description, + gboolean invert, + gboolean can_reset) +{ + NamedTimer *timer; + GQuark id; + + g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0); + + id = g_quark_from_string (timer_name); + timer = gsk_profiler_get_timer (profiler, id); + if (timer != NULL) + { + g_critical ("Cannot add a timer '%s' as one already exists.", timer_name); + return timer->id; + } + + timer = named_timer_new (id, description, invert, can_reset); + g_hash_table_insert (profiler->timers, GINT_TO_POINTER (id), timer); + + return timer->id; +} + +void +gsk_profiler_counter_inc (GskProfiler *profiler, + GQuark counter_id) +{ + NamedCounter *counter; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + + counter = gsk_profiler_get_counter (profiler, counter_id); + if (counter == NULL) + return; + + counter->value += 1; + +} + +void +gsk_profiler_timer_begin (GskProfiler *profiler, + GQuark timer_id) +{ + NamedTimer *timer; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + + timer = gsk_profiler_get_timer (profiler, timer_id); + if (timer == NULL) + return; + + if (timer->in_flight) + return; + + timer->in_flight = TRUE; + timer->start_time = g_get_monotonic_time () * 1000; +} + +gint64 +gsk_profiler_timer_end (GskProfiler *profiler, + GQuark timer_id) +{ + NamedTimer *timer; + gint64 diff; + + g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0); + + timer = gsk_profiler_get_timer (profiler, timer_id); + if (timer == NULL) + { + g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?", + g_quark_to_string (timer_id), timer_id); + return 0; + } + + if (!timer->in_flight) + { + g_critical ("Timer '%s' (id:%d) is not running; did you forget to call gsk_profiler_timer_begin()?", + g_quark_to_string (timer->id), timer->id); + return 0; + } + + diff = (g_get_monotonic_time () * 1000) - timer->start_time; + + timer->in_flight = FALSE; + timer->value += diff; + + return diff; +} + +void +gsk_profiler_timer_set (GskProfiler *profiler, + GQuark timer_id, + gint64 value) +{ + NamedTimer *timer; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + + timer = gsk_profiler_get_timer (profiler, timer_id); + if (timer == NULL) + { + g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?", + g_quark_to_string (timer_id), timer_id); + return; + } + + if (timer->in_flight) + { + g_critical ("Timer '%s' (id:%d) is running; are you sure you don't want to call " + "gsk_profiler_timer_end() instead of gsk_profiler_timer_set()?", + g_quark_to_string (timer_id), timer_id); + } + + timer->value = value; +} + +gint64 +gsk_profiler_counter_get (GskProfiler *profiler, + GQuark counter_id) +{ + NamedCounter *counter; + + g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0); + + counter = gsk_profiler_get_counter (profiler, counter_id); + if (counter == NULL) + { + g_critical ("No counter '%s' (id:%d) found; did you forget to call gsk_profiler_add_counter()?", + g_quark_to_string (counter_id), counter_id); + return 0; + } + + return counter->value; +} + +gint64 +gsk_profiler_timer_get (GskProfiler *profiler, + GQuark timer_id) +{ + NamedTimer *timer; + + g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0); + + timer = gsk_profiler_get_timer (profiler, timer_id); + if (timer == NULL) + { + g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?", + g_quark_to_string (timer_id), timer_id); + return 0; + } + + if (timer->invert) + return (gint64) (1000000000.0 / (double) timer->value); + + return timer->value; +} + +void +gsk_profiler_reset (GskProfiler *profiler) +{ + GHashTableIter iter; + gpointer value_p = NULL; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + + g_hash_table_iter_init (&iter, profiler->counters); + while (g_hash_table_iter_next (&iter, NULL, &value_p)) + { + NamedCounter *counter = value_p; + + if (counter->can_reset) + counter->value = 0; + } + + g_hash_table_iter_init (&iter, profiler->timers); + while (g_hash_table_iter_next (&iter, NULL, &value_p)) + { + NamedTimer *timer = value_p; + + if (timer->can_reset) + { + timer->value = 0; + timer->min_value = 0; + timer->max_value = 0; + timer->avg_value = 0; + timer->n_samples = 0; + } + } + + profiler->last_sample = 0; +} + +void +gsk_profiler_push_samples (GskProfiler *profiler) +{ + GHashTableIter iter; + gpointer value_p = NULL; + guint last_sample; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + + g_hash_table_iter_init (&iter, profiler->timers); + while (g_hash_table_iter_next (&iter, NULL, &value_p)) + { + NamedTimer *timer = value_p; + Sample *s; + + last_sample = profiler->last_sample; + profiler->last_sample += 1; + if (profiler->last_sample == MAX_SAMPLES) + profiler->last_sample = 0; + + s = &(profiler->timer_samples[last_sample]); + s->id = timer->id; + + if (timer->invert) + s->value = (gint64) (1000000000.0 / (double) timer->value); + else + s->value = timer->value; + } +} + +void +gsk_profiler_append_counters (GskProfiler *profiler, + GString *buffer) +{ + GHashTableIter iter; + gpointer value_p = NULL; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + g_return_if_fail (buffer != NULL); + + g_hash_table_iter_init (&iter, profiler->counters); + while (g_hash_table_iter_next (&iter, NULL, &value_p)) + { + NamedCounter *counter = value_p; + + g_string_append_printf (buffer, "%s: %" G_GINT64_FORMAT "\n", + counter->description, + counter->value); + } +} + +void +gsk_profiler_append_timers (GskProfiler *profiler, + GString *buffer) +{ + GHashTableIter iter; + gpointer value_p = NULL; + int i; + + g_return_if_fail (GSK_IS_PROFILER (profiler)); + g_return_if_fail (buffer != NULL); + + for (i = 0; i < profiler->last_sample; i++) + { + Sample *s = &(profiler->timer_samples[i]); + NamedTimer *timer; + + if (s->id == 0) + continue; + + timer = gsk_profiler_get_timer (profiler, s->id); + timer->min_value = MIN (timer->min_value, s->value); + timer->max_value = MAX (timer->max_value, s->value); + timer->avg_value += s->value; + timer->n_samples += 1; + } + + g_hash_table_iter_init (&iter, profiler->timers); + while (g_hash_table_iter_next (&iter, NULL, &value_p)) + { + NamedTimer *timer = value_p; + const char *unit = timer->invert ? "" : "usec"; + double scale = timer->invert ? 1.0 : 1000.0; + + if (timer->n_samples != 0) + timer->avg_value = timer->avg_value / timer->n_samples; + + g_string_append_printf (buffer, + "%s %s: " + "Min:%.2f, " + "Avg:%.2f, " + "Max:%.2f\n", + timer->description, + unit, + (double) timer->min_value / scale, + (double) timer->avg_value / scale, + (double) timer->max_value / scale); + } +} diff --git a/gsk/gskprofilerprivate.h b/gsk/gskprofilerprivate.h new file mode 100644 index 0000000000..382ecc895b --- /dev/null +++ b/gsk/gskprofilerprivate.h @@ -0,0 +1,48 @@ +#ifndef __GSK_PROFILER_PRIVATE_H__ +#define __GSK_PROFILER_PRIVATE_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GSK_TYPE_PROFILER (gsk_profiler_get_type ()) +G_DECLARE_FINAL_TYPE (GskProfiler, gsk_profiler, GSK, PROFILER, GObject) + +GskProfiler * gsk_profiler_new (void); + +GQuark gsk_profiler_add_counter (GskProfiler *profiler, + const char *counter_name, + const char *description, + gboolean can_reset); +GQuark gsk_profiler_add_timer (GskProfiler *profiler, + const char *timer_name, + const char *description, + gboolean invert, + gboolean can_reset); + +void gsk_profiler_counter_inc (GskProfiler *profiler, + GQuark counter_id); +void gsk_profiler_timer_begin (GskProfiler *profiler, + GQuark timer_id); +gint64 gsk_profiler_timer_end (GskProfiler *profiler, + GQuark timer_id); +void gsk_profiler_timer_set (GskProfiler *profiler, + GQuark timer_id, + gint64 value); + +gint64 gsk_profiler_counter_get (GskProfiler *profiler, + GQuark counter_id); +gint64 gsk_profiler_timer_get (GskProfiler *profiler, + GQuark timer_id); + +void gsk_profiler_reset (GskProfiler *profiler); + +void gsk_profiler_push_samples (GskProfiler *profiler); +void gsk_profiler_append_counters (GskProfiler *profiler, + GString *buffer); +void gsk_profiler_append_timers (GskProfiler *profiler, + GString *buffer); + +G_END_DECLS + +#endif /* __GSK_PROFILER_PRIVATE_H__ */ |