Any way to speed up/reduce CPU usage when drawing with Cairo?
Asked Answered
Z

4

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.

Z answered 31/7, 2011 at 10:46 Comment(2)
Description of what are you drawing and how are you drawing would probably bring more insites.Ayeaye
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.Snare
Z
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.

Z answered 19/8, 2011 at 17:9 Comment(0)
L
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.

Leath answered 31/7, 2011 at 16:45 Comment(2)
Edited the question to include details.Z
Probably the first suggestion of yours may work. How can I do that? Which surface type should I use, how do I get it's context to draw on it, and finally how to perform a quick copy?Z
S
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.

Snare answered 31/7, 2011 at 16:38 Comment(2)
Edited the question to include details.Z
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.Snare
I
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?

Iatric answered 31/7, 2011 at 10:55 Comment(6)
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.Z
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.Z
I am sorry that it did not helpIatric
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)Snare
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).Snare
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.Ancel
Z
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.

Z answered 19/8, 2011 at 17:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.