summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-02-11 17:13:52 +0100
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-02-13 13:55:01 +0100
commitcd29145ddcf099acf0651aca637fc2ce65ba7546 (patch)
tree82eb5349a54a5702209f9a91810a7c2a85477be9 /src/plugins/platforms/cocoa
parentcce303b6a70bfc5e7f191529724969ca7509039f (diff)
macOS: Implement support for backing store static contents
We already had the machinery to copy one buffer to another, so this machinery has been generalized a bit, so we can use it to preserve the static content during buffer resize. As explained in the comments, we need to do the buffer copies during buffer creation, instead of relying on our back buffer finalization, as we are not guaranteed that we can always copy the static content from the front buffer. Pick-to: 6.7 Change-Id: Ia9b3ac6ec982949990d172b0b14f35b13bbae92a Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h4
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm119
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm1
3 files changed, 93 insertions, 31 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 6db88f923c..71b6015a54 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -54,6 +54,7 @@ private:
bool eventFilter(QObject *watched, QEvent *event) override;
QSize m_requestedSize;
+ QRegion m_staticContents;
class GraphicsBuffer : public QIOSurfaceGraphicsBuffer
{
@@ -78,7 +79,8 @@ private:
bool recreateBackBufferIfNeeded();
void finalizeBackBuffer();
- void preserveFromFrontBuffer(const QRegion &region, const QPoint &offset = QPoint());
+ void blitBuffer(GraphicsBuffer *sourceBuffer, const QRegion &sourceRegion,
+ GraphicsBuffer *destinationBuffer, const QPoint &destinationOffset = QPoint());
void backingPropertiesChanged();
QMacNotificationObserver m_backingPropertiesObserver;
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 8f50bc5834..6970c44a12 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -72,12 +72,11 @@ bool QCALayerBackingStore::eventFilter(QObject *watched, QEvent *event)
void QCALayerBackingStore::resize(const QSize &size, const QRegion &staticContents)
{
- qCDebug(lcQpaBackingStore) << "Resize requested to" << size;
-
- if (!staticContents.isNull())
- qCWarning(lcQpaBackingStore) << "QCALayerBackingStore does not support static contents";
+ qCDebug(lcQpaBackingStore) << "Resize requested to" << size
+ << "with static contents" << staticContents;
m_requestedSize = size;
+ m_staticContents = staticContents;
}
void QCALayerBackingStore::beginPaint(const QRegion &region)
@@ -193,7 +192,45 @@ bool QCALayerBackingStore::recreateBackBufferIfNeeded()
<< "based on requested" << m_requestedSize << "and dpr =" << devicePixelRatio;
static auto pixelFormat = QImage::toPixelFormat(QImage::Format_ARGB32_Premultiplied);
- m_buffers.back().reset(new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace()));
+ auto *newBackBuffer = new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace());
+
+ if (!m_staticContents.isEmpty() && m_buffers.back()) {
+ // We implicitly support static backingstore content as a result of
+ // finalizing the back buffer on flush, where we copy any non-painted
+ // areas from the front buffer. But there is no guarantee that a resize
+ // will always come after a flush, where we have a pristine front buffer
+ // to copy from. It may come after a few begin/endPaints, where the back
+ // buffer then contains (part of) the latest state. We also have the case
+ // of single-buffered backingstore, where the front and back buffer is
+ // the same, which means we must do the copy from the old back buffer
+ // to the newly resized buffer now, before we replace it below.
+
+ // If the back buffer has been partially filled already, we need to
+ // copy parts of the static content from that. The rest we copy from
+ // the front buffer.
+ const QRegion backBufferRegion = m_staticContents - m_buffers.back()->dirtyRegion;
+ const QRegion frontBufferRegion = m_staticContents - backBufferRegion;
+
+ qCInfo(lcQpaBackingStore) << "Preserving static content" << backBufferRegion
+ << "from back buffer, and" << frontBufferRegion << "from front buffer";
+
+ newBackBuffer->lock(QPlatformGraphicsBuffer::SWWriteAccess);
+ blitBuffer(m_buffers.back().get(), backBufferRegion, newBackBuffer);
+ Q_ASSERT(frontBufferRegion.isEmpty() || m_buffers.front());
+ blitBuffer(m_buffers.front().get(), frontBufferRegion, newBackBuffer);
+ newBackBuffer->unlock();
+
+ // The new back buffer now is valid for the static contents region.
+ // We don't need to maintain the static contents region for resizes
+ // of any other buffers in the swap chain, as these will finalize
+ // their content on flush from the buffer we just filled, and we
+ // don't need to mark them dirty for the area we just filled, as
+ // new buffers are fully dirty when created.
+ newBackBuffer->dirtyRegion -= m_staticContents;
+ m_staticContents = {};
+ }
+
+ m_buffers.back().reset(newBackBuffer);
return true;
}
@@ -256,7 +293,7 @@ bool QCALayerBackingStore::scroll(const QRegion &region, int dx, int dy)
if (!frontBufferRegion.isEmpty()) {
qCDebug(lcQpaBackingStore) << "Scrolling" << frontBufferRegion << "by copying from front buffer";
- preserveFromFrontBuffer(frontBufferRegion, scrollDelta);
+ blitBuffer(m_buffers.front().get(), frontBufferRegion, m_buffers.back().get(), scrollDelta);
}
m_buffers.back()->unlock();
@@ -475,54 +512,76 @@ void QCALayerBackingStore::finalizeBackBuffer()
if (!m_buffers.back()->isDirty())
return;
- m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
- preserveFromFrontBuffer(m_buffers.back()->dirtyRegion);
- m_buffers.back()->unlock();
+ qCDebug(lcQpaBackingStore) << "Finalizing back buffer with dirty region" << m_buffers.back()->dirtyRegion;
+
+ if (m_buffers.back() != m_buffers.front()) {
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
+ blitBuffer(m_buffers.front().get(), m_buffers.back()->dirtyRegion, m_buffers.back().get());
+ m_buffers.back()->unlock();
+ } else {
+ qCDebug(lcQpaBackingStore) << "Front and back buffer is the same. Can not finalize back buffer.";
+ }
// The back buffer is now completely in sync, ready to be presented
m_buffers.back()->dirtyRegion = QRegion();
}
-void QCALayerBackingStore::preserveFromFrontBuffer(const QRegion &region, const QPoint &offset)
+/*
+ \internal
+
+ Blits \a sourceRegion from \a sourceBuffer to \a destinationBuffer,
+ at offset \a destinationOffset.
+
+ The source buffer is automatically locked for read only access
+ during the blit.
+
+ The destination buffer has to be locked for write access by the
+ caller.
+*/
+
+void QCALayerBackingStore::blitBuffer(GraphicsBuffer *sourceBuffer, const QRegion &sourceRegion,
+ GraphicsBuffer *destinationBuffer, const QPoint &destinationOffset)
{
+ Q_ASSERT(sourceBuffer && destinationBuffer);
+ Q_ASSERT(sourceBuffer != destinationBuffer);
- if (m_buffers.front() == m_buffers.back())
- return; // Nothing to preserve from
+ if (sourceRegion.isEmpty())
+ return;
- qCDebug(lcQpaBackingStore) << "Preserving" << region << "of front buffer to"
- << region.translated(offset) << "of back buffer";
+ qCDebug(lcQpaBackingStore) << "Blitting" << sourceRegion << "of" << sourceBuffer
+ << "to" << sourceRegion.translated(destinationOffset) << "of" << destinationBuffer;
- Q_ASSERT(m_buffers.back()->isLocked() == QPlatformGraphicsBuffer::SWWriteAccess);
+ Q_ASSERT(destinationBuffer->isLocked() == QPlatformGraphicsBuffer::SWWriteAccess);
- m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess);
- const QImage *frontBuffer = m_buffers.front()->asImage();
+ sourceBuffer->lock(QPlatformGraphicsBuffer::SWReadAccess);
+ const QImage *sourceImage = sourceBuffer->asImage();
- const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size());
- const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio();
+ const QRect sourceBufferBounds(QPoint(0, 0), sourceBuffer->size());
+ const qreal sourceDevicePixelRatio = sourceImage->devicePixelRatio();
- QPainter painter(m_buffers.back()->asImage());
+ QPainter painter(destinationBuffer->asImage());
painter.setCompositionMode(QPainter::CompositionMode_Source);
// Let painter operate in device pixels, to make it easier to compare coordinates
- const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
- painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+ const qreal destinationDevicePixelRatio = painter.device()->devicePixelRatio();
+ painter.scale(1.0 / destinationDevicePixelRatio, 1.0 / destinationDevicePixelRatio);
- for (const QRect &rect : region) {
+ for (const QRect &rect : sourceRegion) {
QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio,
rect.size() * sourceDevicePixelRatio);
- QRect targetRect((rect.topLeft() + offset) * targetDevicePixelRatio,
- rect.size() * targetDevicePixelRatio);
+ QRect destinationRect((rect.topLeft() + destinationOffset) * destinationDevicePixelRatio,
+ rect.size() * destinationDevicePixelRatio);
#ifdef QT_DEBUG
- if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) {
- qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve"
- << QRegion(sourceRect).subtracted(frontSurfaceBounds);
+ if (Q_UNLIKELY(!sourceBufferBounds.contains(sourceRect.bottomRight()))) {
+ qCWarning(lcQpaBackingStore) << "Source buffer of size" << sourceBuffer->size()
+ << "is too small to blit" << sourceRect;
}
#endif
- painter.drawImage(targetRect, *frontBuffer, sourceRect);
+ painter.drawImage(destinationRect, *sourceImage, sourceRect);
}
- m_buffers.front()->unlock();
+ sourceBuffer->unlock();
}
// ----------------------------------------------------------------------------
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 8d5645f8bb..f21e683c00 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -232,6 +232,7 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons
case RasterGLSurface:
case ApplicationState:
case ApplicationIcon:
+ case BackingStoreStaticContents:
return true;
default:
return QPlatformIntegration::hasCapability(cap);