You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

341 lines
8.7 KiB

/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "scene/surfaceitem.h"
#include "core/pixelgrid.h"
#include "scene/scene.h"
using namespace std::chrono_literals;
namespace KWin
{
SurfaceItem::SurfaceItem(Scene *scene, Item *parent)
: Item(scene, parent)
{
}
QSizeF SurfaceItem::destinationSize() const
{
return m_destinationSize;
}
void SurfaceItem::setDestinationSize(const QSizeF &size)
{
if (m_destinationSize != size) {
m_destinationSize = size;
setSize(size);
discardQuads();
}
}
QRectF SurfaceItem::bufferSourceBox() const
{
return m_bufferSourceBox;
}
void SurfaceItem::setBufferSourceBox(const QRectF &box)
{
if (m_bufferSourceBox != box) {
m_bufferSourceBox = box;
discardQuads();
}
}
OutputTransform SurfaceItem::bufferTransform() const
{
return m_surfaceToBufferTransform;
}
void SurfaceItem::setBufferTransform(OutputTransform transform)
{
if (m_surfaceToBufferTransform != transform) {
m_surfaceToBufferTransform = transform;
m_bufferToSurfaceTransform = transform.inverted();
discardQuads();
}
}
QSize SurfaceItem::bufferSize() const
{
return m_bufferSize;
}
void SurfaceItem::setBufferSize(const QSize &size)
{
if (m_bufferSize != size) {
m_bufferSize = size;
discardPixmap();
discardQuads();
}
}
QRegion SurfaceItem::mapFromBuffer(const QRegion &region) const
{
const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
const qreal xScale = m_destinationSize.width() / sourceBox.width();
const qreal yScale = m_destinationSize.height() / sourceBox.height();
QRegion result;
for (QRectF rect : region) {
const QRectF r = m_bufferToSurfaceTransform.map(rect, m_bufferSize).translated(-sourceBox.topLeft());
result += QRectF(r.x() * xScale, r.y() * yScale, r.width() * xScale, r.height() * yScale).toAlignedRect();
}
return result;
}
static QRegion expandRegion(const QRegion &region, const QMargins &padding)
{
if (region.isEmpty()) {
return QRegion();
}
QRegion ret;
for (const QRect &rect : region) {
ret += rect.marginsAdded(padding);
}
return ret;
}
void SurfaceItem::addDamage(const QRegion &region)
{
if (m_lastDamage) {
const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
m_lastDamageTimeDiffs.push_back(diff);
if (m_lastDamageTimeDiffs.size() > 100) {
m_lastDamageTimeDiffs.pop_front();
}
m_frameTimeEstimation = std::accumulate(m_lastDamageTimeDiffs.begin(), m_lastDamageTimeDiffs.end(), 0ns) / m_lastDamageTimeDiffs.size();
}
m_lastDamage = std::chrono::steady_clock::now();
m_damage += region;
const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
const qreal xScale = sourceBox.width() / m_destinationSize.width();
const qreal yScale = sourceBox.height() / m_destinationSize.height();
const QRegion logicalDamage = mapFromBuffer(region);
const auto delegates = scene()->delegates();
for (SceneDelegate *delegate : delegates) {
QRegion delegateDamage = logicalDamage;
const qreal delegateScale = delegate->scale();
if (xScale != delegateScale || yScale != delegateScale) {
// Simplified version of ceil(ceil(0.5 * output_scale / surface_scale) / output_scale)
const int xPadding = std::ceil(0.5 / xScale);
const int yPadding = std::ceil(0.5 / yScale);
delegateDamage = expandRegion(delegateDamage, QMargins(xPadding, yPadding, xPadding, yPadding));
}
scheduleRepaint(delegate, delegateDamage);
}
Q_EMIT damaged();
}
void SurfaceItem::resetDamage()
{
m_damage = QRegion();
}
QRegion SurfaceItem::damage() const
{
return m_damage;
}
SurfacePixmap *SurfaceItem::pixmap() const
{
if (m_pixmap && m_pixmap->isValid()) {
return m_pixmap.get();
}
if (m_previousPixmap && m_previousPixmap->isValid()) {
return m_previousPixmap.get();
}
return nullptr;
}
SurfacePixmap *SurfaceItem::previousPixmap() const
{
return m_previousPixmap.get();
}
void SurfaceItem::referencePreviousPixmap()
{
if (m_previousPixmap && m_previousPixmap->isDiscarded()) {
m_referencePixmapCounter++;
}
}
void SurfaceItem::unreferencePreviousPixmap()
{
if (!m_previousPixmap || !m_previousPixmap->isDiscarded()) {
return;
}
m_referencePixmapCounter--;
if (m_referencePixmapCounter == 0) {
m_previousPixmap.reset();
}
}
void SurfaceItem::updatePixmap()
{
if (!m_pixmap) {
m_pixmap = createPixmap();
}
if (m_pixmap->isValid()) {
m_pixmap->update();
} else {
m_pixmap->create();
if (m_pixmap->isValid()) {
unreferencePreviousPixmap();
discardQuads();
}
}
}
void SurfaceItem::discardPixmap()
{
if (m_pixmap) {
if (m_pixmap->isValid()) {
m_previousPixmap = std::move(m_pixmap);
m_previousPixmap->markAsDiscarded();
referencePreviousPixmap();
} else {
m_pixmap.reset();
}
}
}
void SurfaceItem::destroyPixmap()
{
m_pixmap.reset();
}
void SurfaceItem::preprocess()
{
updatePixmap();
}
WindowQuadList SurfaceItem::buildQuads() const
{
if (!pixmap()) {
return {};
}
const QList<QRectF> region = shape();
WindowQuadList quads;
quads.reserve(region.count());
const QRectF sourceBox = m_bufferToSurfaceTransform.map(m_bufferSourceBox, m_bufferSize);
const qreal xScale = sourceBox.width() / m_destinationSize.width();
const qreal yScale = sourceBox.height() / m_destinationSize.height();
for (const QRectF rect : region) {
WindowQuad quad;
const QPointF bufferTopLeft = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.left() * xScale, rect.top() * yScale), sourceBox.size()));
const QPointF bufferTopRight = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.right() * xScale, rect.top() * yScale), sourceBox.size()));
const QPointF bufferBottomRight = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.right() * xScale, rect.bottom() * yScale), sourceBox.size()));
const QPointF bufferBottomLeft = snapToPixelGridF(m_bufferSourceBox.topLeft() + m_surfaceToBufferTransform.map(QPointF(rect.left() * xScale, rect.bottom() * yScale), sourceBox.size()));
quad[0] = WindowVertex(rect.topLeft(), QPointF{bufferTopLeft.x() / m_bufferSize.width(), bufferTopLeft.y() / m_bufferSize.height()});
quad[1] = WindowVertex(rect.topRight(), QPointF{bufferTopRight.x() / m_bufferSize.width(), bufferTopRight.y() / m_bufferSize.height()});
quad[2] = WindowVertex(rect.bottomRight(), QPointF{bufferBottomRight.x() / m_bufferSize.width(), bufferBottomRight.y() / m_bufferSize.height()});
quad[3] = WindowVertex(rect.bottomLeft(), QPointF{bufferBottomLeft.x() / m_bufferSize.width(), bufferBottomLeft.y() / m_bufferSize.height()});
quads << quad;
}
return quads;
}
ContentType SurfaceItem::contentType() const
{
return ContentType::None;
}
void SurfaceItem::freeze()
{
}
std::chrono::nanoseconds SurfaceItem::frameTimeEstimation() const
{
if (m_lastDamage) {
const auto diff = std::chrono::steady_clock::now() - *m_lastDamage;
return std::max(m_frameTimeEstimation, diff);
} else {
return m_frameTimeEstimation;
}
}
SurfaceTexture::~SurfaceTexture()
{
}
SurfacePixmap::SurfacePixmap(std::unique_ptr<SurfaceTexture> &&texture, QObject *parent)
: QObject(parent)
, m_texture(std::move(texture))
{
}
GraphicsBuffer *SurfacePixmap::buffer() const
{
return m_bufferRef.buffer();
}
void SurfacePixmap::setBuffer(GraphicsBuffer *buffer)
{
if (m_bufferRef.buffer() == buffer) {
return;
}
m_bufferRef = buffer;
if (m_bufferRef) {
m_hasAlphaChannel = m_bufferRef->hasAlphaChannel();
m_size = m_bufferRef->size();
}
}
GraphicsBufferOrigin SurfacePixmap::bufferOrigin() const
{
return m_bufferOrigin;
}
void SurfacePixmap::setBufferOrigin(GraphicsBufferOrigin origin)
{
m_bufferOrigin = origin;
}
void SurfacePixmap::update()
{
}
SurfaceTexture *SurfacePixmap::texture() const
{
return m_texture.get();
}
bool SurfacePixmap::hasAlphaChannel() const
{
return m_hasAlphaChannel;
}
QSize SurfacePixmap::size() const
{
return m_size;
}
bool SurfacePixmap::isDiscarded() const
{
return m_isDiscarded;
}
void SurfacePixmap::markAsDiscarded()
{
m_isDiscarded = true;
}
} // namespace KWin
#include "moc_surfaceitem.cpp"