File indexing completed on 2024-05-19 05:32:21

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "scene/surfaceitem.h"
0008 #include "core/pixelgrid.h"
0009 #include "scene/scene.h"
0010 
0011 using namespace std::chrono_literals;
0012 
0013 namespace KWin
0014 {
0015 
0016 SurfaceItem::SurfaceItem(Scene *scene, Item *parent)
0017     : Item(scene, parent)
0018 {
0019 }
0020 
0021 QSizeF SurfaceItem::destinationSize() const
0022 {
0023     return m_destinationSize;
0024 }
0025 
0026 void SurfaceItem::setDestinationSize(const QSizeF &size)
0027 {
0028     if (m_destinationSize != size) {
0029         m_destinationSize = size;
0030         setSize(size);
0031         discardQuads();
0032     }
0033 }
0034 
0035 QRectF SurfaceItem::bufferSourceBox() const
0036 {
0037     return m_bufferSourceBox;
0038 }
0039 
0040 void SurfaceItem::setBufferSourceBox(const QRectF &box)
0041 {
0042     if (m_bufferSourceBox != box) {
0043         m_bufferSourceBox = box;
0044         discardQuads();
0045     }
0046 }
0047 
0048 OutputTransform SurfaceItem::bufferTransform() const
0049 {
0050     return m_surfaceToBufferTransform;
0051 }
0052 
0053 void SurfaceItem::setBufferTransform(OutputTransform transform)
0054 {
0055     if (m_surfaceToBufferTransform != transform) {
0056         m_surfaceToBufferTransform = transform;
0057         m_bufferToSurfaceTransform = transform.inverted();
0058         discardQuads();
0059     }
0060 }
0061 
0062 QSize SurfaceItem::bufferSize() const
0063 {
0064     return m_bufferSize;
0065 }
0066 
0067 void SurfaceItem::setBufferSize(const QSize &size)
0068 {
0069     if (m_bufferSize != size) {
0070         m_bufferSize = size;
0071         discardPixmap();
0072         discardQuads();
0073     }
0074 }
0075 
0076 QRegion SurfaceItem::mapFromBuffer(const QRegion &region) const
0077 {
0078     const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
0079     const qreal xScale = m_destinationSize.width() / sourceBox.width();
0080     const qreal yScale = m_destinationSize.height() / sourceBox.height();
0081 
0082     QRegion result;
0083     for (QRectF rect : region) {
0084         const QRectF r = m_bufferToSurfaceTransform.map(rect, m_bufferSize).translated(-sourceBox.topLeft());
0085         result += QRectF(r.x() * xScale, r.y() * yScale, r.width() * xScale, r.height() * yScale).toAlignedRect();
0086     }
0087     return result;
0088 }
0089 
0090 static QRegion expandRegion(const QRegion &region, const QMargins &padding)
0091 {
0092     if (region.isEmpty()) {
0093         return QRegion();
0094     }
0095 
0096     QRegion ret;
0097     for (const QRect &rect : region) {
0098         ret += rect.marginsAdded(padding);
0099     }
0100 
0101     return ret;
0102 }
0103 
0104 void SurfaceItem::addDamage(const QRegion &region)
0105 {
0106     if (m_lastDamage) {
0107         const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
0108         m_lastDamageTimeDiffs.push_back(diff);
0109         if (m_lastDamageTimeDiffs.size() > 100) {
0110             m_lastDamageTimeDiffs.pop_front();
0111         }
0112         m_frameTimeEstimation = std::accumulate(m_lastDamageTimeDiffs.begin(), m_lastDamageTimeDiffs.end(), 0ns) / m_lastDamageTimeDiffs.size();
0113     }
0114     m_lastDamage = std::chrono::steady_clock::now();
0115     m_damage += region;
0116 
0117     const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
0118     const qreal xScale = sourceBox.width() / m_destinationSize.width();
0119     const qreal yScale = sourceBox.height() / m_destinationSize.height();
0120     const QRegion logicalDamage = mapFromBuffer(region);
0121 
0122     const auto delegates = scene()->delegates();
0123     for (SceneDelegate *delegate : delegates) {
0124         QRegion delegateDamage = logicalDamage;
0125         const qreal delegateScale = delegate->scale();
0126         if (xScale != delegateScale || yScale != delegateScale) {
0127             // Simplified version of ceil(ceil(0.5 * output_scale / surface_scale) / output_scale)
0128             const int xPadding = std::ceil(0.5 / xScale);
0129             const int yPadding = std::ceil(0.5 / yScale);
0130             delegateDamage = expandRegion(delegateDamage, QMargins(xPadding, yPadding, xPadding, yPadding));
0131         }
0132         scheduleRepaint(delegate, delegateDamage);
0133     }
0134 
0135     Q_EMIT damaged();
0136 }
0137 
0138 void SurfaceItem::resetDamage()
0139 {
0140     m_damage = QRegion();
0141 }
0142 
0143 QRegion SurfaceItem::damage() const
0144 {
0145     return m_damage;
0146 }
0147 
0148 SurfacePixmap *SurfaceItem::pixmap() const
0149 {
0150     if (m_pixmap && m_pixmap->isValid()) {
0151         return m_pixmap.get();
0152     }
0153     if (m_previousPixmap && m_previousPixmap->isValid()) {
0154         return m_previousPixmap.get();
0155     }
0156     return nullptr;
0157 }
0158 
0159 SurfacePixmap *SurfaceItem::previousPixmap() const
0160 {
0161     return m_previousPixmap.get();
0162 }
0163 
0164 void SurfaceItem::referencePreviousPixmap()
0165 {
0166     if (m_previousPixmap && m_previousPixmap->isDiscarded()) {
0167         m_referencePixmapCounter++;
0168     }
0169 }
0170 
0171 void SurfaceItem::unreferencePreviousPixmap()
0172 {
0173     if (!m_previousPixmap || !m_previousPixmap->isDiscarded()) {
0174         return;
0175     }
0176     m_referencePixmapCounter--;
0177     if (m_referencePixmapCounter == 0) {
0178         m_previousPixmap.reset();
0179     }
0180 }
0181 
0182 void SurfaceItem::updatePixmap()
0183 {
0184     if (!m_pixmap) {
0185         m_pixmap = createPixmap();
0186     }
0187     if (m_pixmap->isValid()) {
0188         m_pixmap->update();
0189     } else {
0190         m_pixmap->create();
0191         if (m_pixmap->isValid()) {
0192             unreferencePreviousPixmap();
0193             discardQuads();
0194         }
0195     }
0196 }
0197 
0198 void SurfaceItem::discardPixmap()
0199 {
0200     if (m_pixmap) {
0201         if (m_pixmap->isValid()) {
0202             m_previousPixmap = std::move(m_pixmap);
0203             m_previousPixmap->markAsDiscarded();
0204             referencePreviousPixmap();
0205         } else {
0206             m_pixmap.reset();
0207         }
0208     }
0209 }
0210 
0211 void SurfaceItem::destroyPixmap()
0212 {
0213     m_pixmap.reset();
0214 }
0215 
0216 void SurfaceItem::preprocess()
0217 {
0218     updatePixmap();
0219 }
0220 
0221 WindowQuadList SurfaceItem::buildQuads() const
0222 {
0223     if (!pixmap()) {
0224         return {};
0225     }
0226 
0227     const QList<QRectF> region = shape();
0228     WindowQuadList quads;
0229     quads.reserve(region.count());
0230 
0231     const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
0232     const qreal xScale = sourceBox.width() / m_destinationSize.width();
0233     const qreal yScale = sourceBox.height() / m_destinationSize.height();
0234 
0235     for (const QRectF rect : region) {
0236         WindowQuad quad;
0237 
0238         const QPointF bufferTopLeft = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.left() * xScale, rect.top() * yScale), sourceBox.size()));
0239         const QPointF bufferTopRight = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.right() * xScale, rect.top() * yScale), sourceBox.size()));
0240         const QPointF bufferBottomRight = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.right() * xScale, rect.bottom() * yScale), sourceBox.size()));
0241         const QPointF bufferBottomLeft = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.left() * xScale, rect.bottom() * yScale), sourceBox.size()));
0242 
0243         quad[0] = WindowVertex(rect.topLeft(), QPointF{bufferTopLeft.x() / m_bufferSize.width(), bufferTopLeft.y() / m_bufferSize.height()});
0244         quad[1] = WindowVertex(rect.topRight(), QPointF{bufferTopRight.x() / m_bufferSize.width(), bufferTopRight.y() / m_bufferSize.height()});
0245         quad[2] = WindowVertex(rect.bottomRight(), QPointF{bufferBottomRight.x() / m_bufferSize.width(), bufferBottomRight.y() / m_bufferSize.height()});
0246         quad[3] = WindowVertex(rect.bottomLeft(), QPointF{bufferBottomLeft.x() / m_bufferSize.width(), bufferBottomLeft.y() / m_bufferSize.height()});
0247 
0248         quads << quad;
0249     }
0250 
0251     return quads;
0252 }
0253 
0254 ContentType SurfaceItem::contentType() const
0255 {
0256     return ContentType::None;
0257 }
0258 
0259 void SurfaceItem::freeze()
0260 {
0261 }
0262 
0263 std::chrono::nanoseconds SurfaceItem::frameTimeEstimation() const
0264 {
0265     if (m_lastDamage) {
0266         const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
0267         return std::max(m_frameTimeEstimation, diff);
0268     } else {
0269         return m_frameTimeEstimation;
0270     }
0271 }
0272 
0273 SurfaceTexture::~SurfaceTexture()
0274 {
0275 }
0276 
0277 SurfacePixmap::SurfacePixmap(std::unique_ptr<SurfaceTexture> &&texture, QObject *parent)
0278     : QObject(parent)
0279     , m_texture(std::move(texture))
0280 {
0281 }
0282 
0283 GraphicsBuffer *SurfacePixmap::buffer() const
0284 {
0285     return m_bufferRef.buffer();
0286 }
0287 
0288 void SurfacePixmap::setBuffer(GraphicsBuffer *buffer)
0289 {
0290     if (m_bufferRef.buffer() == buffer) {
0291         return;
0292     }
0293     m_bufferRef = buffer;
0294     if (m_bufferRef) {
0295         m_hasAlphaChannel = m_bufferRef->hasAlphaChannel();
0296         m_size = m_bufferRef->size();
0297     }
0298 }
0299 
0300 GraphicsBufferOrigin SurfacePixmap::bufferOrigin() const
0301 {
0302     return m_bufferOrigin;
0303 }
0304 
0305 void SurfacePixmap::setBufferOrigin(GraphicsBufferOrigin origin)
0306 {
0307     m_bufferOrigin = origin;
0308 }
0309 
0310 void SurfacePixmap::update()
0311 {
0312 }
0313 
0314 SurfaceTexture *SurfacePixmap::texture() const
0315 {
0316     return m_texture.get();
0317 }
0318 
0319 bool SurfacePixmap::hasAlphaChannel() const
0320 {
0321     return m_hasAlphaChannel;
0322 }
0323 
0324 QSize SurfacePixmap::size() const
0325 {
0326     return m_size;
0327 }
0328 
0329 bool SurfacePixmap::isDiscarded() const
0330 {
0331     return m_isDiscarded;
0332 }
0333 
0334 void SurfacePixmap::markAsDiscarded()
0335 {
0336     m_isDiscarded = true;
0337 }
0338 
0339 } // namespace KWin
0340 
0341 #include "moc_surfaceitem.cpp"