Re: Cairo + GTK animation - high Xorg load

Uli Schlachter <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
On 27.10.2016 20:40, David J. Herzfeld wrote:
[...]
> I've updated the example again, to use exactly this model
> <http://pastebin.com/apaFiGyd>. Here, the Xorg load is decreased
> somewhat (at the expense of increased load for the actual application),
> but Xorg load is still ~15% and scales with the size of the window. I
> don't understand this - I've already done all of the necessary drawing
> work in the thread. I could understand if the thread load increased with
> the size of the surface (which it does not appear to), but why should
> Xorg's? Essentially I want to say - here is this pixel buffer - just
> display it as is. Perhaps this is too simplistic an understanding of how
> Cairo is interacting with the display system.

Well, the pixel data in the buffer is serialised and sent to the X
server over a unix socket 60 times per seconds. Let's assume the widget
has size 2000x1000, then you are pushing 2000*1000*4*60 bytes of data to
the server. That's about 457 MiB per second. Over a unix socket. With
multiple copies involved until the pixel data reaches its target. Media
players know why they don't use this path. ;-)

Also, I don't see the point of the thread, so I started from the
previous example.

Attached is an example which actually implements the behaviour proposed
by Stefan: Scrolling the old content to the left and only drawing the
newest point. For this to work it needs two surfaces, since it has to
copy things back and forth between them.

Also, as proposed by Stefan I use cairo_surface_create_similar for
creating the surfaces to draw to, so that cairo doesn't have to push
"all of that data" to the X server all the time.

CPU usage seems to be a bit lower, but I guess still higher than you wish.

Cheers,
Uli
-- 
“I’m Olaf and I like warm hugs.”

-- 
cairo mailing list
[email protected]
https://lists.cairographics.org/mailman/listinfo/cairo
in17XGjs.c (text/x-csrc, 4.1 KB)
/**
 * A simple animation using cairo and GTK+ 
 *
 * This program shows high /usr/libexec/Xorg load while running full screen on Linux
 *
 * Compile with:
 *   gcc `pkg-config --cflags --libs gtk+-3.0` -lm main.c
 */

#include <gtk/gtk.h>
#include <math.h>

#define NUM_WINDOWS (1u)
#define NUM_POINTS (1000u)
#define PERIOD (100u)

/* Local function prototypes */
static gboolean invalidate_cb(void *);
static gboolean drawing_area_draw_cb(GtkWidget *, cairo_t *, void *);

int main(int argc, char **argv)
{
	unsigned int i;
	gtk_init(&argc, &argv);
	
	for (i = 0; i < NUM_WINDOWS; i++)
	{
		GtkWidget *main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
		gtk_window_set_title(GTK_WINDOW(main_window), "Drawing example");
		gtk_window_set_default_size(GTK_WINDOW(main_window), 400, 400);
		GtkWidget *drawing_area = gtk_drawing_area_new();
		
		gtk_container_add(GTK_CONTAINER(main_window), drawing_area);
		gtk_widget_show_all(main_window);

		/* Create a  timer to invalidate our window at 60Hz */
		g_timeout_add(1000 / 60, invalidate_cb, drawing_area);

		/* Connect our redraw callback */
		g_signal_connect(drawing_area, "draw", G_CALLBACK(drawing_area_draw_cb), NULL);

		/* Connect the destroy signal */
		g_signal_connect(main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	}
	gtk_main();
}

static gboolean invalidate_cb(void *ptr)
{
	if (GTK_IS_WIDGET(ptr))
	{
		gtk_widget_queue_draw(GTK_WIDGET(ptr));
		return TRUE;
	}	
	return FALSE;
}

static inline float sine_to_point(int x, int width, int height)
{
	(void) width;
	return (height / 2.0) * sin(x * 2 * M_PI / (PERIOD)) + height / 2.0;
}

static int current_width = -1;
static int current_height = -1;
static cairo_surface_t *current_source = NULL;
static cairo_surface_t *current_temp = NULL;
static double last_x = -1;
static double last_y = -1;
static int redraw_number = 0;

static void update_drawing(int width, int height, cairo_surface_t *target)
{
	cairo_t *context;
	unsigned int i;

	if (width != current_width || height != current_height)
	{
		cairo_surface_destroy(current_source);
		cairo_surface_destroy(current_temp);

		current_width = width;
		current_height = height;
		current_source = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR, NUM_POINTS, height);
		current_temp = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR, NUM_POINTS, height);

		context = cairo_create(current_source);

		/* Redraw everything */

		/* Draw the background */	
		cairo_set_source_rgb(context, 1, 1, 1);
		cairo_paint(context);

		/* Draw a moving sine wave */
		cairo_set_source_rgb(context, 0.5, 0.5, 0);
		cairo_move_to(context, 0, sine_to_point(0 + redraw_number, width, height));
		for (i = 1; i < NUM_POINTS; i++)
		{
			cairo_line_to(context,  i, sine_to_point(i + redraw_number, width, height));	
		}	
		cairo_get_current_point(context, &last_x, &last_y);
		cairo_stroke(context);

		cairo_destroy(context);
	} else {
		cairo_surface_t *temp;

		/* Move everything left and add a new data point */
		context = cairo_create(current_temp);

		/* Scroll */
		cairo_set_source_surface(context, current_source, -1, 0);
		cairo_paint(context);
		cairo_set_source_rgb(context, 1, 1, 1);
		cairo_rectangle(context, NUM_POINTS-1, 0, 1, height);
		cairo_fill(context);

		/* Add new point */
		cairo_set_source_rgb(context, 0.5, 0.5, 0);
		cairo_move_to(context, last_x-1, last_y);
		cairo_line_to(context, NUM_POINTS-1, sine_to_point(NUM_POINTS + redraw_number, width, height));
		cairo_get_current_point(context, &last_x, &last_y);
		cairo_stroke(context);

		cairo_destroy(context);

		/* Swap surfaces */
		temp = current_temp;
		current_temp = current_source;
		current_source = temp;
	}
}

static gboolean drawing_area_draw_cb(GtkWidget *widget, cairo_t *context, void *ptr)
{
	int width, height;

	(void) ptr;

	width = gtk_widget_get_allocated_width(widget);
	height = gtk_widget_get_allocated_height(widget);
	update_drawing(width, height, cairo_get_target(context));

	/* Draw the background */	
	cairo_set_source_rgb(context, 1, 1, 1);
	cairo_paint(context);

	/* Draw the content */
	cairo_set_source_surface(context, current_source, 0, 0);
	cairo_paint(context);

	redraw_number++;
	return TRUE;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.