LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File 0005-qpixmap_mitshm.patch of Package qt3 (Project openSUSE:Factory)

qt-bugs@ issue : 11790 (part of)
applied: no
author: Lubos Lunak <l.lunak@kde.org>

NOTE: Needs #define QT_MITSHM in the matching qplatformdefs.h file. This
    patch does so only for linux-g++ and linux-g++-distcc platforms.

MITSHM extension support for QPixmap<->QImage conversions.

Hello,

 the review and apply the attached patches that improve performance of 
QImage->QPixmap conversions. They should be applied in order 
'mitshm','more_local' and 'fast', but they're independent from each other 
(well, besides merging problems).

 Mitshm patch adds MITSHM extension support for both 
QPixmap::convertFromImage() and QPixmap::convertToImage(). I've noticed there 
was some MITSHM support already, turned off by default, but it was used only 
for QPixmap::xForm() , and it used shared pixmaps (and I'd bet nobody uses 
it). My patch adds shared ximages support for faster pixmap<->image 
conversions. Since I don't understand the xForm() code much, and I didn't 
want to do anything with it, I added three #define's:
 - QT_MITSHM generally enabling MITSHM support, which should be set in 
qplatformsdefs.h (or wherever you setup platform specific stuff), it can be 
enabled at least on Linux
 - QT_MITSHM_CONVERSIONS - this is for my new code
 - QT_MITSHM_XFORM - this is for the xForm() code
 There's one more #define, QT_MITSHM_RMID_IGNORES_REFCOUNT. Glibc 
documentation of shmctl( ... IPC_RMID ) quite clearly says that the memory 
segment is freed only after the refcount increased by shmat() and decreased 
by shmdt() is 0. However, at least according to 
http://bugs.kde.org/show_bug.cgi?id=27517 , this doesn't happen on other 
platforms for some strange reason. Such platforms should have this #define if  
you ever consider supporting MITSHM on them.

 The lower limit for using MITSHM for the image is about 8KiB 
(width*height*depth > 100*100*32 ). Also, BestOptim in such case doesn't keep 
the ximage, as the shared ximage is always freed before the function returns 
(I don't know if it's worth copying it).

 The second patch ('more_local'), in short, does nothing. Besides improving 
performance by about 10% by making variables more "local", making few of them 
const, and also making some of them unsigned (this help gcc for some reason).

 The last one, 'fast', moves some if's out of the loops, and handles some most 
common case specially (15bpp, 16bpp and 32bpp ximage depths). 32bpp case, if 
the endianess matches, is simply uses memcpy(), for the 15/16bpp depth, 
variables are replaced directly by matching values, statements are a bit 
reordered and merged when suitable, and again, in case endianess matches, 
pixels are written simply as Q_INT16. Most probably it would also help to 
process two pixels at once and write them as Q_INT32, but I didn't want to 
complicate the code too much  (later >;)  ).

 The last snippet of 'fast' handles case when xi->bytes_per_line is not equal 
to width for 8bpp ximage. I'm not actually sure if that can ever happen, but 
since I've already written it *shrug*.

 The 'more_local' and 'fast' patches change only convertFromImage(), as I 
don't think convertToImage() is that performance critical (but it's as 
unoptimized as convertFromImage() was).

 Maybe some numbers. The difference is of course mainly visible with larger 
pixmaps. The two optimizations alone reduce the time to 50% for 32bpp, to 70% 
for 16bpp. The MITSHM support, when other patches are already applied too, 
for 32bpp images saves about 33%. Together, the total time is reduced to 
about 40% for 32bpp. Imlib probably still beats that, but at least this 
obsoletes KPixmapIO.


--- src/kernel/qpixmap_x11.cpp
+++ src/kernel/qpixmap_x11.cpp
@@ -37,7 +37,19 @@
 
 // NOT REVISED
 
+#include "qplatformdefs.h"
+
+#if defined(Q_OS_WIN32) && defined(QT_MITSHM)
+#undef QT_MITSHM
+#endif
+
+#ifdef QT_MITSHM
+
+// Use the MIT Shared Memory extension for pixmap<->image conversions
+#define QT_MITSHM_CONVERSIONS
+
 // Uncomment the next line to enable the MIT Shared Memory extension
+// for QPixmap::xForm()
 //
 // WARNING:  This has some problems:
 //
@@ -45,14 +57,13 @@
 //    2. Qt does not handle the ShmCompletion message, so you will
 //        get strange effects if you xForm() repeatedly.
 //
-// #define QT_MITSHM
+// #define QT_MITSHM_XFORM
 
-#if defined(Q_OS_WIN32) && defined(QT_MITSHM)
-#undef QT_MITSHM
+#else
+#undef QT_MITSHM_CONVERSIONS
+#undef QT_MITSHM_XFORM
 #endif
 
-#include "qplatformdefs.h"
-
 #include "qbitmap.h"
 #include "qpaintdevicemetrics.h"
 #include "qimage.h"
@@ -91,7 +102,7 @@ inline static void qSafeXDestroyImage( X
   MIT Shared Memory Extension support: makes xForm noticeably (~20%) faster.
  *****************************************************************************/
 
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
 
 static bool	       xshminit = FALSE;
 static XShmSegmentInfo xshminfo;
@@ -173,8 +184,100 @@ static bool qt_create_mitshm_buffer( con
 //     return FALSE;
 // }
 
-#endif // QT_MITSHM
+#endif // QT_MITSHM_XFORM
+
+#ifdef QT_MITSHM_CONVERSIONS
+
+static bool qt_mitshm_error = false;
+static int qt_mitshm_errorhandler( Display*, XErrorEvent* )
+{
+    qt_mitshm_error = true;
+    return 0;
+}
+
+static XImage* qt_XShmCreateImage( Display* dpy, Visual* visual, unsigned int depth,
+    int format, int /*offset*/, char* /*data*/, unsigned int width, unsigned int height,
+    int /*bitmap_pad*/, int /*bytes_per_line*/, XShmSegmentInfo* shminfo )
+{
+    if( width * height * depth < 100*100*32 )
+        return NULL;
+    static int shm_inited = -1;
+    if( shm_inited == -1 ) {
+        if( XShmQueryExtension( dpy ))
+            shm_inited = 1;
+        else
+            shm_inited = 0;
+    }
+    if( shm_inited == 0 )
+        return NULL;
+    XImage* xi = XShmCreateImage( dpy, visual, depth, format, NULL, shminfo, width,
+        height );
+    if( xi == NULL )
+        return NULL;
+    shminfo->shmid = shmget( IPC_PRIVATE, xi->bytes_per_line * xi->height,
+        IPC_CREAT|0600);
+    if( shminfo->shmid < 0 ) {
+        XDestroyImage( xi );
+        return NULL;
+    }
+    shminfo->readOnly = False;
+    shminfo->shmaddr = (char*)shmat( shminfo->shmid, 0, 0 );
+    if( shminfo->shmaddr == (char*)-1 ) {
+        XDestroyImage( xi );
+        shmctl( shminfo->shmid, IPC_RMID, 0 );
+        return NULL;
+    }
+    xi->data = shminfo->shmaddr;
+#ifndef QT_MITSHM_RMID_IGNORES_REFCOUNT
+    // mark as deleted to automatically free the memory in case
+    // of a crash (but this doesn't work e.g. on Solaris)
+    shmctl( shminfo->shmid, IPC_RMID, 0 );
+#endif
+    if( shm_inited == 1 ) { // first time
+        XErrorHandler old_h = XSetErrorHandler( qt_mitshm_errorhandler );
+        XShmAttach( dpy, shminfo );
+        shm_inited = 2;
+        XSync( dpy, False );
+        XSetErrorHandler( old_h );
+        if( qt_mitshm_error ) { // oops ... perhaps we are remote?
+            shm_inited = 0;
+            XDestroyImage( xi );
+            shmdt( shminfo->shmaddr );
+#ifdef QT_MITSHM_RMID_IGNORES_REFCOUNT
+            shmctl( shminfo->shmid, IPC_RMID, 0 );
+#endif    
+            return NULL;
+        }
+    } else
+        XShmAttach( dpy, shminfo );
+    return xi;
+}
+
+static void qt_XShmDestroyImage( XImage* xi, XShmSegmentInfo* shminfo )
+{
+    XShmDetach( QPaintDevice::x11AppDisplay(), shminfo );
+    XDestroyImage( xi );
+    shmdt( shminfo->shmaddr );
+#ifdef QT_MITSHM_RMID_IGNORES_REFCOUNT
+    shmctl( shminfo->shmid, IPC_RMID, 0 );
+#endif    
+}
+
+static XImage* qt_XShmGetImage( const QPixmap* pix, int format,
+    XShmSegmentInfo* shminfo )
+{
+    XImage* xi = qt_XShmCreateImage( pix->x11Display(), (Visual*)pix->x11Visual(),
+        pix->depth(), format, 0, 0, pix->width(), pix->height(), 32, 0, shminfo );
+    if( xi == NULL )
+        return NULL;
+    if( XShmGetImage( pix->x11Display(), pix->handle(), xi, 0, 0, AllPlanes ) == False ) {
+        qt_XShmDestroyImage( xi, shminfo );
+        return NULL;
+    }
+    return xi;
+}
 
+#endif // QT_MITSHM_CONVERSIONS
 
 /*****************************************************************************
   Internal functions
@@ -627,9 +730,20 @@ QImage QPixmap::convertToImage() const
 	d = 32;					//   > 8  ==> 32
 
     XImage *xi = (XImage *)data->ximage;	// any cached ximage?
-    if ( !xi )					// fetch data from X server
+#ifdef QT_MITSHM_CONVERSIONS
+    bool mitshm_ximage = false;
+    XShmSegmentInfo shminfo;
+#endif
+    if ( !xi ) {				// fetch data from X server
+#ifdef QT_MITSHM_CONVERSIONS
+        xi = qt_XShmGetImage( this, mono ? XYPixmap : ZPixmap, &shminfo );
+        if( xi ) {
+            mitshm_ximage = true;
+        } else
+#endif
 	xi = XGetImage( x11Display(), hd, 0, 0, w, h, AllPlanes,
 			mono ? XYPixmap : ZPixmap );
+    }
     Q_CHECK_PTR( xi );
     if (!xi)
         return image; // null image
@@ -640,15 +754,31 @@ QImage QPixmap::convertToImage() const
 		   QImage::LittleEndian : QImage::BigEndian;
     }
     image.create( w, h, d, 0, bitOrder );
-    if ( image.isNull() )			// could not create image
+    if ( image.isNull() ) {			// could not create image
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage )
+            qt_XShmDestroyImage( xi, &shminfo );
+        else
+#endif
+        qSafeXDestroyImage( xi );
 	return image;
+    }
 
     const QPixmap* msk = mask();
     const QPixmap *alf = data->alphapm;
 
     QImage alpha;
     if (alf) {
-	XImage *axi = XGetImage(x11Display(), alf->hd, 0, 0, w, h, AllPlanes, ZPixmap);
+        XImage* axi;
+#ifdef QT_MITSHM_CONVERSIONS
+        bool mitshm_aximage = false;
+        XShmSegmentInfo ashminfo;
+        axi = qt_XShmGetImage( alf, ZPixmap, &ashminfo );
+        if( axi ) {
+            mitshm_aximage = true;
+        } else
+#endif
+            axi = XGetImage(x11Display(), alf->hd, 0, 0, w, h, AllPlanes, ZPixmap);
 
 	if (axi) {
 	    image.setAlphaBuffer( TRUE );
@@ -662,6 +792,11 @@ QImage QPixmap::convertToImage() const
 		src += axi->bytes_per_line;
 	    }
 
+#ifdef QT_MITSHM_CONVERSIONS
+            if( mitshm_aximage )
+                qt_XShmDestroyImage( axi, &ashminfo );
+            else
+#endif
 	    qSafeXDestroyImage( axi );
 	}
     } else if (msk) {
@@ -804,6 +939,12 @@ QImage QPixmap::convertToImage() const
 		  xi->bits_per_pixel );
 #endif
 	image.reset();
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage )
+            qt_XShmDestroyImage( xi, &shminfo );
+        else
+#endif
+            qSafeXDestroyImage( xi );
 	return image;
     }
 
@@ -909,10 +1050,22 @@ QImage QPixmap::convertToImage() const
 	delete [] carr;
     }
     if ( data->optim != BestOptim ) {		// throw away image data
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage )
+            qt_XShmDestroyImage( xi, &shminfo );
+        else
+#endif
 	qSafeXDestroyImage( xi );
 	((QPixmap*)this)->data->ximage = 0;
-    } else					// keep ximage data
+    } else {					// keep ximage data
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage ) { // copy the XImage?
+            qt_XShmDestroyImage( xi, &shminfo );
+            xi = 0;
+        }
+#endif
 	((QPixmap*)this)->data->ximage = xi;
+    }
 
     return image;
 }
@@ -1085,6 +1238,11 @@ bool QPixmap::convertFromImage( const QI
     bool    trucol = (visual->c_class == TrueColor || visual->c_class == DirectColor);
     int	    nbytes = image.numBytes();
     uchar  *newbits= 0;
+    int newbits_size = 0;
+#ifdef QT_MITSHM_CONVERSIONS
+    bool mitshm_ximage = false;
+    XShmSegmentInfo shminfo;
+#endif
 
     if ( trucol ) {				// truecolor display
 	QRgb  pix[256];				// pixel translation table
@@ -1113,10 +1271,18 @@ bool QPixmap::convertFromImage( const QI
 	    }
 	}
 
+#ifdef QT_MITSHM_CONVERSIONS
+	xi = qt_XShmCreateImage( dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0, &shminfo );
+	if( xi != NULL ) {
+	    mitshm_ximage = true;
+	    newbits = (uchar*)xi->data;
+	}
+	else
+#endif
 	xi = XCreateImage( dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0 );
-	Q_CHECK_PTR( xi );
         if (!xi)
             return false;
+	if( newbits == NULL )
 	newbits = (uchar *)malloc( xi->bytes_per_line*h );
 	Q_CHECK_PTR( newbits );
 	if ( !newbits )				// no memory
@@ -1323,6 +1489,7 @@ bool QPixmap::convertFromImage( const QI
 	}
 
 	newbits = (uchar *)malloc( nbytes );	// copy image into newbits
+        newbits_size = nbytes;
 	Q_CHECK_PTR( newbits );
 	if ( !newbits )				// no memory
 	    return FALSE;
@@ -1440,11 +1607,18 @@ bool QPixmap::convertFromImage( const QI
     }
 
     if ( !xi ) {				// X image not created
+#ifdef QT_MITSHM_CONVERSIONS
+        xi = qt_XShmCreateImage( dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0, &shminfo );
+        if( xi != NULL )
+            mitshm_ximage = true;
+        else
+#endif
 	xi = XCreateImage( dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0 );
 	if ( xi->bits_per_pixel == 16 ) {	// convert 8 bpp ==> 16 bpp
 	    ushort *p2;
 	    int	    p2inc = xi->bytes_per_line/sizeof(ushort);
 	    ushort *newerbits = (ushort *)malloc( xi->bytes_per_line * h );
+            newbits_size = xi->bytes_per_line * h;
 	    Q_CHECK_PTR( newerbits );
 	    if ( !newerbits )				// no memory
 		return FALSE;
@@ -1462,6 +1636,14 @@ bool QPixmap::convertFromImage( const QI
 		      "(bpp=%d)", xi->bits_per_pixel );
 #endif
 	}
+#ifdef QT_MITSHM_CONVERSIONS
+        if( newbits_size > 0 && mitshm_ximage ) { // need to copy to shared memory
+            memcpy( xi->data, newbits, newbits_size );
+            free( newbits );
+            newbits = (uchar*)xi->data;
+        }
+        else
+#endif
 	xi->data = (char *)newbits;
     }
 
@@ -1495,19 +1677,24 @@ bool QPixmap::convertFromImage( const QI
 
     }
 
+#ifdef QT_MITSHM_CONVERSIONS
+    if( mitshm_ximage )
+        XShmPutImage( dpy, hd, qt_xget_readonly_gc( x11Screen(), FALSE ),
+                      xi, 0, 0, 0, 0, w, h, False );
+    else
+#endif
     XPutImage( dpy, hd, qt_xget_readonly_gc( x11Screen(), FALSE  ),
 	       xi, 0, 0, 0, 0, w, h );
 
-    if ( data->optim != BestOptim ) {		// throw away image
-	qSafeXDestroyImage( xi );
-	data->ximage = 0;
-    } else {					// keep ximage that we created
-	data->ximage = xi;
-    }
     data->w = w;
     data->h = h;
     data->d = dd;
 
+    XImage* axi = NULL;
+#ifdef QT_MITSHM_CONVERSIONS
+    bool mitshm_aximage = false;
+    XShmSegmentInfo ashminfo;
+#endif
     if ( image.hasAlphaBuffer() ) {
 	QBitmap m;
 	m = image.createAlphaMask( conversion_flags );
@@ -1543,13 +1730,22 @@ bool QPixmap::convertFromImage( const QI
 	    data->alphapm->rendhd =
 		(HANDLE) XftDrawCreateAlpha( x11Display(), data->alphapm->hd, 8 );
 
-	    XImage *axi = XCreateImage(x11Display(), (Visual *) x11Visual(),
+#ifdef QT_MITSHM_CONVERSIONS
+	    axi = qt_XShmCreateImage( x11Display(), (Visual*)x11Visual(),
+		    8, ZPixmap, 0, 0, w, h, 8, 0, &ashminfo );
+	    if( axi != NULL )
+		mitshm_aximage = true;
+	    else
+#endif
+		axi = XCreateImage(x11Display(), (Visual *) x11Visual(),
 				       8, ZPixmap, 0, 0, w, h, 8, 0);
 
 	    if (axi) {
+		if( axi->data==NULL ) {
 		// the data is deleted by qSafeXDestroyImage
                 axi->data = (char *) malloc(h * axi->bytes_per_line);
 		Q_CHECK_PTR( axi->data );
+		}
                 char *aptr = axi->data;
 
                 if (image.depth() == 32) {
@@ -1567,14 +1763,48 @@ bool QPixmap::convertFromImage( const QI
                 }
 
                 GC gc = XCreateGC(x11Display(), data->alphapm->hd, 0, 0);
+ #ifdef QT_MITSHM_CONVERSIONS
+		if( mitshm_aximage )
+		    XShmPutImage( dpy, data->alphapm->hd, gc, axi, 0, 0, 0, 0, w, h, False );
+		else
+#endif
                 XPutImage(dpy, data->alphapm->hd, gc, axi, 0, 0, 0, 0, w, h);
                 XFreeGC(x11Display(), gc);
-		qSafeXDestroyImage(axi);
 	    }
 	}
 #endif // QT_NO_XFTFREETYPE
     }
 
+#ifdef QT_MITSHM_CONVERSIONS
+    if( mitshm_ximage || mitshm_aximage )
+        XSync( x11Display(), False ); // wait until processed
+#endif
+
+    if ( data->optim != BestOptim ) {       // throw away image
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage )
+            qt_XShmDestroyImage( xi, &shminfo );
+        else
+#endif
+     qSafeXDestroyImage( xi );
+     data->ximage = 0;
+    } else {      // keep ximage that we created
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_ximage ) { // copy the XImage?
+            qt_XShmDestroyImage( xi, &shminfo );
+            xi = 0;
+        }
+#endif
+     data->ximage = xi;
+    }
+    if( axi ) {
+#ifdef QT_MITSHM_CONVERSIONS
+        if( mitshm_aximage )
+            qt_XShmDestroyImage( axi, &ashminfo );
+        else
+#endif
+        qSafeXDestroyImage(axi);
+    }
     return TRUE;
 }
 
@@ -1737,7 +1967,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
 	return pm;
     }
 
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
     static bool try_once = TRUE;
     if (try_once) {
 	try_once = FALSE;
@@ -1770,7 +2000,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
 	dbpl = ((w*bpp+31)/32)*4;
     dbytes = dbpl*h;
 
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
     if ( use_mitshm ) {
 	dptr = (uchar *)xshmimg->data;
 	uchar fillbyte = bpp == 8 ? white.pixel() : 0xff;
@@ -1786,7 +2016,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
 	    memset( dptr, Qt::white.pixel( x11Screen() ), dbytes );
 	else
 	    memset( dptr, 0xff, dbytes );
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
     }
 #endif
 
@@ -1817,7 +2047,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
     } else {
 	xbpl  = (w*bpp)/8;
 	p_inc = dbpl - xbpl;
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
 	if ( use_mitshm )
 	    p_inc = xshmimg->bytes_per_line - xbpl;
 #endif
@@ -1854,7 +2084,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
 	QPixmap pm( w, h );
 	pm.data->uninit = FALSE;
 	pm.x11SetScreen( x11Screen() );
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
 	if ( use_mitshm ) {
 	    XCopyArea( dpy, xshmpm, pm.handle(), gc, 0, 0, w, h, 0, 0 );
 	} else {
@@ -1863,7 +2093,7 @@ QPixmap QPixmap::xForm( const QWMatrix &
 			       ZPixmap, 0, (char *)dptr, w, h, 32, 0 );
 	    XPutImage( dpy, pm.handle(), gc, xi, 0, 0, 0, 0, w, h);
 	    qSafeXDestroyImage( xi );
-#if defined(QT_MITSHM)
+#if defined(QT_MITSHM_XFORM)
 	}
 #endif
 
--- mkspecs/linux-g++/qplatformdefs.h
+++ mkspecs/linux-g++/qplatformdefs.h
@@ -102,5 +102,6 @@
 #define QT_VSNPRINTF		::vsnprintf
 #endif
 
+#define QT_MITSHM
 
 #endif // QPLATFORMDEFS_H