qt-bugs@ issue : 11790 (part of) applied: no author: Lubos Lunak 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. --- work/qt-x11-free-3.3.8/src/kernel/qpixmap_x11.cpp +++ work/qt-x11-free-3.3.8/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 ); + } TQ_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 ); - TQ_CHECK_PTR( xi ); if (!xi) return false; + if( newbits == NULL ) newbits = (uchar *)malloc( xi->bytes_per_line*h ); TQ_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; TQ_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; TQ_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); TQ_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 --- work/qt-x11-free-3.3.8/mkspecs/linux-g++/qplatformdefs.h +++ work/qt-x11-free-3.3.8/mkspecs/linux-g++/qplatformdefs.h @@ -102,5 +102,6 @@ #define QT_VSNPRINTF ::vsnprintf #endif +#define QT_MITSHM #endif // QPLATFORMDEFS_H