File indexing completed on 2024-11-10 04:57:16
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 ®ion) 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 ®ion, 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 ®ion) 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"