mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
gsk: Add profiler object
The GskProfiler holds counters and timers, and will be used by the renderer implementations to acquire frame timings.
This commit is contained in:
parent
137ff8ede7
commit
005fea59b6
@ -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 \
|
||||
|
470
gsk/gskprofiler.c
Normal file
470
gsk/gskprofiler.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
48
gsk/gskprofilerprivate.h
Normal file
48
gsk/gskprofilerprivate.h
Normal file
@ -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__ */
|
Loading…
Reference in New Issue
Block a user