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: https://phabricator.kde.org/D29131
parent 2d88fd0e
This diff is collapsed.
...@@ -148,26 +148,38 @@ class OpenGLWindowPixmap; ...@@ -148,26 +148,38 @@ class OpenGLWindowPixmap;
class OpenGLWindow final : public Scene::Window class OpenGLWindow final : public Scene::Window
{ {
public: public:
enum Leaf { ShadowLeaf = 0, DecorationLeaf, ContentLeaf, PreviousContentLeaf, LeafCount }; enum Leaf { ShadowLeaf, DecorationLeaf, ContentLeaf, PreviousContentLeaf };
struct LeafNode struct RenderNode
{ {
LeafNode() RenderNode()
: texture(nullptr), : texture(nullptr)
firstVertex(0), , firstVertex(0)
vertexCount(0), , vertexCount(0)
opacity(1.0), , opacity(1.0)
hasAlpha(false), , hasAlpha(false)
coordinateType(UnnormalizedCoordinates) , coordinateType(UnnormalizedCoordinates)
{ {
} }
GLTexture *texture; GLTexture *texture;
WindowQuadList quads;
int firstVertex; int firstVertex;
int vertexCount; int vertexCount;
float opacity; float opacity;
bool hasAlpha; bool hasAlpha;
TextureCoordinateType coordinateType; 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); OpenGLWindow(Toplevel *toplevel, SceneOpenGL *scene);
...@@ -182,9 +194,7 @@ private: ...@@ -182,9 +194,7 @@ private:
QMatrix4x4 modelViewProjectionMatrix(int mask, const WindowPaintData &data) const; QMatrix4x4 modelViewProjectionMatrix(int mask, const WindowPaintData &data) const;
QVector4D modulate(float opacity, float brightness) const; QVector4D modulate(float opacity, float brightness) const;
void setBlendEnabled(bool enabled); void setBlendEnabled(bool enabled);
void setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data); void initializeRenderContext(RenderContext &context, const WindowPaintData &data);
void renderSubSurface(GLShader *shader, const QMatrix4x4 &mvp, const QMatrix4x4 &windowMatrix,
OpenGLWindowPixmap *pixmap, const QRegion &region, bool hardwareClipping);
bool beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data); bool beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data);
void endRenderWindow(); void endRenderWindow();
bool bindTexture(); bool bindTexture();
......
...@@ -787,11 +787,8 @@ QRegion Scene::Window::bufferShape() const ...@@ -787,11 +787,8 @@ QRegion Scene::Window::bufferShape() const
QRegion Scene::Window::clientShape() const QRegion Scene::Window::clientShape() const
{ {
if (AbstractClient *client = qobject_cast<AbstractClient *>(toplevel)) { if (isShaded())
if (client->isShade()) { return QRegion();
return QRegion();
}
}
const QRegion shape = bufferShape(); const QRegion shape = bufferShape();
const QMargins bufferMargins = toplevel->bufferMargins(); const QMargins bufferMargins = toplevel->bufferMargins();
...@@ -833,6 +830,13 @@ bool Scene::Window::isOpaque() const ...@@ -833,6 +830,13 @@ bool Scene::Window::isOpaque() const
return toplevel->opacity() == 1.0 && !toplevel->hasAlpha(); 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 bool Scene::Window::isPaintingEnabled() const
{ {
return !disable_painting; return !disable_painting;
...@@ -877,7 +881,11 @@ WindowQuadList Scene::Window::buildQuads(bool force) const ...@@ -877,7 +881,11 @@ WindowQuadList Scene::Window::buildQuads(bool force) const
if (cached_quad_list != nullptr && !force) if (cached_quad_list != nullptr && !force)
return *cached_quad_list; return *cached_quad_list;
WindowQuadList ret = makeContentsQuads(); WindowQuadList ret;
if (!isShaded()) {
ret += makeContentsQuads();
}
if (!toplevel->frameMargins().isNull()) { if (!toplevel->frameMargins().isNull()) {
AbstractClient *client = dynamic_cast<AbstractClient*>(toplevel); AbstractClient *client = dynamic_cast<AbstractClient*>(toplevel);
...@@ -977,36 +985,66 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg ...@@ -977,36 +985,66 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg
WindowQuadList Scene::Window::makeContentsQuads() const WindowQuadList Scene::Window::makeContentsQuads() const
{ {
const QRegion contentsRegion = clientShape(); // TODO(vlad): What about the case where we need to build window quads for a deleted
if (contentsRegion.isEmpty()) { // window? Presumably, the current window will be invalid so no window quads will be
return WindowQuadList(); // generated. Is it okay?
}
const QPointF geometryOffset = bufferOffset(); WindowPixmap *currentPixmap = windowPixmap<WindowPixmap>();
const qreal textureScale = toplevel->bufferScale(); if (!currentPixmap)
return WindowQuadList();
WindowQuadList quads; WindowQuadList quads;
quads.reserve(contentsRegion.rectCount()); int id = 0;
for (const QRectF &rect : contentsRegion) { // We need to assign an id to each generated window quad in order to be able to match
WindowQuad quad(WindowQuadContents); // 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(); QStack<WindowPixmap *> stack;
const qreal y0 = rect.top() + geometryOffset.y(); stack.push(currentPixmap);
const qreal x1 = rect.right() + geometryOffset.x();
const qreal y1 = rect.bottom() + geometryOffset.y();
const qreal u0 = rect.left() * textureScale; while (!stack.isEmpty()) {
const qreal v0 = rect.top() * textureScale; WindowPixmap *windowPixmap = stack.pop();
const qreal u1 = rect.right() * textureScale;
const qreal v1 = rect.bottom() * textureScale;
quad[0] = WindowVertex(QPointF(x0, y0), QPointF(u0, v0)); // If it's an unmapped sub-surface, don't generate window quads for it.
quad[1] = WindowVertex(QPointF(x1, y0), QPointF(u1, v0)); if (!windowPixmap->isValid())
quad[2] = WindowVertex(QPointF(x1, y1), QPointF(u1, v1)); continue;
quad[3] = WindowVertex(QPointF(x0, y1), QPointF(u0, v1));
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)
stack.push(child);
} }
return quads; return quads;
...@@ -1199,6 +1237,39 @@ KWaylandServer::SurfaceInterface *WindowPixmap::surface() const ...@@ -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 // Scene::EffectFrame
//**************************************** //****************************************
......
...@@ -329,6 +329,8 @@ public: ...@@ -329,6 +329,8 @@ public:
bool isVisible() const; bool isVisible() const;
// is the window fully opaque // is the window fully opaque
bool isOpaque() const; bool isOpaque() const;
// is the window shaded
bool isShaded() const;
// shape of the window // shape of the window
QRegion bufferShape() const; QRegion bufferShape() const;
QRegion clientShape() const; QRegion clientShape() const;
...@@ -453,10 +455,38 @@ public: ...@@ -453,10 +455,38 @@ public:
* @see isDiscarded * @see isDiscarded
*/ */
void markAsDiscarded(); 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. * The size of the pixmap.
*/ */
const QSize &size() const; 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 * 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 * pixmap also contains the decoration which is not rendered into this pixmap, though. This
...@@ -468,6 +498,10 @@ public: ...@@ -468,6 +498,10 @@ public:
* Note: the Toplevel can change over the lifetime of the WindowPixmap in case the Toplevel is copied to Deleted. * Note: the Toplevel can change over the lifetime of the WindowPixmap in case the Toplevel is copied to Deleted.
*/ */
Toplevel *toplevel() const; 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 * @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