Provide SurfaceItem::shape as a floating vector

Our logical co-ordinates for shape can be floating. The shape is used to
determine final vertices on screen.

The commit appears to introduce some new loops but they're mostly what
QRegion would be doing internally so it shouldn't impact performance.
For most cases we just have a single rectangle in our shape anyway.

opaqueRegion is unchanged for now.
This commit is contained in:
David Edmundson 2022-09-12 10:29:33 +01:00
parent 482741499d
commit bcd9f1e958
15 changed files with 71 additions and 42 deletions

View file

@ -148,16 +148,21 @@ DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, Window *win
handleOutputChanged();
}
QRegion DecorationItem::shape() const
QVector<QRectF> DecorationItem::shape() const
{
QRectF left, top, right, bottom;
m_window->layoutDecorationRects(left, top, right, bottom);
return QRegion(left.toRect()).united(top.toRect()).united(right.toRect()).united(bottom.toRect());
return {left, top, right, bottom};
}
QRegion DecorationItem::opaque() const
{
return m_window->decorationHasAlpha() ? QRegion() : shape();
if (m_window->decorationHasAlpha()) {
return QRegion();
}
QRectF left, top, right, bottom;
m_window->layoutDecorationRects(left, top, right, bottom);
return QRegion(left.toRect()).united(top.toRect()).united(right.toRect()).united(bottom.toRect());
}
void DecorationItem::preprocess()

View file

@ -83,7 +83,7 @@ public:
DecorationRenderer *renderer() const;
Window *window() const;
QRegion shape() const override final;
QVector<QRectF> shape() const override final;
QRegion opaque() const override final;
private Q_SLOTS:

View file

@ -169,9 +169,9 @@ void Item::updateBoundingRect()
}
}
QRegion Item::shape() const
QVector<QRectF> Item::shape() const
{
return rect().toAlignedRect();
return QVector<QRectF>();
}
QRegion Item::opaque() const

View file

@ -11,6 +11,7 @@
#include <QMatrix4x4>
#include <QObject>
#include <QVector>
#include <optional>
@ -51,7 +52,7 @@ public:
*/
QRectF boundingRect() const;
virtual QRegion shape() const;
virtual QVector<QRectF> shape() const;
virtual QRegion opaque() const;
/**

View file

@ -386,8 +386,18 @@ void SceneOpenGL::createRenderNode(Item *item, RenderContext *context)
SurfacePixmap *pixmap = surfaceItem->pixmap();
if (pixmap) {
if (!geometry.isEmpty()) {
bool hasAlpha = pixmap->hasAlphaChannel();
bool isCompletelyOpaque = true;
// Don't bother with blending if the entire surface is opaque
bool hasAlpha = pixmap->hasAlphaChannel() && !surfaceItem->shape().subtracted(surfaceItem->opaque()).isEmpty();
const QVector<QRectF> shape = surfaceItem->shape();
for (const QRectF &shapePart : shape) {
if (!item->opaque().contains(shapePart.toRect())) {
isCompletelyOpaque = false;
break;
}
}
hasAlpha &= !isCompletelyOpaque;
context->renderNodes.append(RenderNode{
.texture = bindSurfaceTexture(surfaceItem),
.geometry = geometry,

View file

@ -167,7 +167,7 @@ void SceneQPainter::renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceIte
}
surfaceItem->resetDamage();
const QRegion shape = surfaceItem->shape();
const QVector<QRectF> shape = surfaceItem->shape();
for (const QRectF rect : shape) {
const QMatrix4x4 matrix = surfaceItem->surfaceToBufferMatrix();
const QPointF bufferTopLeft = matrix.map(rect.topLeft());

View file

@ -130,11 +130,11 @@ WindowQuadList SurfaceItem::buildQuads() const
return {};
}
const QRegion region = shape();
const QVector<QRectF> region = shape();
const auto size = pixmap()->size();
WindowQuadList quads;
quads.reserve(region.rectCount());
quads.reserve(region.count());
for (const QRectF rect : region) {
WindowQuad quad;

View file

@ -28,9 +28,9 @@ SurfaceItemInternal::SurfaceItemInternal(InternalWindow *window, Item *parent)
setSurfaceToBufferMatrix(surfaceToBufferMatrix);
}
QRegion SurfaceItemInternal::shape() const
QVector<QRectF> SurfaceItemInternal::shape() const
{
return QRegion(rect().toAlignedRect());
return {rect()};
}
std::unique_ptr<SurfacePixmap> SurfaceItemInternal::createPixmap()

View file

@ -25,7 +25,7 @@ class KWIN_EXPORT SurfaceItemInternal : public SurfaceItem
public:
explicit SurfaceItemInternal(InternalWindow *window, Item *parent = nullptr);
QRegion shape() const override;
QVector<QRectF> shape() const override;
private Q_SLOTS:
void handleBufferGeometryChanged(Window *window, const QRectF &old);

View file

@ -53,9 +53,9 @@ SurfaceItemWayland::SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface
setSurfaceToBufferMatrix(surface->surfaceToBufferMatrix());
}
QRegion SurfaceItemWayland::shape() const
QVector<QRectF> SurfaceItemWayland::shape() const
{
return QRegion(rect().toAlignedRect());
return {rect()};
}
QRegion SurfaceItemWayland::opaque() const
@ -208,12 +208,16 @@ SurfaceItemXwayland::SurfaceItemXwayland(Window *window, Item *parent)
connect(window, &Window::geometryShapeChanged, this, &SurfaceItemXwayland::discardQuads);
}
QRegion SurfaceItemXwayland::shape() const
QVector<QRectF> SurfaceItemXwayland::shape() const
{
const QRectF clipRect = rect() & window()->clientGeometry().translated(-window()->bufferGeometry().topLeft());
const QRegion shape = window()->shapeRegion();
QVector<QRectF> shape = window()->shapeRegion();
return shape & clipRect.toRect();
// bounded to clipRect
for (QRectF &shapePart : shape) {
shapePart = shapePart.intersected(clipRect);
}
return shape;
}
} // namespace KWin

View file

@ -29,7 +29,7 @@ public:
explicit SurfaceItemWayland(KWaylandServer::SurfaceInterface *surface,
Window *window, Item *parent = nullptr);
QRegion shape() const override;
QVector<QRectF> shape() const override;
QRegion opaque() const override;
ContentType contentType() const override;
@ -88,7 +88,7 @@ class KWIN_EXPORT SurfaceItemXwayland : public SurfaceItemWayland
public:
explicit SurfaceItemXwayland(Window *window, Item *parent = nullptr);
QRegion shape() const override;
QVector<QRectF> shape() const override;
};
} // namespace KWin

View file

@ -138,21 +138,29 @@ void SurfaceItemX11::handleGeometryShapeChanged()
discardQuads();
}
QRegion SurfaceItemX11::shape() const
QVector<QRectF> SurfaceItemX11::shape() const
{
const QRectF clipRect = window()->clientGeometry().translated(-window()->bufferGeometry().topLeft());
const QRegion shape = window()->shapeRegion();
return shape & clipRect.toAlignedRect();
QVector<QRectF> shape = window()->shapeRegion();
// bounded to clipRect
for (QRectF &shapePart : shape) {
shapePart = shapePart.intersected(clipRect);
}
return shape;
}
QRegion SurfaceItemX11::opaque() const
{
if (!window()->hasAlpha()) {
return shape();
} else {
return window()->opaqueRegion() & shape();
QRegion shapeRegion;
for (const QRectF &shapePart : shape()) {
shapeRegion |= shapePart.toRect();
}
if (!window()->hasAlpha()) {
return shapeRegion;
} else {
return window()->opaqueRegion() & shapeRegion;
}
return QRegion();
}
std::unique_ptr<SurfacePixmap> SurfaceItemX11::createPixmap()

View file

@ -32,7 +32,7 @@ public:
void waitForDamage();
void destroyDamage();
QRegion shape() const override;
QVector<QRectF> shape() const override;
QRegion opaque() const override;
private Q_SLOTS:

View file

@ -483,13 +483,12 @@ void Window::getWmOpaqueRegion()
const auto rects = info->opaqueRegion();
QRegion new_opaque_region;
for (const auto &r : rects) {
new_opaque_region += Xcb::fromXNative(QRect(r.pos.x, r.pos.y, r.size.width, r.size.height)).toAlignedRect();
new_opaque_region |= Xcb::fromXNative(QRect(r.pos.x, r.pos.y, r.size.width, r.size.height)).toRect();
}
opaque_region = new_opaque_region;
}
QRegion Window::shapeRegion() const
QVector<QRectF> Window::shapeRegion() const
{
if (m_shapeRegionIsValid) {
return m_shapeRegion;
@ -501,19 +500,21 @@ QRegion Window::shapeRegion() const
auto cookie = xcb_shape_get_rectangles_unchecked(kwinApp()->x11Connection(), frameId(), XCB_SHAPE_SK_BOUNDING);
UniqueCPtr<xcb_shape_get_rectangles_reply_t> reply(xcb_shape_get_rectangles_reply(kwinApp()->x11Connection(), cookie, nullptr));
if (reply) {
m_shapeRegion = QRegion();
m_shapeRegion.clear();
const xcb_rectangle_t *rects = xcb_shape_get_rectangles_rectangles(reply.get());
const int rectCount = xcb_shape_get_rectangles_rectangles_length(reply.get());
for (int i = 0; i < rectCount; ++i) {
m_shapeRegion += Xcb::fromXNative(QRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height)).toAlignedRect();
QRectF region = Xcb::fromXNative(QRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height)).toAlignedRect();
// make sure the shape is sane (X is async, maybe even XShape is broken)
region = region.intersected(QRectF(QPointF(0, 0), bufferGeometry.size()));
m_shapeRegion += region;
}
// make sure the shape is sane (X is async, maybe even XShape is broken)
m_shapeRegion &= QRegion(0, 0, bufferGeometry.width(), bufferGeometry.height());
} else {
m_shapeRegion = QRegion();
m_shapeRegion.clear();
}
} else {
m_shapeRegion = QRegion(0, 0, bufferGeometry.width(), bufferGeometry.height());
m_shapeRegion = {QRectF(0, 0, bufferGeometry.width(), bufferGeometry.height())};
}
m_shapeRegionIsValid = true;
@ -523,7 +524,7 @@ QRegion Window::shapeRegion() const
void Window::discardShapeRegion()
{
m_shapeRegionIsValid = false;
m_shapeRegion = QRegion();
m_shapeRegion.clear();
}
bool Window::isClient() const

View file

@ -773,7 +773,7 @@ public:
* @see hasAlpha
*/
const QRegion &opaqueRegion() const;
QRegion shapeRegion() const;
QVector<QRectF> shapeRegion() const;
bool skipsCloseAnimation() const;
void setSkipCloseAnimation(bool set);
@ -1925,7 +1925,7 @@ private:
ClientMachine *m_clientMachine;
xcb_window_t m_wmClientLeader;
QRegion opaque_region;
mutable QRegion m_shapeRegion;
mutable QVector<QRectF> m_shapeRegion;
mutable bool m_shapeRegionIsValid = false;
bool m_skipCloseAnimation;
quint32 m_pendingSurfaceId = 0;