Commit f2c8981f authored by Vlad Zahorodnii's avatar Vlad Zahorodnii

[scene] Generate window quads for sub-surfaces

No window quads are generated for sub-surfaces right now. This leads to
issues with effects that operate on window quads, e.g. magic lamp and
wobbly windows. Furthermore, the OpenGL scene needs window quads to
properly clip windows during the rendering process.

The best way to render sub-surfaces would be with a little help from a
scene graph. Contrary to GNOME, KDE hasn't developed any scene graph
implementation that we could use in kwin. As a short term solution, this
change adjusts the scene to generate window quads.

Window quads are generated as we traverse the current window pixmap tree
in the depth-first search manner. In order to match a list of quads with
a particular WindowPixmap, we assign an id to each quad.

BUG: 387313
FIXED-IN: 5.19.0

Differential Revision:
parent 2d88fd0e
This diff is collapsed.
......@@ -148,26 +148,38 @@ class OpenGLWindowPixmap;
class OpenGLWindow final : public Scene::Window
enum Leaf { ShadowLeaf = 0, DecorationLeaf, ContentLeaf, PreviousContentLeaf, LeafCount };
enum Leaf { ShadowLeaf, DecorationLeaf, ContentLeaf, PreviousContentLeaf };
struct LeafNode
struct RenderNode
: texture(nullptr),
: texture(nullptr)
, firstVertex(0)
, vertexCount(0)
, opacity(1.0)
, hasAlpha(false)
, coordinateType(UnnormalizedCoordinates)
GLTexture *texture;
WindowQuadList quads;
int firstVertex;
int vertexCount;
float opacity;
bool hasAlpha;
TextureCoordinateType coordinateType;
Leaf leafType;
struct RenderContext
QVector<RenderNode> renderNodes;
int shadowOffset = 0;
int decorationOffset = 0;
int contentOffset = 0;
int previousContentOffset = 0;
int quadCount = 0;
OpenGLWindow(Toplevel *toplevel, SceneOpenGL *scene);
......@@ -182,9 +194,7 @@ private:
QMatrix4x4 modelViewProjectionMatrix(int mask, const WindowPaintData &data) const;
QVector4D modulate(float opacity, float brightness) const;
void setBlendEnabled(bool enabled);
void setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data);
void renderSubSurface(GLShader *shader, const QMatrix4x4 &mvp, const QMatrix4x4 &windowMatrix,
OpenGLWindowPixmap *pixmap, const QRegion &region, bool hardwareClipping);
void initializeRenderContext(RenderContext &context, const WindowPaintData &data);
bool beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data);
void endRenderWindow();
bool bindTexture();
......@@ -787,11 +787,8 @@ QRegion Scene::Window::bufferShape() const
QRegion Scene::Window::clientShape() const
if (AbstractClient *client = qobject_cast<AbstractClient *>(toplevel)) {
if (client->isShade()) {
return QRegion();
if (isShaded())
return QRegion();
const QRegion shape = bufferShape();
const QMargins bufferMargins = toplevel->bufferMargins();
......@@ -833,6 +830,13 @@ bool Scene::Window::isOpaque() const
return toplevel->opacity() == 1.0 && !toplevel->hasAlpha();
bool Scene::Window::isShaded() const
if (AbstractClient *client = qobject_cast<AbstractClient *>(toplevel))
return client->isShade();
return false;
bool Scene::Window::isPaintingEnabled() const
return !disable_painting;
......@@ -877,7 +881,11 @@ WindowQuadList Scene::Window::buildQuads(bool force) const
if (cached_quad_list != nullptr && !force)
return *cached_quad_list;
WindowQuadList ret = makeContentsQuads();
WindowQuadList ret;
if (!isShaded()) {
ret += makeContentsQuads();
if (!toplevel->frameMargins().isNull()) {
AbstractClient *client = dynamic_cast<AbstractClient*>(toplevel);
......@@ -977,36 +985,66 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg
WindowQuadList Scene::Window::makeContentsQuads() const
const QRegion contentsRegion = clientShape();
if (contentsRegion.isEmpty()) {
return WindowQuadList();
// TODO(vlad): What about the case where we need to build window quads for a deleted
// window? Presumably, the current window will be invalid so no window quads will be
// generated. Is it okay?
const QPointF geometryOffset = bufferOffset();
const qreal textureScale = toplevel->bufferScale();
WindowPixmap *currentPixmap = windowPixmap<WindowPixmap>();
if (!currentPixmap)
return WindowQuadList();
WindowQuadList quads;
int id = 0;
for (const QRectF &rect : contentsRegion) {
WindowQuad quad(WindowQuadContents);
// We need to assign an id to each generated window quad in order to be able to match
// a list of window quads against a particular window pixmap. We traverse the window
// pixmap tree in the depth-first search manner and assign an id to each window quad.
// The id is the time when we visited the window pixmap.
const qreal x0 = rect.left() + geometryOffset.x();
const qreal y0 = + geometryOffset.y();
const qreal x1 = rect.right() + geometryOffset.x();
const qreal y1 = rect.bottom() + geometryOffset.y();
QStack<WindowPixmap *> stack;
const qreal u0 = rect.left() * textureScale;
const qreal v0 = * textureScale;
const qreal u1 = rect.right() * textureScale;
const qreal v1 = rect.bottom() * textureScale;
while (!stack.isEmpty()) {
WindowPixmap *windowPixmap = stack.pop();
quad[0] = WindowVertex(QPointF(x0, y0), QPointF(u0, v0));
quad[1] = WindowVertex(QPointF(x1, y0), QPointF(u1, v0));
quad[2] = WindowVertex(QPointF(x1, y1), QPointF(u1, v1));
quad[3] = WindowVertex(QPointF(x0, y1), QPointF(u0, v1));
// If it's an unmapped sub-surface, don't generate window quads for it.
if (!windowPixmap->isValid())
quads << quad;
const QRegion region = windowPixmap->shape();
const QPoint position = windowPixmap->framePosition();
const qreal scale = windowPixmap->scale();
const int quadId = id++;
for (const QRect &rect : region) {
// Note that the window quad id is not unique if the window is shaped, i.e. the
// region contains more than just one rectangle. We assume that the "source" quad
// had been subdivided.
WindowQuad quad(WindowQuadContents, quadId);
const qreal x0 = rect.x() + position.x();
const qreal y0 = rect.y() + position.y();
const qreal x1 = rect.x() + rect.width() + position.x();
const qreal y1 = rect.y() + rect.height() + position.y();
const qreal u0 = rect.x() * scale;
const qreal v0 = rect.y() * scale;
const qreal u1 = (rect.x() + rect.width()) * scale;
const qreal v1 = (rect.y() + rect.height()) * scale;
quad[0] = WindowVertex(QPointF(x0, y0), QPointF(u0, v0));
quad[1] = WindowVertex(QPointF(x1, y0), QPointF(u1, v0));
quad[2] = WindowVertex(QPointF(x1, y1), QPointF(u1, v1));
quad[3] = WindowVertex(QPointF(x0, y1), QPointF(u0, v1));
quads << quad;
// Push the child window pixmaps onto the stack, remember we're visiting the pixmaps
// in the depth-first search manner.
const auto children = windowPixmap->children();
for (WindowPixmap *child : children)
return quads;
......@@ -1199,6 +1237,39 @@ KWaylandServer::SurfaceInterface *WindowPixmap::surface() const
QPoint WindowPixmap::position() const
if (subSurface())
return subSurface()->position();
return m_window->bufferOffset();
QPoint WindowPixmap::framePosition() const
return position() + (m_parent ? m_parent->framePosition() : QPoint());
qreal WindowPixmap::scale() const
if (surface())
return surface()->scale();
return toplevel()->bufferScale();
QRegion WindowPixmap::shape() const
if (subSurface())
return QRect(QPoint(), surface()->size());
return m_window->clientShape();
bool WindowPixmap::hasAlphaChannel() const
if (buffer())
return buffer()->hasAlphaChannel();
return toplevel()->hasAlpha();
// Scene::EffectFrame
......@@ -329,6 +329,8 @@ public:
bool isVisible() const;
// is the window fully opaque
bool isOpaque() const;
// is the window shaded
bool isShaded() const;
// shape of the window
QRegion bufferShape() const;
QRegion clientShape() const;
......@@ -453,10 +455,38 @@ public:
* @see isDiscarded
void markAsDiscarded();
* Returns the position of the WindowPixmap relative to the upper left corner of the parent.
* This method returns the position of the WindowPixmap relative to the upper left corner
* of the window pixmap if parent() is @c null.
* The upper left corner of the parent window pixmap corresponds to (0, 0).
QPoint position() const;
* Returns the position of the WindowPixmap relative to the upper left corner of the window
* frame. Note that position() returns the position relative to the parent WindowPixmap.
* The upper left corner of the window frame corresponds to (0, 0).
QPoint framePosition() const;
* The size of the pixmap.
const QSize &size() const;
* Returns the device pixel ratio for the attached buffer. This is the ratio between device
* pixels and logical pixels.
qreal scale() const;
* Returns the region that specifies the area inside the attached buffer with the actual
* client's contents.
* The upper left corner of the attached buffer corresponds to (0, 0).
QRegion shape() const;
* The geometry of the Client's content inside the pixmap. In case of a decorated Client the
* pixmap also contains the decoration which is not rendered into this pixmap, though. This
......@@ -468,6 +498,10 @@ public:
* Note: the Toplevel can change over the lifetime of the WindowPixmap in case the Toplevel is copied to Deleted.
Toplevel *toplevel() const;
* Returns @c true if the attached buffer has an alpha channel; otherwise returns @c false.
bool hasAlphaChannel() const;
* @returns the parent WindowPixmap in the sub-surface tree
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment