]> git.cworth.org Git - acre/blobdiff - acre-x.c
Expand to add CPU/GPU load and frame latency graphs.
[acre] / acre-x.c
index f0fac370a5d9bb45e414c7834a6e5e9087805624..22cb063d01a49f8ee79fd8d9bccc7e2cd6012858 100644 (file)
--- a/acre-x.c
+++ b/acre-x.c
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
+#include <stdlib.h>
 #include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
 
 #include <X11/Xlib.h>
+
+#include <cairo-svg.h>
 #include <cairo-xlib.h>
 
 #include "acre.h"
 #include "math.h"
 
+typedef struct
+{
+       acre_t *cpu_gpu_load;
+       acre_t *frame_time;
+       acre_t *frame_latency;
+} charts_t;
+
+#define STRNCMP_LITERAL(var, literal) \
+    strncmp ((var), (literal), sizeof (literal) - 1)
+
 static acre_t *
-load_chart (void)
+_create_chart (const char *title,
+              const char *x_axis_label,
+              const char *y_axis_label)
 {
        acre_t *acre;
-       acre_data_t *data0, *data1, *data2;
-       int i;
 
        acre = acre_create ();
-       acre_set_x_axis_label (acre, "X axis");
-       acre_set_y_axis_label (acre, "Y axis");
 
-       data0 = acre_data_create ();
-       data1 = acre_data_create ();
-       data2 = acre_data_create ();
+       acre_set_title (acre, title);
+       acre_set_x_axis_label (acre, x_axis_label);
+       acre_set_y_axis_label (acre, y_axis_label);
 
-       acre_data_set_name (data0, "Data 0");
-       acre_data_set_name (data1, "Data 1");
-       acre_data_set_name (data2, "Data 2");
+       return acre;
+}
 
-       for (i = 0; i <= 100; i++) {
-               acre_data_add_point_2d (data0, i,   0 - (i/3.0)*(i/3.0));
+static int
+load_fips_charts (charts_t *charts, const char *filename)
+{
+       FILE *file;
+       acre_data_t *cpu_load_data;
+       acre_data_t *gpu_load_data;
+       acre_data_t *cpu_bound_frame_time_data;
+       acre_data_t *gpu_bound_frame_time_data;
+       acre_data_t *frame_latency_data;
+       char *line = NULL, *s;
+       size_t line_size;
+       ssize_t bytes;
+
+       file = fopen (filename, "r");
+       if (file == NULL) {
+               fprintf (stderr, "Failed to open %s: %s\n",
+                        filename, strerror (errno));
+               return 1;
        }
 
-       for (i = 0; i < 100; i++) {
-               double t = 1.0 - (i / 100.0);
-               acre_data_add_point_2d (data1, i, -1000 * (1.0 - t*t*t));
-       }
-       
-       for (i = 0; i <= 1000; i++) {
-               double t, x, y;
-               t = i/10.0 - 50;
-               x = t + 50;
-               if (t == 0.0)
-                       y = -200;
-               else
-                       y = -1200 + 1000 * sin(t) / t;
-               acre_data_add_point_2d (data2, x, y);
+       charts->cpu_gpu_load = _create_chart ("CPU/GPU load", "Frame #", "Load");
+       charts->frame_time = _create_chart ("Frame time", "Frame #", "Time (ms)");
+       charts->frame_latency = _create_chart ("Frame latency", "Frame #", "Latency (ms)");
+
+       cpu_load_data = acre_data_create ();
+       acre_data_set_name (cpu_load_data, "CPU");
+
+       gpu_load_data = acre_data_create ();
+       acre_data_set_name (gpu_load_data, "GPU");
+
+       cpu_bound_frame_time_data = acre_data_create ();
+       acre_data_set_style (cpu_bound_frame_time_data, ACRE_STYLE_BARS);
+       acre_data_set_name (cpu_bound_frame_time_data, "CPU Bound");
+
+       gpu_bound_frame_time_data = acre_data_create ();
+       acre_data_set_style (gpu_bound_frame_time_data, ACRE_STYLE_BARS);
+       acre_data_set_name (gpu_bound_frame_time_data, "GPU Bound");
+
+       frame_latency_data = acre_data_create ();
+
+       while (1) {
+               int scanned;
+
+               bytes = getline (&line, &line_size, file);
+               if (bytes == -1)
+                       break;
+
+               s = line;
+
+               if (STRNCMP_LITERAL (s, "frame: ") == 0) {
+                       unsigned frame;
+                       double frame_time_ms, frame_latency_ms;
+                       double cpu_load, gpu_load;
+
+                       s += strlen("frame: ");
+
+                       scanned = sscanf (s, "%d %lg %lg %lg %lg",
+                                         &frame,
+                                         &frame_time_ms,
+                                         &frame_latency_ms,
+                                         &cpu_load,
+                                         &gpu_load);
+                       if (scanned != 5) {
+                               fprintf (stderr, "Warning: Failed to parse line: %s\n", line);
+                               continue;
+                       }
+
+                       acre_data_add_point_2d (cpu_load_data, frame, cpu_load);
+                       acre_data_add_point_2d (gpu_load_data, frame, gpu_load);
+                       if (cpu_load > gpu_load) {
+                               acre_data_add_point_2d (
+                                       cpu_bound_frame_time_data,
+                                       frame, frame_time_ms);
+                       } else {
+                               acre_data_add_point_2d (
+                                       gpu_bound_frame_time_data,
+                                       frame, frame_time_ms);
+                       }
+                       acre_data_add_point_2d (frame_latency_data, frame,
+                                               frame_latency_ms);
+
+               } else {
+                       /* Ignore all other lines. */
+               }
        }
 
-       acre_add_data (acre, data0);
-       acre_add_data (acre, data1);
-       acre_add_data (acre, data2);
+       free (line);
 
-       acre_set_title (acre, "All the data");
+       acre_add_data (charts->cpu_gpu_load, cpu_load_data);
+       acre_add_data (charts->cpu_gpu_load, gpu_load_data);
+       acre_add_data (charts->frame_time, cpu_bound_frame_time_data);
+       acre_add_data (charts->frame_time, gpu_bound_frame_time_data);
+       acre_add_data (charts->frame_latency, frame_latency_data);
 
-       return acre;
+       return 0;
 }
 
 static void
-draw (Display *dpy, Window window, Visual *visual, acre_t *acre,
+draw_cairo (cairo_t *cr, charts_t *charts, int width, int height,
+           double x_min, double x_max)
+{
+       int chart_height = height / 3;
+
+       /* Erase to white */
+       cairo_set_source_rgb (cr, 1, 1, 1);
+       cairo_paint (cr);
+
+       acre_set_x_axis_range (charts->cpu_gpu_load, x_min, x_max);
+       acre_draw (charts->cpu_gpu_load, cr, width, chart_height);
+
+       cairo_translate (cr, 0.0, chart_height);
+
+       acre_set_x_axis_range (charts->frame_time, x_min, x_max);
+       acre_draw (charts->frame_time, cr, width, chart_height);
+
+       cairo_translate (cr, 0.0, chart_height);
+
+       acre_set_x_axis_range (charts->frame_latency, x_min, x_max);
+       acre_draw (charts->frame_latency, cr, width, chart_height);
+}
+
+static void
+draw_xlib (Display *dpy, Window window, Visual *visual, charts_t *charts,
       int width, int height, double x_min, double x_max)
 {
        cairo_t *cr;
@@ -84,22 +189,55 @@ draw (Display *dpy, Window window, Visual *visual, acre_t *acre,
                                             width, height);
        cr = cairo_create (surface);
 
-       /* Erase to white */
-       cairo_set_source_rgb (cr, 1, 1, 1);
-       cairo_paint (cr);
+       draw_cairo (cr, charts, width, height, x_min, x_max);
 
-       acre_set_x_axis_range (acre, x_min, x_max);
-       acre_draw (acre, cr, width, height);
+       cairo_destroy (cr);
+
+       cairo_surface_destroy (surface);
+}
+
+static void
+draw_svg (const char *filename, charts_t *charts,
+         int width, int height, double x_min, double x_max)
+{
+       cairo_t *cr;
+       cairo_surface_t *surface;
+
+       surface = cairo_svg_surface_create (filename, width, height);
+
+       cr = cairo_create (surface);
+
+       draw_cairo (cr, charts, width, height, x_min, x_max);
 
        cairo_destroy (cr);
 
        cairo_surface_destroy (surface);
 }
 
+static void
+draw_png (const char *filename, charts_t *charts,
+         int width, int height, double x_min, double x_max)
+{
+       cairo_t *cr;
+       cairo_surface_t *surface;
+
+       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                             width, height);
+
+       cr = cairo_create (surface);
+
+       draw_cairo (cr, charts, width, height, x_min, x_max);
+
+       cairo_destroy (cr);
+
+       cairo_surface_write_to_png (surface, filename);
+
+       cairo_surface_destroy (surface);
+}
 
 static void
 handle_events(Display *dpy, Window window, Visual *visual,
-             acre_t *acre, int width, int height)
+             charts_t *charts, int width, int height)
 {
         XEvent xev;
         KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q"));
@@ -110,22 +248,25 @@ handle_events(Display *dpy, Window window, Visual *visual,
         KeyCode equal_code = XKeysymToKeycode (dpy, XStringToKeysym("equal"));
         KeyCode minus_code = XKeysymToKeycode (dpy, XStringToKeysym("minus"));
         KeyCode home_code = XKeysymToKeycode (dpy, XStringToKeysym("Home"));
+        KeyCode png_code = XKeysymToKeycode (dpy, XStringToKeysym("P"));
+        KeyCode svg_code = XKeysymToKeycode (dpy, XStringToKeysym("S"));
        KeyCode keycode;
        bool need_redraw = false;
        double x_min, x_max, shift;
 
-       acre_get_x_axis_data_range (acre, &x_min, &x_max);
+       acre_get_x_axis_data_range (charts->cpu_gpu_load, &x_min, &x_max);
 
         while (1) {
                 if (! XPending (dpy) && need_redraw)
-                       draw (dpy, window, visual, acre,
-                             width, height, x_min, x_max);
+                       draw_xlib (dpy, window, visual, charts,
+                                  width, height, x_min, x_max);
 
 #define PAN  0.05
 #define ZOOM PAN
                XNextEvent (dpy, &xev);
                 switch (xev.type) {
                 case KeyPress:
+                       need_redraw = true;
                        keycode = xev.xkey.keycode;
                        if (keycode == quit_code ||
                            keycode == escape_code)
@@ -153,15 +294,31 @@ handle_events(Display *dpy, Window window, Visual *visual,
                        }
                        else if (keycode == minus_code)
                        {
-                               shift = (1- 2*ZOOM) * (x_max - x_min);
+                               shift = (ZOOM/(1 - 2 * ZOOM)) * (x_max - x_min);
                                x_min -= shift;
                                x_max += shift;
                        }
                        else if (keycode == home_code)
                        {
-                               acre_get_x_axis_data_range (acre, &x_min, &x_max);
+                               acre_get_x_axis_data_range (charts->cpu_gpu_load,
+                                                           &x_min, &x_max);
+                       }
+                       else if (keycode == svg_code)
+                       {
+                               need_redraw = false;
+                               draw_svg ("acre-fips.svg", charts,
+                                         width, height, x_min, x_max);
+                       }
+                       else if (keycode == png_code)
+                       {
+                               need_redraw = false;
+                               draw_png ("acre-fips.png", charts,
+                                         width, height, x_min, x_max);
+                       }
+                       else
+                       {
+                               need_redraw = false;
                        }
-                       need_redraw = 1;
                        break;
                 case ConfigureNotify:
                         width = xev.xconfigure.width;
@@ -176,21 +333,30 @@ handle_events(Display *dpy, Window window, Visual *visual,
 }
 
 int
-main (void)
+main (int argc, char *argv[])
 {
         Display *dpy;
         Window window, root;
        Visual *visual;
-       acre_t *acre;
        XSetWindowAttributes window_attr;
        Colormap colormap;
        unsigned long window_mask, event_mask;
        unsigned long white;
+       charts_t charts;
+       int err;
 
        int width = 800;
        int height = 600;
 
-       acre = load_chart ();
+       if (argc < 2) {
+               fprintf (stderr, "Usage: acre-x data-file\n");
+               fprintf (stderr, "Where the data file is the output from fips (with frame-timing feature.\n");
+               exit (1);
+       }
+
+       err = load_fips_charts (&charts, argv[1]);
+       if (err)
+               return err;
 
         dpy = XOpenDisplay (NULL);
 
@@ -219,9 +385,11 @@ main (void)
 
         XMapWindow (dpy, window);
 
-        handle_events (dpy, window, visual, acre, width, height);
+        handle_events (dpy, window, visual, &charts, width, height);
 
-       acre_destroy (acre);
+       acre_destroy (charts.cpu_gpu_load);
+       acre_destroy (charts.frame_time);
+       acre_destroy (charts.frame_latency);
 
         XDestroyWindow (dpy, window);
         XCloseDisplay (dpy);