[Intel-gfx] [PATCH i-g-t 05/16] plot: Draw nice plots!
Damien Lespiau
damien.lespiau at intel.com
Mon Jul 6 05:35:33 PDT 2015
Signed-off-by: Damien Lespiau <damien.lespiau at intel.com>
---
.../intel-gpu-tools/intel-gpu-tools-docs.xml | 1 +
lib/Makefile.sources | 2 +
lib/igt_plot.c | 607 +++++++++++++++++++++
lib/igt_plot.h | 122 +++++
lib/tests/.gitignore | 4 +
lib/tests/Makefile.sources | 1 +
lib/tests/igt_plot.c | 98 ++++
7 files changed, 835 insertions(+)
create mode 100644 lib/igt_plot.c
create mode 100644 lib/igt_plot.h
create mode 100644 lib/tests/igt_plot.c
diff --git a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
index 0992308..83f7d29 100644
--- a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
+++ b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
@@ -19,6 +19,7 @@
<xi:include href="xml/igt_core.xml"/>
<xi:include href="xml/igt_types.xml"/>
<xi:include href="xml/igt_stats.xml"/>
+ <xi:include href="xml/igt_plot.xml"/>
<xi:include href="xml/igt_debugfs.xml"/>
<xi:include href="xml/igt_draw.xml"/>
<xi:include href="xml/igt_kms.xml"/>
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index 205a9aa..9fabd44 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -12,6 +12,8 @@ libintel_tools_la_SOURCES = \
igt_aux.h \
igt_gt.c \
igt_gt.h \
+ igt_plot.c \
+ igt_plot.h \
igt_stats.c \
igt_stats.h \
igt_types.h \
diff --git a/lib/igt_plot.c b/lib/igt_plot.c
new file mode 100644
index 0000000..f7187f6
--- /dev/null
+++ b/lib/igt_plot.c
@@ -0,0 +1,607 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "igt_plot.h"
+#include "igt_aux.h" /* min() */
+
+/**
+ * SECTION:igt_plot
+ * @short_description: Draw various plots
+ * @title: Plots
+ * @include: igt_plots.h
+ *
+ * A drawing is better than a long speech. Plotting data can reveal surprises
+ * and the igt_plot_t object let you do just that.
+ */
+
+/* snap to pixel */
+#define SNAP(d) ((uint64_t)(d) + 0.5)
+
+static igt_vector_t *igt_vector_new_internal(unsigned int n)
+{
+ igt_vector_t *v;
+
+ /* single allocation for both the vector and data */
+ v = malloc(sizeof(igt_vector_t) + n * sizeof(double));
+ v->ref = 1;
+ v->n = n;
+
+ return v;
+}
+
+/**
+ * igt_vector_new:
+ * @n: Number of samples
+ *
+ * Creates a zeroed vector of size @n.
+ */
+igt_vector_t *igt_vector_new(unsigned int n)
+{
+ igt_vector_t *v;
+
+ v = igt_vector_new_internal(n);
+ memset(v->values, 0, n * sizeof(double));
+
+ return v;
+}
+
+/**
+ * igt_vector_new_from_array:
+ * @array: (array length=n): C array of doubles
+ * @n: Size of the array
+ *
+ * Creates a new vector from @array, copying the data.
+ */
+igt_vector_t *igt_vector_new_from_array(const double *array, unsigned int n)
+{
+ igt_vector_t *v;
+
+ v = igt_vector_new_internal(n);
+ memcpy(v->values, array, n * sizeof(double));
+
+ return v;
+}
+
+/**
+ * igt_vector_new_from_array_64:
+ * @array: (array length=n): C array of doubles
+ * @n: Size of the array
+ *
+ * Like igt_vector_new_from_array() but for uint64_t.
+ */
+igt_vector_t *igt_vector_new_from_array_u64(const uint64_t *array,
+ unsigned int n)
+{
+ igt_vector_t *v;
+ unsigned int i;
+
+ v = igt_vector_new_internal(n);
+ for (i = 0; i < n; i++)
+ v->values[i] = (double)array[i];
+
+ return v;
+}
+
+/**
+ * igt_vector_ref:
+ * @v: An #igt_vector_t
+ *
+ * Takes a reference on @v.
+ */
+igt_vector_t *igt_vector_ref(igt_vector_t *v)
+{
+ v->ref++;
+ return v;
+}
+
+/**
+ * igt_vector_unref:
+ * @v: An #igt_vector_t
+ *
+ * Releases a reference on @v, freeing the object if the reference reaches 0.
+ */
+void igt_vector_unref(igt_vector_t *v)
+{
+ if (--v->ref == 0)
+ free(v);
+}
+
+/**
+ * igt_vector_get_min_max:
+ * @v: An #igt_vector_t
+ * @min: (out): The minimum value in @v
+ * @max: (out): The maximum value in @v
+ *
+ * Finds the mininum and maximum value in @v.
+ */
+void igt_vector_get_min_max(const igt_vector_t *v, double *min, double *max)
+{
+ unsigned int i;
+ double small, big;
+
+ /*
+ * Make sure we deal with an even number of samples for the second step
+ */
+ if (v->n % 2 == 1){
+ *min = v->values[0];
+ *max = v->values[0];
+ i = 1;
+ } else {
+ *min = DBL_MAX;
+ *max = -DBL_MAX;
+ i = 0;
+ }
+
+ for (; i < v->n; i += 2) {
+ if (v->values[i] < v->values[i + 1]) {
+ small = v->values[i];
+ big = v->values[i + 1];
+ } else {
+ small = v->values[i + 1];
+ big = v->values[i];
+ }
+
+ if (*min > small)
+ *min = small;
+ if (*max < big)
+ *max = big;
+ }
+}
+
+/**
+ * igt_vector_linear:
+ * @min: Lower bound
+ * @max: Upper bound
+ * @n: Number of samples to generate
+ *
+ * Creates a vector of @n values evenly spaced between @min and @max (both
+ * inclusive).
+ */
+igt_vector_t *igt_vector_linear(double min, double max, unsigned int n)
+{
+ igt_vector_t *v;
+ unsigned int i;
+
+ v = igt_vector_new_internal(n);
+ for (i = 0; i < n; i++)
+ v->values[i] = min + i * (max - min) / (n - 1);
+
+ return v;
+}
+
+static void igt_plot_axis_init(igt_plot_axis_t *axis,
+ igt_orientation_t orientation)
+{
+ memset(axis, 0, sizeof(*axis));
+
+ axis->n_ticks = 5;
+ axis->orientation = orientation;
+ axis->min = DBL_MAX;
+ axis->max = -DBL_MAX;
+}
+
+static void igt_plot_axis_fini(igt_plot_axis_t *axis)
+{
+
+}
+
+static void igt_plot_axis_add_range(igt_plot_axis_t *axis,
+ double min, double max)
+{
+ if (min < axis->min)
+ axis->min = min;
+ if (max > axis->max)
+ axis->max = max;
+}
+
+static void igt_plot_ctx_init(igt_plot_ctx_t *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->line_width = 1.5;
+}
+
+static void igt_plot_ctx_fini(igt_plot_ctx_t *ctx)
+{
+ igt_vector_unref(ctx->x);
+ ctx->x = NULL;
+ igt_vector_unref(ctx->y);
+ ctx->y = NULL;
+}
+
+/**
+ * igt_plot_init:
+ * @plot: An #igt_plot_t instance
+ * @width: Width of the plot (in pixels)
+ * @height: Height of the plot (in pixels)
+ *
+ * Initializes an igt_plot_t object. Use igt_plot_fini() when finished with it.
+ */
+void igt_plot_init(igt_plot_t *plot, unsigned int width, unsigned int height)
+{
+ memset(plot, 0, sizeof(*plot));
+
+ plot->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ width, height);
+ plot->width = width;
+ plot->height = height;
+ plot->cr = cairo_create(plot->surface);
+
+ igt_plot_axis_init(&plot->x_axis, IGT_ORIENTATION_HORIZONTAL);
+ igt_plot_axis_init(&plot->y_axis, IGT_ORIENTATION_VERTICAL);
+
+ plot->ctx = &plot->contexts[0];
+ igt_plot_ctx_init(plot->ctx);
+}
+
+/**
+ * igt_plot_fini:
+ * @plot: An #igt_plot_t instance
+ *
+ * Frees resources allocated during the life time of @plot.
+ */
+void igt_plot_fini(igt_plot_t *plot)
+{
+ unsigned int i;
+
+ for (i = 0; i < plot->n_valid_contexts; i++)
+ igt_plot_ctx_fini(&plot->contexts[i]);
+
+ igt_plot_axis_fini(&plot->x_axis);
+ igt_plot_axis_fini(&plot->y_axis);
+
+ cairo_destroy(plot->cr);
+ cairo_surface_destroy(plot->surface);
+}
+
+/**
+ * igt_plot_set_line_width:
+ * @plot: An #igt_plot_t instance
+ * @width: The new line width to use
+ *
+ * Set the color to use when drawing plots.
+ */
+void igt_plot_set_line_width(igt_plot_t *plot, double width)
+{
+ igt_plot_ctx_t *ctx = plot->ctx;
+
+ ctx->line_width = width;
+}
+
+static void igt_plot_set_vectors(igt_plot_t *plot,
+ igt_vector_t *x, igt_vector_t *y)
+{
+ igt_plot_ctx_t *ctx = plot->ctx;
+ double x_min, x_max, y_min, y_max;
+
+ ctx->x = igt_vector_ref(x);
+ ctx->y = igt_vector_ref(y);
+
+ /* X axis (sorted data) */
+ x_min = x->values[0];
+ x_max = x->values[x->n - 1];
+ ctx->x_range = x_max - x_min;
+ igt_plot_axis_add_range(&plot->x_axis, x_min, x_max);
+
+ /* Y axis */
+ igt_vector_get_min_max(y, &y_min, &y_max);
+ igt_plot_axis_add_range(&plot->y_axis, y_min, y_max);
+ ctx->y_range = y_max - y_min;
+}
+
+static void igt_plot_next_ctx(igt_plot_t *plot)
+{
+ unsigned int new_ctx_idx;
+ igt_plot_ctx_t *ctx;
+
+ new_ctx_idx = ++plot->n_valid_contexts;
+ assert(new_ctx_idx < IGT_PLOT_MAX_PLOTS);
+
+ /* keep most states for the next plot */
+ memcpy(&plot->contexts[new_ctx_idx], plot->ctx, sizeof(*plot->ctx));
+ ctx = plot->ctx = &plot->contexts[new_ctx_idx];
+
+ /* but reset the vector data */
+ ctx->x = ctx->y = NULL;
+ ctx->x_range = ctx->y_range = 0.0;
+}
+
+/**
+ * igt_plot_draw:
+ * @plot: An #igt_plot_t instance
+ * @x: X-axis data
+ * @y: Y-axis data
+ *
+ * Draw things on the @plot.
+ */
+void igt_plot_draw(igt_plot_t *plot, igt_vector_t *x, igt_vector_t *y)
+{
+ igt_plot_set_vectors(plot, x, y);
+ igt_plot_next_ctx(plot);
+}
+
+typedef struct {
+ char *text;
+ cairo_text_extents_t extents;
+ igt_align_t halign, valign;
+} igt_label_t;
+
+typedef struct {
+ double tick_label_padding; /* padding between label and axis */
+ double tick_label_font_size;
+ igt_box_t plot_area;
+ igt_label_t *x_tick_labels;
+ igt_label_t *y_tick_labels;
+} flush_t;
+
+static double plot_length(igt_plot_t *plot, double percent)
+{
+ return round(percent * min(plot->width, plot->height));
+}
+
+static void
+igt_plot_draw_text(igt_plot_t *plot, double x, double y, igt_label_t *label)
+{
+ /* XXX: bearings? */
+
+ switch (label->halign) {
+ case IGT_ALIGN_LEFT:
+ break;
+ case IGT_ALIGN_RIGHT:
+ x -= label->extents.width + label->extents.x_bearing;
+ break;
+ case IGT_ALIGN_CENTER:
+ default:
+ x -= (label->extents.width + label->extents.x_bearing) / 2;
+ break;
+ }
+
+ switch (label->valign) {
+ case IGT_ALIGN_TOP:
+ y += label->extents.height;
+ break;
+ case IGT_ALIGN_BOTTOM:
+ break;
+ case IGT_ALIGN_CENTER:
+ default:
+ y += label->extents.height / 2;
+ break;
+ }
+
+ cairo_move_to(plot->cr, x, y);
+ cairo_show_text(plot->cr, label->text);
+}
+
+static double fit(double p, double start, double range, double scale)
+{
+ return start + (range / 2 + p) * scale;
+}
+
+static void
+igt_plot_draw_one(igt_plot_t *plot, igt_plot_ctx_t *ctx, flush_t *flush)
+{
+ igt_box_t *area = &flush->plot_area;
+ igt_vector_t *x = ctx->x;
+ igt_vector_t *y = ctx->y;
+ double x_min, y_min, x_range, y_range, x_scale, y_scale, area_width,
+ area_height;
+ unsigned int i;
+
+ /*
+ * We don't use cairo's CTM to fit the data into the drawing area
+ * as we may want to draw screen-space things (ie. markers) at the
+ * same time. Also it's a bit impractical to derive things like
+ * line_width, which are screen-space dimensions, into the curve data
+ * space.
+ */
+ area_width = area->x2 - area->x1;
+ area_height = area->y2 - area->y1;
+ x_min = plot->x_axis.min;
+ y_min = plot->y_axis.min;
+ x_range = plot->x_axis.max - x_min;
+ y_range = plot->y_axis.max - y_min;
+ x_scale = area_width / x_range;
+ y_scale = area_height / y_range;
+
+ cairo_move_to(plot->cr,
+ fit(x->values[0], area->x1, x_range, x_scale),
+ fit(y->values[0], area->y2, y_range, -y_scale));
+ for (i = 1; i < x->n; i++)
+ cairo_line_to(plot->cr,
+ fit(x->values[i], area->x1, x_range, x_scale),
+ fit(y->values[i], area->y2, y_range, -y_scale));
+ cairo_set_line_cap(plot->cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(plot->cr, ctx->line_width);
+ cairo_stroke(plot->cr);
+
+}
+
+static void igt_plot_draw_ticks(igt_plot_t *plot, igt_plot_axis_t *axis,
+ double tick_length, flush_t *flush)
+{
+ unsigned int i;
+ igt_box_t *area = &flush->plot_area;
+ double area_width, area_height;
+
+ area_width = area->x2 - area->x1;
+ area_height = area->y2 - area->y1;
+
+ cairo_set_line_cap(plot->cr, CAIRO_LINE_CAP_SQUARE);
+ cairo_set_line_width(plot->cr, 1.0);
+
+ for (i = 0; i < axis->n_ticks; i++) {
+ double x, y;
+ igt_label_t *label;
+
+ if (axis->orientation == IGT_ORIENTATION_HORIZONTAL) {
+ x = area->x1 + i * area_width / (axis->n_ticks - 1);
+ y = area->y2;
+
+ cairo_move_to(plot->cr, SNAP(x), y);
+ cairo_line_to(plot->cr, SNAP(x), SNAP(y - tick_length));
+ cairo_stroke(plot->cr);
+
+ label = &flush->x_tick_labels[i];
+ y += flush->tick_label_padding;
+ } else {
+ x = area->x1;
+ y = area->y2 - i * area_height / (axis->n_ticks - 1);
+
+ cairo_move_to(plot->cr, x, SNAP(y));
+ cairo_line_to(plot->cr, SNAP(x + tick_length), SNAP(y));
+ cairo_stroke(plot->cr);
+
+ label = &flush->y_tick_labels[i];
+ x -= flush->tick_label_padding;
+ }
+
+ cairo_set_font_size(plot->cr, flush->tick_label_font_size);
+ igt_plot_draw_text(plot, x, y, label);
+ }
+}
+
+static void igt_plot_draw_axis(igt_plot_t *plot, flush_t *flush)
+{
+ igt_box_t *area = &flush->plot_area;
+ const double tick_length = plot_length(plot, 0.01);
+
+ /* X-axis */
+ cairo_move_to(plot->cr, area->x1, area->y2);
+ cairo_line_to(plot->cr, area->x2, area->y2);
+ igt_plot_draw_ticks(plot, &plot->x_axis, tick_length, flush);
+
+ /* Y-axis */
+ cairo_move_to(plot->cr, area->x1, area->y2);
+ cairo_line_to(plot->cr, area->x1, area->y1);
+ igt_plot_draw_ticks(plot, &plot->y_axis, tick_length, flush);
+
+}
+
+static void igt_plot_layout_tick_labels(igt_plot_t *plot,
+ igt_plot_axis_t *axis,
+ igt_label_t *labels,
+ double *max_size)
+{
+ unsigned int i;
+
+ *max_size = -DBL_MAX;
+
+ for (i = 0; i < axis->n_ticks; i++) {
+ igt_label_t *label = &labels[i];
+ double v = axis->min +
+ (axis->max - axis->min) * i / (axis->n_ticks - 1);
+
+ asprintf(&label->text, "%.02lf", v);
+ cairo_text_extents(plot->cr, label->text, &label->extents);
+
+ if (axis->orientation == IGT_ORIENTATION_HORIZONTAL) {
+ label->halign = IGT_ALIGN_CENTER;
+ label->valign = IGT_ALIGN_TOP;
+ if (label->extents.height > *max_size)
+ *max_size = label->extents.height;
+ } else {
+ label->halign = IGT_ALIGN_RIGHT;
+ label->valign = IGT_ALIGN_CENTER;
+ if (label->extents.width > *max_size)
+ *max_size = label->extents.width;
+ }
+ }
+}
+
+static void igt_plot_layout(igt_plot_t *plot, flush_t *flush)
+{
+ const double outer_padding = 0.10;
+ double max_width, max_height;
+
+ flush->tick_label_padding = plot_length(plot, 0.02);
+ flush->tick_label_font_size = round(0.015 * plot->width);
+
+ /* outer padding */
+ flush->plot_area.x1 = SNAP(plot->width * outer_padding);
+ flush->plot_area.y1 = SNAP(plot->height * outer_padding);
+ flush->plot_area.x2 = SNAP(plot->width * (1.0 - outer_padding));
+ flush->plot_area.y2 = SNAP(plot->height * (1.0 - outer_padding));
+
+ /* measure tick labels and adjust the plot area */
+ cairo_set_font_size(plot->cr, flush->tick_label_font_size);
+ igt_plot_layout_tick_labels(plot, &plot->x_axis, flush->x_tick_labels,
+ &max_height);
+ flush->plot_area.y2 -= max_height - flush->tick_label_padding;
+ igt_plot_layout_tick_labels(plot, &plot->y_axis, flush->y_tick_labels,
+ &max_width);
+ flush->plot_area.x1 += max_width + flush->tick_label_padding;
+}
+
+static void igt_plot_flush_init(igt_plot_t *plot, flush_t *flush)
+{
+ memset(flush, 0, sizeof(*flush));
+
+ flush->x_tick_labels = malloc(plot->x_axis.n_ticks *
+ sizeof(*flush->x_tick_labels));
+ flush->y_tick_labels = malloc(plot->y_axis.n_ticks *
+ sizeof(*flush->y_tick_labels));
+}
+
+static void igt_plot_flush_fini(igt_plot_t *plot, flush_t *flush)
+{
+ free(flush->x_tick_labels);
+ free(flush->y_tick_labels);
+}
+
+/**
+ * igt_plot_write:
+ * @plot: An #igt_plot_t instance
+ * @filename: File name
+ *
+ * Write @plot onto the disk in a file named @filename.
+ */
+void igt_plot_write(igt_plot_t *plot, const char *filename)
+{
+ flush_t flush;
+ unsigned int i;
+
+ igt_plot_flush_init(plot, &flush);
+
+ igt_plot_layout(plot, &flush);
+
+ igt_plot_draw_axis(plot, &flush);
+
+ for (i = 0; i < plot->n_valid_contexts; i++)
+ igt_plot_draw_one(plot, &plot->contexts[i], &flush);
+
+ cairo_surface_write_to_png(plot->surface, filename);
+
+ igt_plot_flush_fini(plot, &flush);
+}
diff --git a/lib/igt_plot.h b/lib/igt_plot.h
new file mode 100644
index 0000000..82ad10a
--- /dev/null
+++ b/lib/igt_plot.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __IGT_PLOT_H__
+#define __IGT_PLOT_H__
+
+#include <stdint.h>
+
+#include <cairo.h>
+
+#include "igt_types.h"
+
+/**
+ * igt_vector_t:
+ * @values: Array of doubles
+ * @n: Length of @values
+ *
+ * A ref-counted, fixed-length, array of doubles
+ *
+ * A simple, fixed-sized, vector of doubles
+ */
+typedef struct {
+ /*< private >*/
+ int ref;
+ /*< public >*/
+ unsigned int n;
+ double values[];
+} igt_vector_t;
+
+igt_vector_t *igt_vector_new(unsigned int n);
+igt_vector_t *igt_vector_new_from_array(const double *array, unsigned int n);
+igt_vector_t *igt_vector_new_from_array_u64(const uint64_t *array,
+ unsigned int n);
+igt_vector_t *igt_vector_linear(double min, double max, unsigned n);
+igt_vector_t *igt_vector_ref(igt_vector_t *v);
+void igt_vector_unref(igt_vector_t *v);
+void igt_vector_get_min_max(const igt_vector_t *v, double *min, double *max);
+
+/**
+ * igt_plot_axis_t:
+ *
+ * An axis.
+ */
+typedef struct {
+ /*< private >*/
+ igt_orientation_t orientation;
+ unsigned int n_ticks;
+ double min, max; /* range of the values on this axis */
+} igt_plot_axis_t;
+
+#define IGT_PLOT_MAX_PLOTS 32
+
+typedef struct {
+ /*< private >*/
+ igt_vector_t *x, *y;
+ double x_range, y_range;
+ double line_width;
+} igt_plot_ctx_t;
+
+/**
+ * igt_plot_t:
+ *
+ * Draw nice plots!
+ */
+typedef struct {
+ /*< private >*/
+
+ /* Cairo's corner */
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ /* plot-wide states */
+ unsigned int width, height;
+ igt_trbl_t margin;
+ igt_plot_axis_t x_axis, y_axis;
+
+ /* per draw command contexts */
+ igt_plot_ctx_t contexts[IGT_PLOT_MAX_PLOTS + 1];
+ unsigned int n_valid_contexts;
+ igt_plot_ctx_t *ctx;
+} igt_plot_t;
+
+/**
+ * igt_plot_style_t:
+ * @IGT_PLOT_LINE: Draw lines between data points
+ * @IGT_PLOT_POINT: Draw points at each data point
+ *
+ * The different types of plots we can draw.
+ */
+typedef enum {
+ IGT_PLOT_LINE,
+ IGT_PLOT_POINT,
+} igt_plot_style_t;
+
+void igt_plot_init(igt_plot_t *plot, unsigned int width, unsigned int height);
+void igt_plot_fini(igt_plot_t *plot);
+void igt_plot_set_line_width(igt_plot_t *plot, double width);
+void igt_plot_draw(igt_plot_t *plot, igt_vector_t *x, igt_vector_t *y);
+void igt_plot_write(igt_plot_t *plot, const char *filename);
+
+#endif /* __IGT_PLOT_H__ */
diff --git a/lib/tests/.gitignore b/lib/tests/.gitignore
index 6519406..d7a9174 100644
--- a/lib/tests/.gitignore
+++ b/lib/tests/.gitignore
@@ -5,8 +5,12 @@ igt_list_only
igt_no_exit
igt_no_exit_list_only
igt_no_subtest
+igt_plot
igt_segfault
igt_simple_test_subtests
igt_simulation
igt_stats
igt_timeout
+
+# files generated by igt_plot
+test_*.png
diff --git a/lib/tests/Makefile.sources b/lib/tests/Makefile.sources
index 58ae36b..9c5cf3f 100644
--- a/lib/tests/Makefile.sources
+++ b/lib/tests/Makefile.sources
@@ -4,6 +4,7 @@ check_PROGRAMS = \
igt_fork_helper \
igt_list_only \
igt_no_subtest \
+ igt_plot \
igt_simulation \
igt_simple_test_subtests \
igt_stats \
diff --git a/lib/tests/igt_plot.c b/lib/tests/igt_plot.c
new file mode 100644
index 0000000..be0e132
--- /dev/null
+++ b/lib/tests/igt_plot.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+#include "igt_core.h"
+#include "igt_plot.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
+#define SNAP_TO_PIXEL(d) ((uint64_t)(d) + 0.5)
+
+static void test_snap_to_pixel(void)
+{
+ static const struct { double input; double expected; } test_data[] = {
+ { 1.0, 1.5 }, { 1.1, 1.5 }, { 1.2, 1.5 }, { 1.3, 1.5 },
+ { 1.4, 1.5 }, { 1.5, 1.5 }, { 1.6, 1.5 }, { 1.7, 1.5 },
+ { 1.8, 1.5 }, { 1.9, 1.5 }, { 2.0, 2.5 }, { 2.1, 2.5 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(test_data); i++)
+ igt_assert_eq_double(SNAP_TO_PIXEL(test_data[i].input),
+ test_data[i].expected);
+}
+
+static void test_min_max(void)
+{
+ static const uint64_t s1[] =
+ { 47, 49, 6, 7, 15, 36, 39, 40, 41, 42, 43 };
+ static const uint64_t s2[] = { 40, 41, 7, 15, 36, 39 };
+ igt_vector_t *v1, *v2;
+ double min, max;
+
+ v1 = igt_vector_new_from_array_u64(s1, ARRAY_SIZE(s1));
+ v2 = igt_vector_new_from_array_u64(s2, ARRAY_SIZE(s2));
+
+ igt_vector_get_min_max(v1, &min, &max);
+ igt_assert_eq_double(min, 6);
+ igt_assert_eq_double(max, 49);
+
+ igt_vector_get_min_max(v2, &min, &max);
+ igt_assert_eq_double(min, 7);
+ igt_assert_eq_double(max, 41);
+
+ igt_vector_unref(v1);
+ igt_vector_unref(v2);
+}
+
+static void test_simple_plot(void)
+{
+ igt_vector_t *x, *y;
+ unsigned int i;
+ igt_plot_t plot;
+
+ x = igt_vector_linear(-1.0, 1.0, 200);
+
+ y = igt_vector_new(200);
+ for (i = 0; i < y->n; i++)
+ y->values[i] = sin(2 * M_PI * x->values[i]);
+
+ igt_plot_init(&plot, 800, 600);
+ igt_plot_draw(&plot, x, y);
+ igt_plot_write(&plot, "test_simple_plot.png");
+
+ igt_plot_fini(&plot);
+ igt_vector_unref(x);
+ igt_vector_unref(y);
+}
+
+igt_simple_main
+{
+ test_snap_to_pixel();
+ test_min_max();
+ test_simple_plot();
+}
--
2.1.0
More information about the Intel-gfx
mailing list