diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-09-27 16:04:34 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2022-01-12 18:13:52 +0100 |
commit | 996061a1c0d5cd8413ffb6a97cb25b8da9ed811b (patch) | |
tree | ec75b4b2c3dbd7de6efaa5dd6a376e0c99a990e9 /src/plugins/platforms/cocoa | |
parent | 588f956f785203a60c1595636972beabcb2e894f (diff) |
macOS: Implement QCALayerBackingStore::scroll for improved performance
The CALayer backingstore never had a scroll implementation because we
were relying on the QRasterBackingStore implementation, but as it turned
out that implementation was not applicable for the CALayer backingstore.
We now implement scroll() by determining which part of the back buffer
can be scrolled directly in-place, and then scrolling the rest by
copying from the front buffer. We have to handle both cases, as clients
may scroll multiple times before flushing, and the scrolled area may
overlap both valid back-buffer content and content that needs to be
pulled from the front-buffer.
Pick-to: 6.3 6.2
Change-Id: Icc09c9488386925116779c9024669a4329b38247
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 55 |
2 files changed, 57 insertions, 0 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index eca595b23f..a91407da5d 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -71,6 +71,8 @@ public: QPaintDevice *paintDevice() override; void endPaint() override; + bool scroll(const QRegion ®ion, int dx, int dy) override; + void flush(QWindow *, const QRegion &, const QPoint &) override; #ifndef QT_NO_OPENGL void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index d5db3adccd..a09176cfaa 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -250,6 +250,61 @@ void QCALayerBackingStore::endPaint() // we defer finalizing the back buffer until its content is needed. } +bool QCALayerBackingStore::scroll(const QRegion ®ion, int dx, int dy) +{ + if (!m_buffers.back()) { + qCInfo(lcQpaBackingStore) << "Scroll requested with no back buffer. Ignoring."; + return false; + } + + const QPoint scrollDelta(dx, dy); + qCInfo(lcQpaBackingStore) << "Scrolling" << region << "by" << scrollDelta; + + ensureBackBuffer(); + recreateBackBufferIfNeeded(); + + const QRegion inPlaceRegion = region - m_buffers.back()->dirtyRegion; + const QRegion frontBufferRegion = region - inPlaceRegion; + + QMacAutoReleasePool pool; + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); + + if (!inPlaceRegion.isEmpty()) { + qCDebug(lcQpaBackingStore) << "Scrolling" << inPlaceRegion << "in place"; + QImage *backBufferImage = m_buffers.back()->asImage(); + const qreal devicePixelRatio = backBufferImage->devicePixelRatio(); + const QPoint devicePixelDelta = scrollDelta * devicePixelRatio; + + extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &); + + for (const QRect &rect : inPlaceRegion) { + qt_scrollRectInImage(*backBufferImage, + QRect(rect.topLeft() * devicePixelRatio, + rect.size() * devicePixelRatio), + devicePixelDelta); + } + + } + + if (!frontBufferRegion.isEmpty()) { + qCDebug(lcQpaBackingStore) << "Scrolling" << frontBufferRegion << "by copying from front buffer"; + preserveFromFrontBuffer(frontBufferRegion, scrollDelta); + } + + m_buffers.back()->unlock(); + + // Mark the target region as filled. Note: We do not mark the source region + // as dirty, even though the content has conceptually been "moved", as that + // would complicate things when preserving from the front buffer. This matches + // the behavior of other backingstore implementations using qt_scrollRectInImage. + updateDirtyStates(region.translated(scrollDelta)); + + qCInfo(lcQpaBackingStore) << "Scroll ended. Back buffer valid region is now" << m_buffers.back()->validRegion(); + + return true; +} + void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, const QPoint &offset) { Q_UNUSED(region); |