On Sun, Jan 31, 2010 at 10:33 PM, Alan W. Irwin
<ir...@beluga.phys.uvic.ca> wrote:
> On 2010-01-31 16:50-0500 Hazen Babcock wrote:
>
>> Alan W. Irwin wrote:
>>>
>>> So the issue must be something special about the way xcairo implements
>>> the
>>> special interactive needs of example 17 that is done extremely
>>> inefficiently
>>> compared to all other interactive devices and also inefficiently compared
>>> to
>>> -dev pscairo.
>>
>> My guess is that this is due to this example heavily exercising the
>> PLESC_FLUSH pathway. In the case of the xcairo device I think that each
>> PLESC_FLUSH causes a complete redraw of the plot window from an off screen
>> image, which is probably located on your server computer. In the case of the
>> pscairo device PLESC_FLUSH is ignored (since there is nothing to see
>> anyway). Could you comment out the call to the blit_to_x() in PLESC_FLUSH in
>> plD_esc_xcairo and see if this improves the speed? You won't see anything
>> until the plot finishes, but hopefully it will get to the end a lot faster.
>
> That is the exact issue.  If you comment out line 1735 in cairo.c which is
> "blit_to_x() in PLESC_FLUSH in plD_esc_xcairo", then xcairo loses its
> interactive capability (black screen until the end), but it reaches the end
> very quickly (and with no significant network traffic) and then displays the
> correct final result.
>
>>
>> If this is the problem then I'm not sure what the next step is. As I
>> understand it, Hez added the off screen image rendering to improve the
>> response of this device in some other redraw situations.
>
> Now that the issue has been narrowed down, I would recommend you and Hez
> have a look at what other interactive device drivers do to implement
> interactive effects efficiently.  For example, Alban's final qt patch had
> the following log message (in part) that is relevant to the present
> discussion:
>
> r10346 | airwin | 2009-08-26 08:48:56 -0700 (Wed, 26 Aug 2009) | 13 lines
>
> AWI for Alban Rochel.  Commit patch which does the following:
>
> + Better implementation of flushing, resulting in x17c -dev qtwidget
> running about as fast as x17c -dev xwin, a large improvement in qtwidget
> animation speed.
>
> In my opinion whatever algorithm he used there (IIRC it was a buffering
> technique of some kind) to drastically improve interactive speed for
> qtwidgets is likely to work well for the xcairo case.  Revision 10346 is a
> big commit that changed other things as well so it will take some effort to
> figure out which part of it is relevant to interactive speed, but with some
> effort I hope you will be able to get some good ideas from that commit and
> also list discussion near the date of 2009-08-26.
>

Alan,

I have attached a patch which may solve the problem for you, and if
not it should at least significantly mitigate it.  I'm not sure how it
will act over a networked X connection, but it should be more
efficient than the current implementation as it uses a "dirty
rectangle" approach to track which portion of the plot has been
updated since the last blit to screen, rather than always blitting the
full off-screen buffer to X on a call to plflush.  From what I
understand, it is not the same as the approach used by the qtwidget
device.

This patch is not ready to commit as there are still some performance
and rendering issues for example 20 and possibly others.  I would like
to test the impact this concept has on your speed issues with example
17 though, to see if it is worth completing.  There are occasional
black flickers when using this patch with example 17 on my system as
well with this patch.  I think I know the origin of the issue so I am
going to look in to that next.

I hope this speeds things up significantly for you!  On my system,
x17c is slightly faster with this patch and xcairo than it is with
qtwidget.

Hez
diff --git a/drivers/cairo.c b/drivers/cairo.c
index a11e8ab..493761b 100644
--- a/drivers/cairo.c
+++ b/drivers/cairo.c
@@ -72,6 +72,10 @@
 #define MAX_STRING_LEN       500
 #define MAX_MARKUP_LEN       MAX_STRING_LEN * 10
 
+/* Values to reset the dirty rectangle regions */
+#define DIRTY_MIN (1.0e50)
+#define DIRTY_MAX (-1.0e50)
+
 static int    text_clipping;
 static int    text_anti_aliasing;
 static int    graphics_anti_aliasing;
@@ -102,6 +106,11 @@ typedef struct
     char            *pangoMarkupString;
     short           upDown;
     float           fontSize;
+    /* Keep track of the bounding box of the modified portion of the surface */
+    double          dirty_x1;
+    double          dirty_y1;
+    double          dirty_x2;
+    double          dirty_y2;
 #if defined ( PLD_xcairo )
     cairo_surface_t *cairoSurface_X;
     cairo_t         *cairoContext_X;
@@ -280,7 +289,13 @@ void start_raster( PLStream *pls )
 
     /* Create an image surface and context for the offscreen rendering */
     aStream->cairoSurface_raster =
-        cairo_image_surface_create( CAIRO_FORMAT_ARGB32, pls->xlength, pls->ylength );
+    /*
+        cairo_surface_create_similar( aStream->cairoSurface,
+                                      CAIRO_CONTENT_COLOR,
+                                      pls->xlength, pls->ylength );
+    */
+        cairo_image_surface_create( CAIRO_FORMAT_RGB24,
+                                    pls->xlength, pls->ylength );
     aStream->cairoContext_raster = cairo_create( aStream->cairoSurface_raster );
 
     /* Disable antialiasing for the raster surface.  The output seems to look
@@ -312,6 +327,9 @@ void end_raster( PLStream *pls )
 
     aStream = (PLCairo *) pls->dev;
 
+    /* TODO FIXME: This should really only copy the used portion of the
+     * offscreen pixmap. */
+
     /* Do not use the external surface if the user says not to */
     if ( !aStream->rasterize_image )
         return;
@@ -388,6 +406,33 @@ void set_line_properties( PLCairo *aStream, cairo_line_join_t join, cairo_line_c
     cairo_set_line_cap( aStream->cairoContext, cap );
 }
 
+/* Call this after rendering paths have been defined and before they have
+ * been cleared.  It will update the dirty rectangle information for the
+ * current stream for the new flush/end of page. */
+
+void update_dirty_rectangle( PLCairo *aStream )
+{
+    double x1, y1, x2, y2;
+    cairo_stroke_extents( aStream->cairoContext, &x1, &y1, &x2, &y2 );
+
+    /* Expand the rectangle slightly to account for thick lines and other
+     * effects which may extend beyond the defined Cairo path. */
+    aStream->dirty_x1 = MAX( 0.0, MIN( x1 * 0.9, aStream->dirty_x1 ) );
+    aStream->dirty_y1 = MAX( 0.0, MIN( y1 * 0.9, aStream->dirty_y1 ) );
+    aStream->dirty_x2 = MAX( x2 * 1.1, aStream->dirty_x2 );
+    aStream->dirty_y2 = MAX( y2 * 1.1, aStream->dirty_y2 );
+}
+
+/* Reset the dirty triangle extents */
+
+void reset_dirty_rectangle( PLCairo *aStream )
+{
+    aStream->dirty_x1 = DIRTY_MIN;
+    aStream->dirty_y1 = DIRTY_MIN;
+    aStream->dirty_x2 = DIRTY_MAX;
+    aStream->dirty_y2 = DIRTY_MAX;
+}
+
 void plD_line_cairo( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
 {
     PLCairo           *aStream;
@@ -403,6 +448,9 @@ void plD_line_cairo( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
 
     cairo_move_to( aStream->cairoContext, aStream->downscale * (double) x1a, aStream->downscale * (double) y1a );
     cairo_line_to( aStream->cairoContext, aStream->downscale * (double) x2a, aStream->downscale * (double) y2a );
+
+    update_dirty_rectangle( aStream );
+
     cairo_stroke( aStream->cairoContext );
 
     set_line_properties( aStream, old_join, old_cap );
@@ -426,6 +474,7 @@ void plD_polyline_cairo( PLStream *pls, short *xa, short *ya, PLINT npts )
     set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_BUTT );
 
     poly_line( pls, xa, ya, npts );
+
     cairo_stroke( aStream->cairoContext );
 
     set_line_properties( aStream, old_join, old_cap );
@@ -548,6 +597,13 @@ void text_begin_cairo( PLStream *pls, EscText *args )
         aStream->pangoMarkupString[i] = 0;
     }
     open_span_tag( aStream->pangoMarkupString, args->n_fci, aStream->fontSize, 0 );
+
+    /* TODO FIXME: Set the entire plot surface as dirty until the logic
+     * is added to properly calculate the actual text extents. */
+    aStream->dirty_x1 = 0.0;
+    aStream->dirty_y1 = 0.0;
+    aStream->dirty_x2 = pls->xlength;
+    aStream->dirty_y2 = pls->ylength;
 }
 
 /*---------------------------------------------------------------------
@@ -1140,6 +1196,8 @@ PLCairo *stream_and_font_setup( PLStream *pls, int interactive )
     aStream->cairoContext = NULL;
     aStream->downscale    = downscale;
 
+    reset_dirty_rectangle( aStream );
+
     /* Set text clipping on by default since it makes little difference in
      * speed for a modern cairo stack.*/
     aStream->text_clipping = 1;
@@ -1217,6 +1275,8 @@ void poly_line( PLStream *pls, short *xa, short *ya, PLINT npts )
     {
         cairo_line_to( aStream->cairoContext, aStream->downscale * (double) xa[i], aStream->downscale * (double) ya[i] );
     }
+
+    update_dirty_rectangle( aStream );
 }
 
 /*---------------------------------------------------------------------
@@ -1242,9 +1302,8 @@ void filled_polygon( PLStream *pls, short *xa, short *ya, PLINT npts )
     set_line_properties( aStream, CAIRO_LINE_JOIN_BEVEL, CAIRO_LINE_CAP_BUTT );
 
     /* Draw the polygons */
-    cairo_move_to( aStream->cairoContext, aStream->downscale * (double) xa[0], aStream->downscale * (double) ya[0] );
-    for ( i = 1; i < npts; i++ )
-        cairo_line_to( aStream->cairoContext, aStream->downscale * (double) xa[i], aStream->downscale * (double) ya[i] );
+    poly_line( pls, xa, ya, npts );
+
     cairo_set_source_rgba( aStream->cairoContext,
         (double) pls->curcolor.r / 255.0,
         (double) pls->curcolor.g / 255.0,
@@ -1301,10 +1360,8 @@ void gradient( PLStream *pls, short *xa, short *ya, PLINT npts )
     }
 
     /* Draw the polygon using the gradient. */
+    poly_line( pls, xa, ya, npts );
 
-    cairo_move_to( aStream->cairoContext, aStream->downscale * (double) xa[0], aStream->downscale * (double) ya[0] );
-    for ( i = 1; i < npts; i++ )
-        cairo_line_to( aStream->cairoContext, aStream->downscale * (double) xa[i], aStream->downscale * (double) ya[i] );
     cairo_set_source( aStream->cairoContext, linear_gradient );
     cairo_fill( aStream->cairoContext );
     cairo_pattern_destroy( linear_gradient );
@@ -1393,6 +1450,8 @@ void arc( PLStream *pls, arc_struct *arc_info )
         cairo_line_to( aStream->cairoContext, 0.0, 0.0 );
     cairo_restore( aStream->cairoContext );
 
+    update_dirty_rectangle( aStream );
+
     cairo_set_source_rgba( aStream->cairoContext,
         (double) pls->curcolor.r / 255.0,
         (double) pls->curcolor.g / 255.0,
@@ -1424,7 +1483,11 @@ void rotate_cairo_surface( PLStream *pls, float x11, float x12, float x21, float
 
     matrix = (cairo_matrix_t *) malloc( sizeof ( cairo_matrix_t ) );
     cairo_matrix_init( matrix, x11, x12, x21, x22, x0, y0 );
+#if defined ( PLD_xcairo )
+    cairo_transform( aStream->cairoContext_X, matrix );
+#else
     cairo_transform( aStream->cairoContext, matrix );
+#endif
     free( matrix );
 }
 
@@ -1537,7 +1600,14 @@ static signed int xcairo_init_cairo( PLStream *pls )
     aStream->cairoSurface_X = cairo_xlib_surface_create( aStream->XDisplay, aStream->XWindow, defaultVisual, pls->xlength, pls->ylength );
     aStream->cairoContext_X = cairo_create( aStream->cairoSurface_X );
     /* This is the Cairo surface PLplot will actually plot to. */
-    aStream->cairoSurface = cairo_image_surface_create( CAIRO_FORMAT_RGB24, pls->xlength, pls->ylength );
+    aStream->cairoSurface =
+    /*
+        cairo_surface_create_similar( aStream->cairoSurface_X,
+                                      CAIRO_CONTENT_COLOR,
+                                      pls->xlength, pls->ylength );
+    */
+        cairo_image_surface_create( CAIRO_FORMAT_RGB24,
+                                    pls->xlength, pls->ylength );
     aStream->cairoContext = cairo_create( aStream->cairoSurface );
 
     /* Invert the surface so that the graphs are drawn right side up. */
@@ -1602,10 +1672,22 @@ void plD_init_xcairo( PLStream *pls )
  * Blit the offscreen image to the X window.
  * ---------------------------------------------------------------------*/
 
-void blit_to_x( PLCairo *aStream )
+void blit_to_x( PLCairo *aStream, double x, double y, double w, double h )
 {
-    cairo_set_source_surface( aStream->cairoContext_X, aStream->cairoSurface, 0.0, 0.0 );
-    cairo_paint( aStream->cairoContext_X );
+    /* Copy a portion of the surface */
+    cairo_save( aStream->cairoContext_X );
+    cairo_rectangle( aStream->cairoContext_X, x, y, w, h );
+    cairo_set_operator( aStream->cairoContext_X, CAIRO_OPERATOR_SOURCE );
+    cairo_set_source_surface( aStream->cairoContext_X, aStream->cairoSurface,
+                              0.0, 0.0 );
+    cairo_fill( aStream->cairoContext_X );
+    cairo_restore( aStream->cairoContext_X );
+
+    /* Reset the drawn-on region */
+    reset_dirty_rectangle( aStream );
+
+    /* Flush the X display */
+    XFlush( aStream->XDisplay );
 }
 
 /*----------------------------------------------------------------------
@@ -1642,6 +1724,7 @@ void plD_eop_xcairo( PLStream *pls )
     KeySym         keysym;
     XComposeStatus cs;
     XEvent         event;
+    XExposeEvent   *expose;
     PLCairo        *aStream;
     char           helpmsg[] = " - Press Enter or right-click to continue";
     char           *plotTitle;
@@ -1652,9 +1735,7 @@ void plD_eop_xcairo( PLStream *pls )
         return;
 
     /* Blit the offscreen image to the X window. */
-    blit_to_x( aStream );
-
-    XFlush( aStream->XDisplay );
+    blit_to_x( aStream, 0.0, 0.0, pls->xlength, pls->ylength );
 
     /* Only pause if nopause is unset. */
     if ( pls->nopause )
@@ -1683,10 +1764,11 @@ void plD_eop_xcairo( PLStream *pls )
         case Expose:
             /* Blit the image again after an expose event, but only for the last
              * available event.  Otherwise multiple redraws occur needlessly. */
-            if ( ( (XExposeEvent *) &event )->count == 0 )
+            expose = (XExposeEvent *) &event;
+            if ( expose->count == 0 )
             {
-                blit_to_x( aStream );
-                XFlush( aStream->XDisplay );
+                blit_to_x( aStream, expose->x, expose->y,
+                           expose->width, expose->height );
             }
             break;
         }
@@ -1738,8 +1820,9 @@ void plD_esc_xcairo( PLStream *pls, PLINT op, void *ptr )
     switch ( op )
     {
     case PLESC_FLUSH:    /* forced update of the window */
-        blit_to_x( aStream );
-        XFlush( aStream->XDisplay );
+        blit_to_x( aStream, aStream->dirty_x1, aStream->dirty_y1,
+                   aStream->dirty_x2 - aStream->dirty_x1,
+                   aStream->dirty_y2 - aStream->dirty_y1 );
         break;
     case PLESC_GETC:     /* get cursor position */
         XFlush( aStream->XDisplay );
------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Plplot-devel mailing list
Plplot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plplot-devel

Reply via email to