Hola list!

I started to write a little Cairo animation to better introduce myself
to the wonders of it, and thought I'd do a little Cairo benchmarking
with it on the 770.

The setup:

Compiled cairo 1.0.2 with vanilla settings, and copied to the device.
Compiled my demo with -O3
The device was not the latest HW nor SW, so the results might be a tad
worse than they are on the current devices. Haven't rerun the
benchmark yet on newer devices.

The demo:

It consists of:
 - creating a XLib surface from the X window
 - creating image surface for background (only on startup)
 - copying from that to a buffer surface to clear bg when drawing
 - drawing a simple shape with lines and few bezier curves on the buffer surface
 - copying the drewn areas to the xlib surface

I don't know if this is the most efficient way to draw with cairo, I
might be doing something foolish here and slowing down the process
(please say that I do?-).

The results:

Not that convincing.

I ran the demo with 1, 5 and 10 objects drawn simultaniously, and the
FPS calculated from those are 5.71, 2.6 and 1.51.

So nowhere near "smooth" animation.

I tried to compile the cairo with the softfloat toolchain in
scratchbox but that made cairo refuse to draw anything (not sure what
goes wrong there), although it doesn't crash or complain.

The timings for the last three items in the above list tell that the
slowest parts are (not suprisingly) the item drawing (0.048s per item
consistently) and the transferring to the xlib surface (0.099s for 1
item, 0.115s for 10 items).

Intresting note is that the xlib surface transfer is apparently well
buffered, and does not grow linearily with the item count. Still the
initial hit is big enough.

This is of course presented in the context of animation, but I think
that if you can't do smooth animation (somewhere between 10-15FPS)
with simple objects, it won't be fast enough for UI either.

The biggest bottleneck seems to be indeed the object drawing (I'd
guess the beziers are to blame), but I intend to test drawing
different types of elements once I get the time, plus a better
profiling of what's taking so long and where.

The test results are attached, as well as the demo (the code is little
messy and contains excess stuff as it's been "evolving").

--
Kalle Vahlman, [EMAIL PROTECTED]
Powered by http://movial.fi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include <cairo.h>
#include <cairo-xlib.h>

#define PI 3.1415926535
#define WIDTH  720 /* 720 800 */
#define HEIGHT 420 /* 420 480 */

#define NUM_LEAFS 5

typedef struct win {
  Display *dpy;
  int scr;

  Window win;
  GC gc;

  int width, height;
  KeyCode quit_code;

  cairo_surface_t *surface;
  cairo_surface_t *bg_surface;

} win_t;

typedef struct leaf leaf_t;

struct leaf {
  int x;
  int y;

  int old_x;
  int old_y;
  
  int phase;
  
  int direction;

  int potential;
  int kinetic;

  int x_speed;
  int y_speed;

  void (*draw_leaf) (cairo_t *cr, leaf_t *leaf);


};

static void win_init(win_t *win);
static void win_deinit(win_t *win);
static void win_draw(win_t *win);
static void win_handle_events(win_t *win);

/* Drawing functions */
static void draw_background (cairo_t *cr, win_t *win);
static void draw_squirle (cairo_t *cr, leaf_t *leaf);
static void draw_box (cairo_t *cr, leaf_t *leaf);

/* World alteration */
static void advance_time (int delta);

leaf_t *leafs;

int
main(int argc, char *argv[])
{
  int i;
  win_t win;

  win.dpy = XOpenDisplay(0);
  win.surface = NULL;

  if (win.dpy == NULL) {
  	fprintf(stderr, "Failed to open display\n");
  	return 1;
  }

  leafs = (leaf_t *) calloc (NUM_LEAFS, sizeof(leaf_t));
  if (leafs == NULL)
    {
  	fprintf(stderr, "Failed to allocate memory\n");
  	return 1;
    }
  for (i=0; i<NUM_LEAFS; i++)
    {
      leafs[i].x = 50 + i*i*i;
      leafs[i].y = -1 * ((i%2)*(HEIGHT/2) ) + (NUM_LEAFS - i) * i * -1;
      leafs[i].old_x = 0;
      leafs[i].old_y = 0;
      leafs[i].phase = 1;
      leafs[i].direction = -1;
      leafs[i].potential = 25-(i*2) + (i%2)*i;
      leafs[i].kinetic = 0;
      leafs[i].x_speed = 4;
      leafs[i].y_speed = 4;
      leafs[i].draw_leaf = draw_box;
    }

  win_init(&win);

  win_handle_events(&win);

  win_deinit(&win);

  XCloseDisplay(win.dpy);

  return 0;
}

static void
win_init(win_t *win)
{
    Window root;
    Visual *visual;
    cairo_t *cr;

    win->width = WIDTH;
    win->height = HEIGHT;

    root = DefaultRootWindow(win->dpy);
    win->scr = DefaultScreen(win->dpy);

    win->win = XCreateSimpleWindow(win->dpy, root, 0, 0,
                                   win->width, win->height, 0,
                                   WhitePixel(win->dpy, win->scr),
                                   WhitePixel(win->dpy, win->scr));

    visual = DefaultVisual(win->dpy, DefaultScreen (win->dpy));

    win->surface = cairo_xlib_surface_create (win->dpy, win->win, visual,
                                              win->width, win->height);

    win->bg_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
  				 win->width, win->height);

    cr = cairo_create(win->surface);
    cairo_set_source_rgba (cr, 1, 1, 1, 1);
    cairo_rectangle (cr, 0, 0, win->width, win->height);
    cairo_fill (cr);
    cairo_stroke (cr);
    cairo_destroy (cr);

    cr = cairo_create(win->bg_surface);
    cairo_set_source_rgba (cr, 1, 1, 1, 1);
    cairo_rectangle (cr, 0, 0, win->width, win->height);
    cairo_fill (cr);
    draw_background(cr, win);
    cairo_stroke (cr);
    cairo_destroy (cr);


    win->quit_code = XKeysymToKeycode(win->dpy, XStringToKeysym("Escape"));

    XSelectInput(win->dpy, win->win,
		 KeyPressMask
		 |StructureNotifyMask
		 |ExposureMask);

    XMapWindow(win->dpy, win->win);

}

static void
win_deinit(win_t *win)
{
    cairo_surface_destroy (win->surface);
    XDestroyWindow(win->dpy, win->win);
}

static int foo = 0;

static void
win_draw(win_t *win)
{
  static int angle = 0;
  int i;
  cairo_surface_t *surface;
  cairo_t *cr;
  struct timeval t;

  gettimeofday (&t, NULL);
  printf ("%ld.%06ld\t",
          t.tv_sec, t.tv_usec);
  
  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
  			 win->width, win->height);
  cr = cairo_create(surface);
  cairo_set_source_rgba (cr, 1, 1, 1, 1);
  cairo_set_source_surface (cr, win->bg_surface, 0, 0);
  for (i=0; i<NUM_LEAFS; i++)
    {
      cairo_rectangle (cr, leafs[i].old_x-5-abs(leafs[i].x_speed)*2,
                           leafs[i].old_y-5-abs(leafs[i].y_speed)*2,
                           35+abs(leafs[i].x_speed)*2,
                           35+abs(leafs[i].y_speed)*2);
    }
  cairo_fill (cr);
  cairo_stroke (cr);

  gettimeofday (&t, NULL);
  printf ("%ld.%06ld\t",
          t.tv_sec, t.tv_usec);

  cairo_set_source_rgba (cr, 0, 0, 0, 1);

  for (i = 0; i < NUM_LEAFS; i++)
    {
      cairo_save (cr);
      cairo_move_to (cr, 0, 0);
      cairo_translate (cr, leafs[i].x, leafs[i].y);
      leafs[i].draw_leaf (cr, &leafs[i]);
      cairo_stroke (cr);
      cairo_restore (cr);
    }

  cairo_surface_flush (surface);
  cairo_destroy(cr);

  gettimeofday (&t, NULL);
  printf ("%ld.%06ld\t",
          t.tv_sec, t.tv_usec);

  cr = cairo_create(win->surface);
  cairo_set_source_surface (cr, surface, 0, 0);
  for (i=0; i<NUM_LEAFS; i++)
    {
      cairo_rectangle (cr, leafs[i].old_x-5-abs(leafs[i].x_speed)*2,
                           leafs[i].old_y-5-abs(leafs[i].y_speed)*2,
                           35+abs(leafs[i].x_speed)*2,
                           35+abs(leafs[i].y_speed)*2);
    }
/*
  cairo_rectangle (cr, 0, 0, win->width, win->height);
*/
  cairo_fill (cr);
  cairo_stroke (cr);
  cairo_destroy(cr);
  cairo_surface_destroy (surface);

  gettimeofday (&t, NULL);
  printf ("%ld.%06ld\n",
          t.tv_sec, t.tv_usec);
}

static void
win_refresh_bg(win_t *win)
{
  int i;
  cairo_surface_t *surface;
  cairo_t *cr;
  struct timeval t;
  
  cr = cairo_create(win->surface);
  cairo_set_source_rgba (cr, 1, 1, 1, 1);
  cairo_set_source_surface (cr, win->bg_surface, 0, 0);
  cairo_rectangle (cr, 0, 0, win->width, win->height);
  cairo_fill (cr);
  cairo_stroke (cr);
  cairo_destroy (cr);
  cairo_surface_flush (win->surface);

}

static void
win_draw_bg(win_t *win)
{
  cairo_t *cr;

  cr = cairo_create(win->bg_surface);

  cairo_set_source_rgba (cr, 1, 1, 1, 1);
  cairo_rectangle (cr, 0, 0, win->width, win->height);
  cairo_fill (cr);

  draw_background (cr, win);

  cairo_stroke (cr);
  cairo_destroy(cr);
  
}

static void
win_handle_events(win_t *win)
{
  int frames, last_frames;
  int start;
  int last_drawn;
  int pending;
  struct timeval t;
  XEvent xev;

  gettimeofday(&t, NULL);
  start = t.tv_sec;
  frames = 0;
  last_frames = 0;
  last_drawn = 0;
  pending = 0;
  
  sleep(1);
  
  while (1)
    {

      gettimeofday(&t, NULL);
      pending = XEventsQueued(win->dpy, QueuedAlready);
      while (pending == 0)
        {
          win_draw(win);
          XSync (win->dpy, False);
          frames++;
          advance_time (1);
          pending = XEventsQueued(win->dpy, QueuedAlready);
/*
          gettimeofday(&t, NULL);
          if (last_frames + 5 < t.tv_sec)
            {
              last_frames = t.tv_sec;
              printf ("FPS:  (%i / %i) %0.2f\n",
                frames, (t.tv_sec - start),
                ((float)frames / (float)(t.tv_sec - start)));
            }
*/
        }

      if (pending > 0)
        {
          XNextEvent(win->dpy, &xev);
          switch(xev.type)
            {
              case KeyPress:
                {
                  XKeyEvent *kev = &xev.xkey;

                  if (kev->keycode == win->quit_code)
                    {
                      gettimeofday(&t, NULL);
                      printf ("Total FPS:  (%i / %i) %0.2f\n",
                       frames, (t.tv_sec - start),
                       ((float)frames / (float)(t.tv_sec - start)));
                      return;
                    }
                }
              break;
              case ConfigureNotify:
                {
                  XConfigureEvent *cev = &xev.xconfigure;

                  win->width = cev->width;
                  win->height = cev->height;

                  win_draw_bg(win);
                  win_refresh_bg (win);
                }
              break;
              case Expose:
                {
                  XExposeEvent *eev = &xev.xexpose;

                  if (eev->count == 0)
                    {
                      win_draw_bg(win);
                      win_refresh_bg (win);
                      win_draw(win);
                      XSync (win->dpy, False);
                      frames++;
                    }
                }
              break;
          }
      }
    else
      {
        win_draw(win);
        XSync (win->dpy, False);
        frames++;
        advance_time (1);
      }
  }
}

static void draw_background (cairo_t *cr, win_t *win)
{
  cairo_pattern_t *pat;
  
  cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);

  /* Inner light border */
  cairo_set_source_rgb(cr, 0.96, 0.95, 0.87);

  /* Horizontal */
  cairo_set_line_width (cr, 8);
  cairo_move_to (cr, 0, win->height-5);
  cairo_rel_line_to (cr, win->width-17, 0);
  cairo_rel_line_to (cr, 6, -4);
  cairo_rel_line_to (cr, 0, - win->height);
  cairo_stroke (cr);

  /* Outer dark border */
  cairo_set_source_rgb(cr, 0.5, 0.43, 0.43);

  cairo_set_line_width (cr, 8);
  cairo_move_to (cr, 0, win->height-1);
  cairo_rel_line_to (cr, win->width-28, 0);
  cairo_rel_curve_to (cr, 24, 0, 24, 0, 24, -24);
  cairo_move_to (cr, win->width-4, win->height);
  cairo_rel_line_to (cr, 0, -win->height);
  cairo_stroke (cr);

  pat = cairo_pattern_create_linear (0, win->height/2, 10, win->height/2);
  cairo_pattern_add_color_stop_rgba (pat, 0, 0, 0, 0, 0.22);
  cairo_pattern_add_color_stop_rgba (pat, 1, 1, 1, 1, 0);
  cairo_rectangle (cr, 0, 0, 10, win->height);
  cairo_set_source (cr, pat);
  cairo_fill (cr);
  cairo_pattern_destroy (pat);
  cairo_stroke(cr);

}

static void advance_time (int delta)
{
  int i;
  
  for (i = 0; i < NUM_LEAFS; i++)
    {
      leafs[i].old_x = leafs[i].x;
      leafs[i].old_y = leafs[i].y;

      if (leafs[i].kinetic <= 0)
        {
          leafs[i].kinetic = 0;
          leafs[i].phase = 1;
          leafs[i].direction = leafs[i].direction * -1;
        }
      if (leafs[i].potential <= 0)
        {
          leafs[i].potential = 0;
          leafs[i].phase = 2;
        }
      
      if (leafs[i].phase == 1)
        {
          leafs[i].potential -= leafs[i].phase;
          leafs[i].kinetic += leafs[i].phase;
          leafs[i].y += leafs[i].y_speed * leafs[i].phase;
        }
      else
        {
          leafs[i].potential += leafs[i].phase;
          leafs[i].kinetic -= leafs[i].phase;
          leafs[i].y -= leafs[i].y_speed;
        }
      leafs[i].x += (leafs[i].x_speed * leafs[i].phase) * leafs[i].direction;
      
      if (leafs[i].y > HEIGHT)
        {
          leafs[i].y = - 40;
        }
      
    }
}

static void draw_squirle (cairo_t *cr, leaf_t *leaf)
{
  cairo_set_source_rgb(cr, 0.6, 0.43, 0.43);
  cairo_set_line_width (cr, 1);
  cairo_rel_curve_to (cr, 20, 0, 0, 20, 20, 20);
  
  cairo_fill (cr);
}


static void draw_box (cairo_t *cr, leaf_t *leaf)
{
  cairo_set_source_rgb(cr, 0.3, 0.43, 0.43);
  cairo_set_line_width (cr, 1);
  cairo_move_to (cr, 9, 0);
  cairo_line_to (cr, 9, 3);
  cairo_curve_to (cr, 0, 12, 0, 15, 10, 20);
  cairo_curve_to (cr, 20, 16, 20, 15, 11, 3);
  cairo_line_to (cr, 11, 0);
  cairo_close_path (cr);
}

Attachment: cairo-on-770.gnumeric
Description: application/gnumeric

_______________________________________________
maemo-developers mailing list
maemo-developers@maemo.org
https://maemo.org/mailman/listinfo/maemo-developers

Reply via email to