Re: Results on Claasen's -nocairo patches



And now the attachment...

On 7/13/06, Matt Hoosier <mwhoosier gmail com> wrote:
About tens days ago, Matthias Claasen posted a series of patches which
reverted some common drawing operations to use GDK calls rather than
Cairo. I'm referring particularly to the following post:

  http://mail.gnome.org/archives/performance-list/2006-July/msg00000.html

I've done a little bit of benchmarking using the attached program (be
sure to give the --overlay-widgets switch on its command line). If
you'll ignore the portion of that program which animates GdkImage's
onto the background of a window, the remainder of it essentially
counts the number of times per second that a GtkButton can be painted.

Without the -nocairo patches from Matthias's posts, I get about 65
frames per second on some ARM hardware with a 320x240x16 display.
Applying the patches, I get about 85 frames per second. I used the
X.org KDrive server from the 7.1 release. (If anybody wants the gory
details on which extensions were supported, I can supply those too.)

I understand that the consensus (reached earlier) among this group is
that the dashed line which indicates focus on a button is the culprit
on non-FPU Cairo systems. So the patch which backports the default Gtk
style (probably gtk_default_draw_focus) is likely the big helper here.

--Matt

#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkwindow.h>

#include <glib.h>

static void     on_destroy_cleanup      ( gpointer user_data );
static void     on_destroy_quit         ( gpointer user_data );
static gboolean on_drawing_area_exposed ( gpointer user_data );
static gboolean idle_invalidate_window  ( gpointer user_data );
static void     paint_moving_background ( GtkWidget *widget );

static gboolean overlay_widgets = FALSE;

static GOptionEntry entries[] =
{
	{
		.long_name = "overlay-widgets",
		.short_name = 0,
		.flags = 0,
		.arg = G_OPTION_ARG_NONE,
		.arg_data = &overlay_widgets,
		.description = "Whether to draw a widget atop the animated frame",
		.arg_description = (gpointer) 0
	},
	{ NULL }
};

int main
    (
     int argc,
     char *argv[]
    )
{
    gtk_init( &argc, &argv );
    gtk_rc_parse( "/usr/local/share/garminsdk-theme/default/gtkrc" );

	GOptionContext *option_context = g_option_context_new("- Rendering");
	g_option_context_add_main_entries(option_context, entries, NULL);
	g_option_context_parse(option_context, &argc, &argv, NULL);
	g_option_context_free(option_context);

    GtkWidget *window     = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    GtkWidget *alignment  = gtk_alignment_new( 0.5f, 0.5f, 0.0f, 0.0f );
    GtkWidget *button     = gtk_button_new_with_label( "Click Me" );

    gtk_container_add( GTK_CONTAINER( window ),     alignment );
    gtk_container_add( GTK_CONTAINER( alignment ),  button );

    g_signal_connect
        (
         G_OBJECT( window ),
         "destroy",
         G_CALLBACK( on_destroy_cleanup ),
         NULL
        );
    g_signal_connect
        (
         G_OBJECT( window ),
         "destroy",
         G_CALLBACK( on_destroy_quit ),
         NULL
        );
    g_signal_connect
        (
         G_OBJECT( alignment ),
         "expose_event",
         G_CALLBACK( on_drawing_area_exposed ),
         (gpointer) alignment
        );
    gtk_idle_add
        (
         idle_invalidate_window,
         (gpointer) alignment
        );
    gtk_widget_set_size_request
        (
        window,
        gdk_screen_get_width( gtk_widget_get_screen( window ) ),
        gdk_screen_get_height( gtk_widget_get_screen( window ) )
        );

    /* don't decorate window; fouls up Matchbox */
    gtk_window_set_decorated( GTK_WINDOW(window), FALSE );

    gtk_widget_show_all( window );
    gtk_main();

    return 0;
}

static gboolean on_drawing_area_exposed
    (
     gpointer user_data
    )
{
    paint_moving_background( GTK_WIDGET( user_data ) );

    /* return FALSE to indicate that usual expose handlers should run too */
    return overlay_widgets ? FALSE : TRUE;
}

static gboolean idle_invalidate_window
    (
     gpointer user_data
    )
{
    gtk_widget_queue_draw( GTK_WIDGET( user_data ) );
    return TRUE;
}

static void draw_line_in_buffer
    (
     GdkImage *image
    )
{
    static int last_x_position = 0;
    static int x_delta = 1;

    int lsb_index = image->byte_order == GDK_LSB_FIRST ? 0 : 1;
    int msb_index = image->byte_order == GDK_MSB_FIRST ? 0 : 1;

    char *mem = (char *)image->mem;

    /* set to white */
    memset
        (
         mem,
         (char) 0xff,
         image->bpl * image->height
        );

    /* draw a stripe */
    int x;
    int y;

    if (x_delta > 0)
    {
        if (last_x_position + x_delta < image->width)
        {
            x = last_x_position + x_delta;
        }
        else if (last_x_position - x_delta >= 0)
        {
            x = last_x_position - x_delta;
            x_delta = -1;
        }
        else
        {
            /* no change possible */
            x = last_x_position;
        }
    }
    else
    {
        if (last_x_position + x_delta >= 0)
        {
            x = last_x_position + x_delta;
        }
        else if (last_x_position - x_delta < image->width)
        {
            x = last_x_position - x_delta;
            x_delta = +1;
        }
        else
        {
            /* no change possible */
            x = last_x_position;
        }
    }

    /* save these results */
    last_x_position = x;

    int y_min = image->height / 4;
    int y_max = (image->height * 3) / 4;

    for (y = y_min; y < y_max; y++)
    {
        int offset = (y * image->bpl) + (x * image->bpp);

        /* ZPixmap format (which is always used by GdkImage) has
           565 format (RGB) in 16-bit mode */

        /*
        // red
        mem[offset + msb_index] = (char) 0xf8;
        mem[offset + lsb_index] = (char) 0x00;
        */

        /*
        // green
        mem[offset + msb_index] = (char) 0x07;
        mem[offset + lsb_index] = (char) 0xe0;
        */

        // blue
        mem[offset + msb_index] = (char) 0x00;
        mem[offset + lsb_index] = (char) 0x1f;
    }

}

static GdkImage *image;

static void paint_moving_background
    (
     GtkWidget *widget
    )
{
    static const int REPORT_GAP = 500;

    static struct timeval last_timeval;
    static int iteration = 0;

    if (iteration == 0)
    {
        gettimeofday( &last_timeval, NULL );
    }

    GdkDrawable *drawable = GDK_DRAWABLE( widget->window );

    int window_width;
    int window_height;
    int window_x;
    int window_y;
    int window_depth;

    gdk_window_get_geometry
        (
         widget->window,
         &window_x,
         &window_y,
         &window_width,
         &window_height,
         &window_depth
        );

    if (image == NULL || image->width != window_width || image->height != window_height)
    {
        if (image != NULL)
        {
            g_object_unref( image );
        }

        image = gdk_image_new
            (
             GDK_IMAGE_SHARED,
             gdk_drawable_get_visual( drawable ),
             window_width,
             window_height
            );

        /* fall back to non-shared memory XImage */
        if (image == NULL)
        {
            g_printf
                (
                "Warning; couldn't get XShmImage, falling back to "
                "standard slow version\n"
                );

            image = gdk_image_new
                (
                GDK_IMAGE_FASTEST,
                gdk_drawable_get_visual( drawable ),
                window_width,
                window_height
                );
        }
        else
        {
            g_printf( "Got XShmImage.\n" );
        }

        if (image != NULL)
        {
            switch (image->byte_order)
            {
                case GDK_LSB_FIRST:
                    printf( "Little endian\n" );
                    break;
                case GDK_MSB_FIRST:
                    printf( "Big endian\n" );
                    break;
            }
        }
        else
        {
            g_printf( "Couldn't allocate GdkImage\n" );
        }

        /*
        printf
            (
             "%dx%d window\n",
             window_width,
             window_height
            );
        printf
            (
             "%dx%d image, using %d bytes per pixel\n",
             image->width,
             image->height,
             image->bpp
            );

        printf
            (
             "%d bytes per line, %d bytes per pixel, ~%d pixels per line\n",
             image->bpl,
             image->bpp,
             image->bpl / image->bpp
            );
        */
    }

    if (image != NULL)
    {
        draw_line_in_buffer( image );

        gdk_draw_image
            (
             drawable,
             widget->style->fg_gc[GTK_STATE_NORMAL],
             image,
             0,
             0,
             0,
             0,
             image->width,
             image->height
            );

        /* Every so often, take timing data to figure out frame rate */
        if (iteration % REPORT_GAP == 0)
        {
            struct timeval now_timeval;
            gettimeofday( &now_timeval, NULL );

            double now_sec = (now_timeval.tv_usec / 1000000.0)
                             + now_timeval.tv_sec;
            double last_sec = (last_timeval.tv_usec / 1000000.0)
                              + last_timeval.tv_sec;

            double fps = REPORT_GAP / (now_sec - last_sec);

            last_timeval = now_timeval;

            printf( "Over [%f, %f] sec, %f fps\n", last_sec, now_sec, fps );
        }
    }
    else
    {
        printf( "Couldn't allocated shared memory image.\n" );
    }

    iteration++;
}

static void on_destroy_cleanup
    (
     gpointer user_data
    )
{
    if (image != NULL)
    {
        g_object_unref( image );
        image = NULL;
    }
}

static void on_destroy_quit
    (
     gpointer user_data
    )
{
    gtk_main_quit();
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]