184 lines
5.4 KiB
C
184 lines
5.4 KiB
C
|
/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "frame-stats.h"
|
||
|
#include "variable.h"
|
||
|
|
||
|
typedef struct FrameStats FrameStats;
|
||
|
|
||
|
struct FrameStats
|
||
|
{
|
||
|
GdkFrameClock *frame_clock;
|
||
|
|
||
|
int num_stats;
|
||
|
double last_print_time;
|
||
|
int frames_since_last_print;
|
||
|
gint64 last_handled_frame;
|
||
|
|
||
|
Variable latency;
|
||
|
};
|
||
|
|
||
|
static int max_stats = -1;
|
||
|
static double statistics_time = 5.;
|
||
|
static gboolean machine_readable = FALSE;
|
||
|
|
||
|
static GOptionEntry frame_sync_options[] = {
|
||
|
{ "max-statistics", 'm', 0, G_OPTION_ARG_INT, &max_stats, "Maximum statistics printed", NULL },
|
||
|
{ "machine-readable", 0, 0, G_OPTION_ARG_NONE, &machine_readable, "Print statistics in columns", NULL },
|
||
|
{ "statistics-time", 's', 0, G_OPTION_ARG_DOUBLE, &statistics_time, "Statistics accumulation time", "TIME" },
|
||
|
{ NULL }
|
||
|
};
|
||
|
|
||
|
void
|
||
|
frame_stats_add_options (GOptionGroup *group)
|
||
|
{
|
||
|
g_option_group_add_entries (group, frame_sync_options);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
print_double (const char *description,
|
||
|
double value)
|
||
|
{
|
||
|
if (machine_readable)
|
||
|
g_print ("%g\t", value);
|
||
|
else
|
||
|
g_print ("%s: %g\n", description, value);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
print_variable (const char *description,
|
||
|
Variable *variable)
|
||
|
{
|
||
|
if (variable->weight != 0)
|
||
|
{
|
||
|
if (machine_readable)
|
||
|
g_print ("%g\t%g\t",
|
||
|
variable_mean (variable),
|
||
|
variable_standard_deviation (variable));
|
||
|
else
|
||
|
g_print ("%s: %g +/- %g\n", description,
|
||
|
variable_mean (variable),
|
||
|
variable_standard_deviation (variable));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (machine_readable)
|
||
|
g_print ("-\t-\t");
|
||
|
else
|
||
|
g_print ("%s: <n/a>\n", description);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_frame_clock_after_paint (GdkFrameClock *frame_clock,
|
||
|
FrameStats *frame_stats)
|
||
|
{
|
||
|
gint64 frame_counter;
|
||
|
gint64 current_time;
|
||
|
|
||
|
current_time = g_get_monotonic_time ();
|
||
|
if (current_time >= frame_stats->last_print_time + 1000000 * statistics_time)
|
||
|
{
|
||
|
if (frame_stats->frames_since_last_print)
|
||
|
{
|
||
|
if (frame_stats->num_stats == 0 && machine_readable)
|
||
|
{
|
||
|
g_print ("# load_factor frame_rate latency\n");
|
||
|
}
|
||
|
|
||
|
frame_stats->num_stats++;
|
||
|
print_double ("Frame rate ",
|
||
|
frame_stats->frames_since_last_print /
|
||
|
((current_time - frame_stats->last_print_time) / 1000000.));
|
||
|
|
||
|
print_variable ("Latency", &frame_stats->latency);
|
||
|
|
||
|
g_print ("\n");
|
||
|
}
|
||
|
|
||
|
frame_stats->last_print_time = current_time;
|
||
|
frame_stats->frames_since_last_print = 0;
|
||
|
variable_init (&frame_stats->latency);
|
||
|
|
||
|
if (frame_stats->num_stats == max_stats)
|
||
|
exit (0);
|
||
|
}
|
||
|
|
||
|
frame_stats->frames_since_last_print++;
|
||
|
|
||
|
for (frame_counter = frame_stats->last_handled_frame;
|
||
|
frame_counter < gdk_frame_clock_get_frame_counter (frame_clock);
|
||
|
frame_counter++)
|
||
|
{
|
||
|
GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
|
||
|
GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (frame_clock, frame_counter - 1);
|
||
|
|
||
|
if (!timings || gdk_frame_timings_get_complete (timings))
|
||
|
frame_stats->last_handled_frame = frame_counter;
|
||
|
|
||
|
if (timings && gdk_frame_timings_get_complete (timings) && previous_timings &&
|
||
|
gdk_frame_timings_get_presentation_time (timings) != 0 &&
|
||
|
gdk_frame_timings_get_presentation_time (previous_timings) != 0)
|
||
|
{
|
||
|
double display_time = (gdk_frame_timings_get_presentation_time (timings) - gdk_frame_timings_get_presentation_time (previous_timings)) / 1000.;
|
||
|
double frame_latency = (gdk_frame_timings_get_presentation_time (previous_timings) - gdk_frame_timings_get_frame_time (previous_timings)) / 1000. + display_time / 2;
|
||
|
|
||
|
variable_add_weighted (&frame_stats->latency, frame_latency, display_time);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_window_realize (GtkWidget *window,
|
||
|
FrameStats *frame_stats)
|
||
|
{
|
||
|
frame_stats->frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (window));
|
||
|
g_signal_connect (frame_stats->frame_clock, "after-paint",
|
||
|
G_CALLBACK (on_frame_clock_after_paint), frame_stats);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_window_unrealize (GtkWidget *window,
|
||
|
FrameStats *frame_stats)
|
||
|
{
|
||
|
g_signal_handlers_disconnect_by_func (frame_stats->frame_clock,
|
||
|
(gpointer) on_frame_clock_after_paint,
|
||
|
frame_stats);
|
||
|
frame_stats->frame_clock = NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_window_destroy (GtkWidget *window,
|
||
|
FrameStats *stats)
|
||
|
{
|
||
|
g_free (stats);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
frame_stats_ensure (GtkWindow *window)
|
||
|
{
|
||
|
FrameStats *frame_stats;
|
||
|
|
||
|
frame_stats = g_object_get_data (G_OBJECT (window), "frame-stats");
|
||
|
if (frame_stats != NULL)
|
||
|
return;
|
||
|
|
||
|
frame_stats = g_new0 (FrameStats, 1);
|
||
|
g_object_set_data (G_OBJECT (window), "frame-stats", frame_stats);
|
||
|
|
||
|
variable_init (&frame_stats->latency);
|
||
|
frame_stats->last_handled_frame = -1;
|
||
|
|
||
|
g_signal_connect (window, "realize",
|
||
|
G_CALLBACK (on_window_realize), frame_stats);
|
||
|
g_signal_connect (window, "unrealize",
|
||
|
G_CALLBACK (on_window_unrealize), frame_stats);
|
||
|
g_signal_connect (window, "destroy",
|
||
|
G_CALLBACK (on_window_destroy), frame_stats);
|
||
|
|
||
|
if (gtk_widget_get_realized (GTK_WIDGET (window)))
|
||
|
on_window_realize (GTK_WIDGET (window), frame_stats);
|
||
|
}
|