diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbbackingstore.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbbackingstore.cpp | 205 |
1 files changed, 155 insertions, 50 deletions
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 420d1ac7c5..1e046aa677 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -69,6 +69,8 @@ public: QXcbShmImage(QXcbScreen *connection, const QSize &size, uint depth, QImage::Format format); ~QXcbShmImage() { destroy(); } + void flushScrolledRegion(bool clientSideScroll); + bool scroll(const QRegion &area, int dx, int dy); QImage *image() { return &m_qimage; } @@ -86,7 +88,8 @@ private: void destroy(); void ensureGC(xcb_drawable_t dst); - void flushPixmap(const QRegion ®ion); + void shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset = QPoint()); + void flushPixmap(const QRegion ®ion, bool fullRegion = false); void setClip(const QRegion ®ion); xcb_shm_segment_info_t m_shm_info; @@ -99,18 +102,26 @@ private: xcb_gcontext_t m_gc; xcb_drawable_t m_gc_drawable; - // When using shared memory this is the region currently shared with the server - QRegion m_dirtyShm; - + // When using shared memory these variables are used only for server-side scrolling. // When not using shared memory, we maintain a server-side pixmap with the backing // store as well as repainted content not yet flushed to the pixmap. We only flush // the regions we need and only when these are marked dirty. This way we can just // do a server-side copy on expose instead of sending the pixels every time xcb_pixmap_t m_xcb_pixmap; QRegion m_pendingFlush; + + // This is the scrolled region which is stored in server-side pixmap + QRegion m_scrolledRegion; + + // When using shared memory this is the region currently shared with the server + QRegion m_dirtyShm; + + // When not using shared memory this is a temporary buffer which is uploaded + // as a pixmap region to server QByteArray m_flushBuffer; bool m_hasAlpha; + bool m_clientSideScroll; }; class QXcbShmGraphicsBuffer : public QPlatformGraphicsBuffer @@ -145,10 +156,11 @@ private: QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QImage::Format format) : QXcbObject(screen->connection()) - , m_graphics_buffer(Q_NULLPTR) + , m_graphics_buffer(nullptr) , m_gc(0) , m_gc_drawable(0) , m_xcb_pixmap(0) + , m_clientSideScroll(false) { const xcb_format_t *fmt = connection()->formatForDepth(depth); Q_ASSERT(fmt); @@ -202,13 +214,59 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format); m_graphics_buffer = new QXcbShmGraphicsBuffer(&m_qimage); - if (!hasShm()) { - m_xcb_pixmap = xcb_generate_id(xcb_connection()); - xcb_create_pixmap(xcb_connection(), - m_xcb_image->depth, - m_xcb_pixmap, - screen->screen()->root, - m_xcb_image->width, m_xcb_image->height); + m_xcb_pixmap = xcb_generate_id(xcb_connection()); + xcb_create_pixmap(xcb_connection(), + m_xcb_image->depth, + m_xcb_pixmap, + screen->screen()->root, + m_xcb_image->width, m_xcb_image->height); +} + +void QXcbShmImage::flushScrolledRegion(bool clientSideScroll) +{ + if (m_clientSideScroll == clientSideScroll) + return; + + m_clientSideScroll = clientSideScroll; + + if (m_scrolledRegion.isNull()) + return; + + if (hasShm() && m_dirtyShm.intersects(m_scrolledRegion)) { + connection()->sync(); + m_dirtyShm = QRegion(); + } + + if (m_clientSideScroll) { + // Copy scrolled image region from server-side pixmap to client-side memory + for (const QRect &rect : m_scrolledRegion) { + const int w = rect.width(); + const int h = rect.height(); + + auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, + xcb_connection(), + m_xcb_image->format, + m_xcb_pixmap, + rect.x(), rect.y(), + w, h, + ~0u); + + if (reply && reply->depth == m_xcb_image->depth) { + const QImage img(xcb_get_image_data(reply.get()), w, h, m_qimage.format()); + + QPainter p(&m_qimage); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawImage(rect.topLeft(), img); + } + } + m_scrolledRegion = QRegion(); + } else { + // Copy scrolled image region from client-side memory to server-side pixmap + ensureGC(m_xcb_pixmap); + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_scrolledRegion); + else + flushPixmap(m_scrolledRegion, true); } } @@ -216,21 +274,28 @@ extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &o bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) { - if (image()->isNull()) - return false; + const QRect bounds(QPoint(), size()); + const QRegion scrollArea(area & bounds); + const QPoint delta(dx, dy); - if (hasShm()) - preparePaint(area); + if (m_clientSideScroll) { + if (m_qimage.isNull()) + return false; - const QPoint delta(dx, dy); - for (const QRect &rect : area) - qt_scrollRectInImage(*image(), rect, delta); + if (hasShm()) + preparePaint(scrollArea); + + for (const QRect &rect : scrollArea) + qt_scrollRectInImage(m_qimage, rect, delta); + } else { + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea)); + else + flushPixmap(scrollArea); - if (m_xcb_pixmap) { - flushPixmap(area); ensureGC(m_xcb_pixmap); - const QRect bounds(QPoint(0, 0), size()); - for (const QRect &src : area) { + + for (const QRect &src : scrollArea) { const QRect dst = src.translated(delta).intersected(bounds); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -242,6 +307,12 @@ bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) } } + m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds); + if (hasShm()) { + m_pendingFlush -= scrollArea; + m_pendingFlush -= m_scrolledRegion; + } + return true; } @@ -265,12 +336,10 @@ void QXcbShmImage::destroy() if (m_gc) xcb_free_gc(xcb_connection(), m_gc); delete m_graphics_buffer; - m_graphics_buffer = Q_NULLPTR; + m_graphics_buffer = nullptr; - if (m_xcb_pixmap) { - xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); - m_xcb_pixmap = 0; - } + xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); + m_xcb_pixmap = 0; } void QXcbShmImage::ensureGC(xcb_drawable_t dst) @@ -353,10 +422,36 @@ static inline quint32 round_up_scanline(quint32 base, quint32 pad) return (base + pad - 1) & -pad; } -void QXcbShmImage::flushPixmap(const QRegion ®ion) +void QXcbShmImage::shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset) +{ + for (const QRect &rect : region) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_shm_put_image(xcb_connection(), + drawable, + m_gc, + m_xcb_image->width, + m_xcb_image->height, + source.x(), source.y(), + rect.width(), rect.height(), + rect.x(), rect.y(), + m_xcb_image->depth, + m_xcb_image->format, + 0, // send event? + m_shm_info.shmseg, + m_xcb_image->data - m_shm_info.shmaddr); + } + m_dirtyShm |= region.translated(offset); +} + +void QXcbShmImage::flushPixmap(const QRegion ®ion, bool fullRegion) { - const QVector<QRect> rects = m_pendingFlush.intersected(region).rects(); - m_pendingFlush -= region; + QVector<QRect> rects; + if (!fullRegion) { + rects = m_pendingFlush.intersected(region).rects(); + m_pendingFlush -= region; + } else { + rects = region.rects(); + } xcb_image_t xcb_subimage; memset(&xcb_subimage, 0, sizeof(xcb_image_t)); @@ -372,7 +467,7 @@ void QXcbShmImage::flushPixmap(const QRegion ®ion) const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order; - for (const QRect &rect : rects) { + for (const QRect &rect : qAsConst(rects)) { // 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. static const uint32_t req_size = sizeof(xcb_put_image_request_t); @@ -445,29 +540,32 @@ void QXcbShmImage::setClip(const QRegion ®ion) void QXcbShmImage::put(xcb_drawable_t dst, const QRegion ®ion, const QPoint &offset) { + Q_ASSERT(!m_clientSideScroll); + ensureGC(dst); setClip(region); - const QRect bounds = region.boundingRect(); - const QPoint target = bounds.topLeft(); - const QRect source = bounds.translated(offset); - if (hasShm()) { - xcb_shm_put_image(xcb_connection(), + // Copy scrolled area on server-side from pixmap to window + const QRegion scrolledRegion = m_scrolledRegion.translated(-offset); + for (const QRect &rect : scrolledRegion) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_copy_area(xcb_connection(), + m_xcb_pixmap, dst, m_gc, - m_xcb_image->width, - m_xcb_image->height, source.x(), source.y(), - source.width(), source.height(), - target.x(), target.y(), - m_xcb_image->depth, - m_xcb_image->format, - 0, // send event? - m_shm_info.shmseg, - m_xcb_image->data - m_shm_info.shmaddr); - m_dirtyShm |= region.translated(offset); + rect.x(), rect.y(), + rect.width(), rect.height()); + } + + // Copy non-scrolled image from client-side memory to server-side window + const QRegion notScrolledArea = region - scrolledRegion; + shmPutImage(dst, notScrolledArea, offset); } else { + const QRect bounds = region.boundingRect(); + const QPoint target = bounds.topLeft(); + const QRect source = bounds.translated(offset); flushPixmap(region); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -489,9 +587,9 @@ void QXcbShmImage::preparePaint(const QRegion ®ion) connection()->sync(); m_dirtyShm = QRegion(); } - } else { - m_pendingFlush |= region; } + m_scrolledRegion -= region; + m_pendingFlush |= region; } QXcbBackingStore::QXcbBackingStore(QWindow *window) @@ -565,7 +663,7 @@ QImage QXcbBackingStore::toImage() const QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const { - return m_image ? m_image->graphicsBuffer() : Q_NULLPTR; + return m_image ? m_image->graphicsBuffer() : nullptr; } void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) @@ -573,6 +671,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin if (!m_image || m_image->size().isEmpty()) return; + m_image->flushScrolledRegion(false); + QSize imageSize = m_image->size(); QRegion clipped = region; @@ -603,6 +703,11 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c QPlatformTextureList *textures, bool translucentBackground) { + if (!m_image || m_image->size().isEmpty()) + return; + + m_image->flushScrolledRegion(true); + QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); |