xcb: Optimize non-shm backing store flushing

Unfortunately the functions in xcb-image are quite slow, both for the
subimage generation and the byte order swapping. xcb_image_subimage is
implemented as a pixel by pixel copy, and the xcb byte swapping is done
manually without utilizing potential CPU instructions to accelerate the
swap.

Replace both with their Qt equivalents.

Change-Id: I1fe1fe5d9576fdf2bab4a8c401d2a6bb842c2727
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
This commit is contained in:
Louai Al-Khanji 2016-04-01 10:58:29 -07:00
parent edaa5fc208
commit b9d386f2cc

View File

@ -58,6 +58,7 @@
#include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/private/qhighdpiscaling_p.h>
#include <qpa/qplatformgraphicsbuffer.h> #include <qpa/qplatformgraphicsbuffer.h>
#include <private/qimage_p.h> #include <private/qimage_p.h>
#include <qendian.h>
#include <algorithm> #include <algorithm>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -107,6 +108,7 @@ private:
// do a server-side copy on expose instead of sending the pixels every time // do a server-side copy on expose instead of sending the pixels every time
xcb_pixmap_t m_xcb_pixmap; xcb_pixmap_t m_xcb_pixmap;
QRegion m_pendingFlush; QRegion m_pendingFlush;
QByteArray m_flushBuffer;
bool m_hasAlpha; bool m_hasAlpha;
}; };
@ -287,11 +289,84 @@ void QXcbShmImage::ensureGC(xcb_drawable_t dst)
} }
} }
static inline void copy_unswapped(char *dst, int dstBytesPerLine, const QImage &img, const QRect &rect)
{
const uchar *srcData = img.constBits();
const int srcBytesPerLine = img.bytesPerLine();
const int leftOffset = rect.left() * img.depth() >> 3;
const int bottom = rect.bottom() + 1;
for (int yy = rect.top(); yy < bottom; ++yy) {
const uchar *src = srcData + yy * srcBytesPerLine + leftOffset;
::memmove(dst, src, dstBytesPerLine);
dst += dstBytesPerLine;
}
}
template <class Pixel>
static inline void copy_swapped(Pixel *dst, const QImage &img, const QRect &rect)
{
const uchar *srcData = img.constBits();
const int srcBytesPerLine = img.bytesPerLine();
const int left = rect.left();
const int right = rect.right() + 1;
const int bottom = rect.bottom() + 1;
for (int yy = rect.top(); yy < bottom; ++yy) {
const Pixel *src = reinterpret_cast<const Pixel *>(srcData + yy * srcBytesPerLine) + left;
for (int xx = left; xx < right; ++xx)
*dst++ = qbswap<Pixel>(*src++);
}
}
static QImage native_sub_image(QByteArray *buffer, const QImage &src, int x, int y, int w, int h, bool swap)
{
const QRect rect(x, y, w, h);
if (!swap && src.rect() == rect)
return src;
const int dstStride = w * src.depth() >> 3;
buffer->resize(h * dstStride);
if (swap) {
switch (src.depth()) {
case 32:
copy_swapped(reinterpret_cast<quint32 *>(buffer->data()), src, rect);
break;
case 16:
copy_swapped(reinterpret_cast<quint16 *>(buffer->data()), src, rect);
break;
}
} else {
copy_unswapped(buffer->data(), dstStride, src, rect);
}
return QImage(reinterpret_cast<const uchar *>(buffer->constData()), w, h, dstStride, src.format());
}
void QXcbShmImage::flushPixmap(const QRegion &region) void QXcbShmImage::flushPixmap(const QRegion &region)
{ {
const QVector<QRect> rects = m_pendingFlush.intersected(region).rects(); const QVector<QRect> rects = m_pendingFlush.intersected(region).rects();
m_pendingFlush -= region; m_pendingFlush -= region;
xcb_image_t xcb_subimage;
memset(&xcb_subimage, 0, sizeof(xcb_image_t));
xcb_subimage.format = m_xcb_image->format;
xcb_subimage.scanline_pad = m_xcb_image->scanline_pad;
xcb_subimage.depth = m_xcb_image->depth;
xcb_subimage.bpp = m_xcb_image->bpp;
xcb_subimage.unit = m_xcb_image->unit;
xcb_subimage.plane_mask = m_xcb_image->plane_mask;
xcb_subimage.byte_order = (xcb_image_order_t) connection()->setup()->image_byte_order;
xcb_subimage.bit_order = m_xcb_image->bit_order;
const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order;
for (const QRect &rect : rects) { for (const QRect &rect : rects) {
// We must make sure that each request is not larger than max_req_size. // We must make sure that each request is not larger than max_req_size.
// Each request takes req_size + m_xcb_image->stride * height bytes. // Each request takes req_size + m_xcb_image->stride * height bytes.
@ -308,37 +383,29 @@ void QXcbShmImage::flushPixmap(const QRegion &region)
// larger than the server's maximum request size and stuff breaks. // larger than the server's maximum request size and stuff breaks.
// To work around that, we upload the image in chunks where each chunk // To work around that, we upload the image in chunks where each chunk
// is small enough for a single request. // is small enough for a single request.
int src_x = rect.x(); const int x = rect.x();
int src_y = rect.y(); int y = rect.y();
int target_x = rect.x(); const int width = rect.width();
int target_y = rect.y();
int width = rect.width();
int height = rect.height(); int height = rect.height();
while (height > 0) { while (height > 0) {
int rows = std::min(height, rows_per_put); const int rows = std::min(height, rows_per_put);
const QImage subImage = native_sub_image(&m_flushBuffer, m_qimage, x, y, width, rows, needsByteSwap);
xcb_image_t *subimage = xcb_image_subimage(m_xcb_image, src_x, src_y, width, rows, xcb_subimage.width = width;
0, 0, 0); xcb_subimage.height = rows;
xcb_subimage.data = const_cast<uint8_t *>(subImage.constBits());
// Convert the image to the native byte order. xcb_image_annotate(&xcb_subimage);
xcb_image_t *native_subimage = xcb_image_native(xcb_connection(), subimage, 1);
xcb_image_put(xcb_connection(), xcb_image_put(xcb_connection(),
m_xcb_pixmap, m_xcb_pixmap,
m_gc, m_gc,
native_subimage, &xcb_subimage,
target_x, x,
target_y, y,
0); 0);
if (native_subimage != subimage) y += rows;
xcb_image_destroy(native_subimage);
xcb_image_destroy(subimage);
src_y += rows;
target_y += rows;
height -= rows; height -= rows;
} }
} }