[cairo] Performance of rendering long, angled lines in Cairo
Vladimir Vukicevic
vladimir at pobox.com
Fri Oct 31 12:41:17 PDT 2008
On 10/31/08 12:22 PM, Giles Westerfield wrote:
> Hello,
>
> I am relatively new to Cairo and this is my first time using the mailing
> list, so please be nice.
>
> I'm currently using Cairo to create a HUD interface in a flight
> simulator, and I'm encountering significant drawing performance issues
> in certain situations. Since my application is a HUD, it needs to be
> redrawn very quickly, and the total drawing time must not exceed 16ms in
> order to maintain a smooth 60fps experience.
>
> Let me give you some specifics about my system: I'm using multiple
> windows PCs, ranging from a single core Pentium M laptop 1gig ram to a
> quad core 4gig ram desktop simulator, and this issue occurs on all of
> them. I'm using Visual C++ in Visual Studio 2008 Professional and am
> working with the current 1.8.0 build of Cairo. I got the binaries and
> includes from the GTK+ for Windows site.
>
> My application draws many things onto the screen and is generally very
> fast, but I've encountered situations where it takes absurdly long to
> draw something very simple (i.e. 150ms to draw a straight line). In
> particular this performance issue generally occurs when long lines are
> drawn at an angle across the screen, or when large shapes, such as a
> call to cairo_rectangle to render a full screen (non-filled) square are
> drawn. I will further clarify a few cases where I've noticed performance
> issues.
>
> First, I will share how I have set up my cairo environment. I'm using
> Cairo to render to an image buffer using 8 bit alpha. The reason for
> this is that I'm using OpenGL as a front end to examine the alpha values
> in the buffer (either on or off) and draw each individual pixel to the
> screen in a single color. If this seems convoluted to you, just accept
> it for now. Here's the code I use to set up the context:
>
> cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_A8,
> 1280, 1024);
> cairo_t *cr = cairo_create(surface);
> cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
> cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
> CAIRO_FONT_WEIGHT_BOLD);
> cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
>
> Now, on to the issues. Note that the following timing is done on the
> single core Pentium M laptop, but I've noticed similar behavior on the
> quad core, and the fact that there is such a large difference between
> the cases concerns me:
>
> 1. The first issue I mentioned having to do with long, angled lines is
> manifested in the following way: When I draw a single 5 pixel wide line
> horizontally across the screen like so, it takes less than 1ms to draw:
>
> cairo_set_line_width(cr, 5.0);
> cairo_move_to(cr, 50.0, 50.0);
> cairo_line_to(cr, 1230.0, 50.0);
> cairo_stroke(cr);
>
> However, if I rotate the line to a 45 degree angle, it takes anywhere
> from 80 to 200ms to draw:
>
> cairo_rotate(45.0*3.14159/180.0);
> cairo_set_line_width(cr, 5.0);
> cairo_move_to(cr, 50.0, 50.0);
> cairo_line_to(cr, 1230.0, 50.0);
> cairo_stroke(cr);
>
> [...]
>
> 2. The second issue has to do with the seeming fact that calling
> cairo_rectangle is much slower than drawing individual lines and
> rendering each one separately. For example, the following rectangle call
> takes over 200ms:
>
> cairo_set_line_width(cr, 5.0);
> cairo_rectangle(cr, 50, 50, 1180, 924);
> cairo_stroke(cr);
>
> ...while drawing and rendering each line of the rectangle separately
> takes 0-50ms, whihc is still longer than I'd like, but significantly
> less than rendering the whole rectangle at once:
Both of your problems are artifacts of the way that cairo rasterizes
paths, which I'm hoping will be fixed soon with the scanline rasterizer.
What happens for the rectangle stroke case is that the stroke
operation eventually generates trapezoids describing the regions to be
filled for the stroke; in your case, these end up on non-pixel-aligned
locations (2.5 pixels to the sides of the path describing the
rectangle). If they ended up on pixel-aligned locations, (e.g. a
rectangle from 50.5,50.5 instead of 50,50), you'd end up hitting a fast
path that would decompose that into a set of integer aligned rectangular
fills.
But since you're hitting the general case, the first thing that happens
is that a large temporary surface big enough to encompass the set of
trapezoids to be rendered is created -- in your case somewhere around
1200 x 950. The trapezoids are rendered to that, and then that surface
is used as a mask for the final filling operation. You can see the
problem -- that's a large surface, and you're both rendering to it and
then compositing with it, not to mention creating/destroying it just to
execute that simple stroke!
For the diagonal case, it's not possible to optimize it as rectangular
fills, so you end up creating the large temporary surface which can
contain the entire diagonal line (which is essentially the worst case
scenario).
big-diagonal-lines would actually make a great perf test...
- Vlad
More information about the cairo
mailing list