macOS: Track painted area of backingstore buffer via its dirty region

When introducing support for scrolling the backingstore it doesn't make
sense to track the painted region explicitly.

Pick-to: 6.2
Change-Id: I370932f02490ac526fb049908f99af678884e807
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2021-11-04 13:29:05 +01:00
parent 38130406ca
commit 5190e77d87
2 changed files with 31 additions and 20 deletions

View File

@ -85,7 +85,6 @@ private:
bool eventFilter(QObject *watched, QEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override;
QSize m_requestedSize; QSize m_requestedSize;
QRegion m_paintedRegion;
class GraphicsBuffer : public QIOSurfaceGraphicsBuffer class GraphicsBuffer : public QIOSurfaceGraphicsBuffer
{ {
@ -96,12 +95,16 @@ private:
QRegion dirtyRegion; // In unscaled coordinates QRegion dirtyRegion; // In unscaled coordinates
QImage *asImage(); QImage *asImage();
qreal devicePixelRatio() const { return m_devicePixelRatio; } qreal devicePixelRatio() const { return m_devicePixelRatio; }
bool isDirty() const { return !dirtyRegion.isEmpty(); }
QRegion validRegion() const;
private: private:
qreal m_devicePixelRatio; qreal m_devicePixelRatio;
QImage m_image; QImage m_image;
}; };
void updateDirtyStates(const QRegion &paintedRegion);
void ensureBackBuffer(); void ensureBackBuffer();
bool recreateBackBufferIfNeeded(); bool recreateBackBufferIfNeeded();
bool prepareForFlush(); bool prepareForFlush();

View File

@ -140,7 +140,8 @@ void QCALayerBackingStore::beginPaint(const QRegion &region)
painter.fillRect(rect, Qt::transparent); painter.fillRect(rect, Qt::transparent);
} }
m_paintedRegion += region; // We assume the client is going to paint the entire region
updateDirtyStates(region);
} }
void QCALayerBackingStore::ensureBackBuffer() void QCALayerBackingStore::ensureBackBuffer()
@ -242,7 +243,7 @@ QPaintDevice *QCALayerBackingStore::paintDevice()
void QCALayerBackingStore::endPaint() void QCALayerBackingStore::endPaint()
{ {
qCInfo(lcQpaBackingStore) << "Paint ended with painted region" << m_paintedRegion; qCInfo(lcQpaBackingStore) << "Paint ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
m_buffers.back()->unlock(); m_buffers.back()->unlock();
} }
@ -259,7 +260,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
return; return;
} }
if (m_buffers.front()->isInUse() && m_buffers.front()->dirtyRegion.isEmpty()) { if (m_buffers.front()->isInUse() && !m_buffers.front()->isDirty()) {
qCInfo(lcQpaBackingStore) << "Asked to flush, but front buffer is up to date. Ignoring."; qCInfo(lcQpaBackingStore) << "Asked to flush, but front buffer is up to date. Ignoring.";
return; return;
} }
@ -414,6 +415,20 @@ QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const
return m_buffers.back().get(); return m_buffers.back().get();
} }
void QCALayerBackingStore::updateDirtyStates(const QRegion &paintedRegion)
{
// Update dirty state of buffers based on what was painted. The back buffer will be
// less dirty, since we painted to it, while other buffers will become more dirty.
// This allows us to minimize copies between front and back buffers on swap in the
// cases where the painted region overlaps with the previous frame (front buffer).
for (const auto &buffer : m_buffers) {
if (buffer == m_buffers.back())
buffer->dirtyRegion -= paintedRegion;
else
buffer->dirtyRegion += paintedRegion;
}
}
bool QCALayerBackingStore::prepareForFlush() bool QCALayerBackingStore::prepareForFlush()
{ {
if (!m_buffers.back()) { if (!m_buffers.back()) {
@ -421,21 +436,10 @@ bool QCALayerBackingStore::prepareForFlush()
return false; return false;
} }
// Update dirty state of buffers based on what was painted. The back buffer will be
// less dirty, since we painted to it, while other buffers will become more dirty.
// This allows us to minimize copies between front and back buffers on swap in the
// cases where the painted region overlaps with the previous frame (front buffer).
for (const auto &buffer : m_buffers) {
if (buffer == m_buffers.back())
buffer->dirtyRegion -= m_paintedRegion;
else
buffer->dirtyRegion += m_paintedRegion;
}
// After painting, the back buffer is only guaranteed to have content for the painted // After painting, the back buffer is only guaranteed to have content for the painted
// region, and may still have dirty areas that need to be synced up with the front buffer, // region, and may still have dirty areas that need to be synced up with the front buffer,
// if we have one. We know that the front buffer is always up to date. // if we have one. We know that the front buffer is always up to date.
if (!m_buffers.back()->dirtyRegion.isEmpty() && m_buffers.front() != m_buffers.back()) { if (m_buffers.back()->isDirty() && m_buffers.front() != m_buffers.back()) {
QRegion preserveRegion = m_buffers.back()->dirtyRegion; QRegion preserveRegion = m_buffers.back()->dirtyRegion;
qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer"; qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer";
@ -473,9 +477,6 @@ bool QCALayerBackingStore::prepareForFlush()
m_buffers.back()->dirtyRegion = QRegion(); m_buffers.back()->dirtyRegion = QRegion();
} }
// Prepare for another round of painting
m_paintedRegion = QRegion();
return true; return true;
} }
@ -484,12 +485,19 @@ bool QCALayerBackingStore::prepareForFlush()
QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio, QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio,
const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace)
: QIOSurfaceGraphicsBuffer(size, format) : QIOSurfaceGraphicsBuffer(size, format)
, dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio) , dirtyRegion(QRect(QPoint(0, 0), size / devicePixelRatio))
, m_devicePixelRatio(devicePixelRatio) , m_devicePixelRatio(devicePixelRatio)
{ {
setColorSpace(colorSpace); setColorSpace(colorSpace);
} }
QRegion QCALayerBackingStore::GraphicsBuffer::validRegion() const
{
QRegion fullRegion = QRect(QPoint(0, 0), size() / m_devicePixelRatio);
return fullRegion - dirtyRegion;
}
QImage *QCALayerBackingStore::GraphicsBuffer::asImage() QImage *QCALayerBackingStore::GraphicsBuffer::asImage()
{ {
if (m_image.isNull()) { if (m_image.isNull()) {