5

I wrote an app that uses Cairo to draw things on screen (on a Gtk::DrawingArea, to be exact). It needs to redraw everything frequently. It turns out, that despite the draphics drawn are very simple, the X server uses LOTS of CPU when redrawing, and the applications works terribly slow. Is there any way to speed this up? Or maybe I shouldn't use DrawingArea and some other widget?

What I draw is set of rectangles, which user can move around by dragging them with mouse. The whole drawing is done withing on_expose_event, but as the mouse pointer moves around (with button pressed), I call queue_draw() to refresh drawing.

rafalcieslak
  • 915
  • 1
  • 12
  • 25
  • 1
    Description of what are you drawing and how are you drawing would probably bring more insites. – Artyom Jul 31 '11 at 10:58
  • Please add some numbers. Just a little counter in the expose event to measure the frame rate. Maybe we are talking about to many refreshs and not a slow drawing. A high resolution laser mouse can produce a lot mouse motion events. – Lothar Aug 01 '11 at 00:27

4 Answers4

9

Just a couple things to check:

Is your drawing done in the expose event?

Draw your image to a Cairo Surface, and then in the expose event simply copy from that surface to the widget's surface.

Are you clipping and drawing only the region necessary?

The expose event gives you an X, Y, width, height of the area that needs to be redrawn. In cairo, create a rectangle on your surface with these dimensions and call clip so that you aren't wasting time redrawing stuff that doesn't need to be.

John Ledbetter
  • 13,557
  • 1
  • 61
  • 80
3

Drawing is expensive, especially text drawing has become the most CPU expensive task of a GUI.

The only way to speed this up is to reduce the amount of drawn items. Check if you really only draw the items that are necessary. The expose-event is giving you a rectangle. Only refresh this part of the widget.

Maybe cache items in a bitmap.

For smooth scrolling for example it can help to draw the content into a bitmap that is for example 500 pixels larger so that in most cases you just need to copy the image and don't draw anything at all (you usually get expose rectangles that are just 5 to 10 pixels high during scrolling).

But you need to give us more information what you are drawing and what the system load is to get a better answer.

Lothar
  • 12,537
  • 6
  • 72
  • 121
  • Edited the question to include details. – rafalcieslak Jul 31 '11 at 17:16
  • 1
    Try to use "queue_draw_area" instead of using "queue_draw" and restrict the area to draw. If you use queue_draw then the whole widget is drawn and a lot of data is transfered. – Lothar Aug 01 '11 at 00:25
0

I found this article about threaded drawing in cairo to solve the speed-problem, maybe that helps:

http://cairographics.org/threaded_animation_with_cairo/

About the high CPU usage:

Do you have proper hardware accelerated drivers installed for X?

  • Sure I do! No any other application causes X to use CPU that much. And thanks for the article, I'll give it a look. – rafalcieslak Jul 31 '11 at 12:36
  • Oh, although the trick in this article is indeed cool, and I will make some use of it, this won't solve this issue, because it speeds up things, in case the drawing is complicated. Mine isn't just a few rectangles. – rafalcieslak Jul 31 '11 at 12:40
  • I am sorry that it did not help –  Jul 31 '11 at 13:07
  • The gdk_threads_enter/gdk_threads_leave implementation is not useable to improve performance, it's a full block of the GTK main loop, so you will never get a better result then a single CPU core. The main reason for this threading hack is to allow easier communication from background threads with Widgets. There are a lot of documented GTK problems with this approach on Windows too - thats why the best advise is to not use it. (Thats why i downvoted) – Lothar Jul 31 '11 at 16:42
  • Pure drawing alone in a background thread should be possible with Cairo, unfortunately when you use text drawing Pango is used and Pango is still not thread safe (some people started working on it in May this years - yes after 10 years of ignorance). – Lothar Jul 31 '11 at 16:44
  • While the article was not of help, I think there was a genuine attempt to help here. I don't think people should be punished for trying to help. – James Hurford Aug 02 '11 at 06:03
0

I finally forced to use maximally 25 fps, by using a lock flag.

bool lock = 0;
bool needs_redraw = 0;

void Redraw(){
    if(lock){
        needs_redraw = 1;
        return;
    }

    //draw image to a surface

    needs_redraw = 0;
    lock = 1;
    Glib::signal_timeout().connect(Unlock, 20);

    queue_draw();
}

bool Unlock(){
    lock = 0;
    if(needs_redraw) Redraw();
    return false;
}

void on_expose_event(something){
    //copy image from surface to widget's context
}

This is a sample code, but that's the idea. It will disallow redraw to be done more often then once per 20 ms.

rafalcieslak
  • 915
  • 1
  • 12
  • 25