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
......@@ -1077,16 +1077,13 @@ OpenGLWindow::~OpenGLWindow()
{
}
static SceneOpenGLTexture *s_frameTexture = nullptr;
// Bind the window pixmap to an OpenGL texture.
bool OpenGLWindow::bindTexture()
{
s_frameTexture = nullptr;
OpenGLWindowPixmap *pixmap = windowPixmap<OpenGLWindowPixmap>();
if (!pixmap) {
return false;
}
s_frameTexture = pixmap->texture();
if (pixmap->isDiscarded()) {
return !pixmap->texture()->isNull();
}
......@@ -1155,7 +1152,7 @@ bool OpenGLWindow::beginRenderWindow(int mask, const QRegion &region, WindowPain
if (data.quads.isEmpty())
return false;
if (!bindTexture() || !s_frameTexture) {
if (!bindTexture()) {
return false;
}
......@@ -1163,20 +1160,6 @@ bool OpenGLWindow::beginRenderWindow(int mask, const QRegion &region, WindowPain
glEnable(GL_SCISSOR_TEST);
}
// Update the texture filter
if (waylandServer()) {
filter = Scene::ImageFilterGood;
s_frameTexture->setFilter(GL_LINEAR);
} else {
if (options->glSmoothScale() != 0 &&
(mask & (Scene::PAINT_WINDOW_TRANSFORMED | Scene::PAINT_SCREEN_TRANSFORMED)))
filter = Scene::ImageFilterGood;
else
filter = Scene::ImageFilterFast;
s_frameTexture->setFilter(filter == Scene::ImageFilterGood ? GL_LINEAR : GL_NEAREST);
}
const GLVertexAttrib attribs[] = {
{ VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) },
{ VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) },
......@@ -1245,40 +1228,151 @@ void OpenGLWindow::setBlendEnabled(bool enabled)
m_blendingEnabled = enabled;
}
void OpenGLWindow::setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data)
/**
* \internal
*
* Counts the total number of pixmaps in the tree with the given root \a windowPixmap.
*/
static int windowPixmapCount(WindowPixmap *windowPixmap)
{
int count = 1; // 1 for the window pixmap itself.
const QVector<WindowPixmap *> children = windowPixmap->children();
for (WindowPixmap *child : children)
count += windowPixmapCount(child);
return count;
}
void OpenGLWindow::initializeRenderContext(RenderContext &context, const WindowPaintData &data)
{
if (!quads[ShadowLeaf].isEmpty()) {
nodes[ShadowLeaf].texture = static_cast<SceneOpenGLShadow *>(m_shadow)->shadowTexture();
nodes[ShadowLeaf].opacity = data.opacity();
nodes[ShadowLeaf].hasAlpha = true;
nodes[ShadowLeaf].coordinateType = NormalizedCoordinates;
WindowPixmap *currentPixmap = windowPixmap<OpenGLWindowPixmap>();
context.shadowOffset = 0;
context.decorationOffset = 1;
context.contentOffset = 2;
context.previousContentOffset = windowPixmapCount(currentPixmap) + 2;
context.quadCount = data.quads.count();
const int nodeCount = context.previousContentOffset + 1;
QVector<RenderNode> &renderNodes = context.renderNodes;
renderNodes.resize(nodeCount);
for (const WindowQuad &quad : data.quads) {
switch (quad.type()) {
case WindowQuadShadow:
renderNodes[context.shadowOffset].quads << quad;
break;
case WindowQuadDecoration:
renderNodes[context.decorationOffset].quads << quad;
break;
case WindowQuadContents:
renderNodes[context.contentOffset + quad.id()].quads << quad;
break;
default:
// Ignore window quad generated by effects.
break;
}
}
if (!quads[DecorationLeaf].isEmpty()) {
nodes[DecorationLeaf].texture = getDecorationTexture();
nodes[DecorationLeaf].opacity = data.opacity();
nodes[DecorationLeaf].hasAlpha = true;
nodes[DecorationLeaf].coordinateType = UnnormalizedCoordinates;
RenderNode &shadowRenderNode = renderNodes[context.shadowOffset];
if (!shadowRenderNode.quads.isEmpty()) {
SceneOpenGLShadow *shadow = static_cast<SceneOpenGLShadow *>(m_shadow);
shadowRenderNode.texture = shadow->shadowTexture();
shadowRenderNode.opacity = data.opacity();
shadowRenderNode.hasAlpha = true;
shadowRenderNode.coordinateType = NormalizedCoordinates;
shadowRenderNode.leafType = ShadowLeaf;
}
nodes[ContentLeaf].texture = s_frameTexture;
nodes[ContentLeaf].hasAlpha = !isOpaque();
// TODO: ARGB crsoofading is atm. a hack, playing on opacities for two dumb SrcOver operations
// Should be a shader
RenderNode &decorationRenderNode = renderNodes[context.decorationOffset];
if (!decorationRenderNode.quads.isEmpty()) {
decorationRenderNode.texture = getDecorationTexture();
decorationRenderNode.opacity = data.opacity();
decorationRenderNode.hasAlpha = true;
decorationRenderNode.coordinateType = UnnormalizedCoordinates;
decorationRenderNode.leafType = DecorationLeaf;
}
// FIXME: Cross-fading must be implemented in a shader.
float contentOpacity = data.opacity();
if (data.crossFadeProgress() != 1.0 && (data.opacity() < 0.95 || toplevel->hasAlpha())) {
const float opacity = 1.0 - data.crossFadeProgress();
nodes[ContentLeaf].opacity = data.opacity() * (1 - pow(opacity, 1.0f + 2.0f * data.opacity()));
} else {
nodes[ContentLeaf].opacity = data.opacity();
contentOpacity *= 1 - pow(opacity, 1.0f + 2.0f * data.opacity());
}
nodes[ContentLeaf].coordinateType = UnnormalizedCoordinates;
// The main surface and all of its sub-surfaces form a tree. In order to initialize
// the render nodes for the window pixmaps we need to traverse the tree in the
// depth-first search manner. The id of content window quads corresponds to the time
// when we visited the corresponding window pixmap. The DFS traversal probably doesn't
// have a significant impact on performance. However, if that's the case, we could
// keep a cache of window pixmaps in the order in which they'll be rendered.
QStack<WindowPixmap *> stack;
stack.push(currentPixmap);
int i = 0;
while (!stack.isEmpty()) {
OpenGLWindowPixmap *windowPixmap = static_cast<OpenGLWindowPixmap *>(stack.pop());
// If it's an unmapped sub-surface, don't render it and all of its children.
if (!windowPixmap->isValid())
continue;
RenderNode &contentRenderNode = renderNodes[context.contentOffset + i++];
contentRenderNode.texture = windowPixmap->texture();
contentRenderNode.hasAlpha = windowPixmap->hasAlphaChannel();
contentRenderNode.opacity = contentOpacity;
contentRenderNode.coordinateType = UnnormalizedCoordinates;
contentRenderNode.leafType = ContentLeaf;
const QVector<WindowPixmap *> children = windowPixmap->children();
for (WindowPixmap *child : children)
stack.push(child);
}
// Note that cross-fading is currently working properly only on X11. In order to make it
// work on Wayland, we have to render the current and the previous window pixmap trees in
// offscreen render targets, then use a cross-fading shader to blend those two layers.
if (data.crossFadeProgress() != 1.0) {
OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>();
nodes[PreviousContentLeaf].texture = previous ? previous->texture() : nullptr;
nodes[PreviousContentLeaf].hasAlpha = !isOpaque();
nodes[PreviousContentLeaf].opacity = data.opacity() * (1.0 - data.crossFadeProgress());
nodes[PreviousContentLeaf].coordinateType = NormalizedCoordinates;
if (previous) { // TODO(vlad): Should cross-fading be disabled on Wayland?
const QRect &oldGeometry = previous->contentsRect();
RenderNode &previousContentRenderNode = renderNodes[context.previousContentOffset];
for (const WindowQuad &quad : qAsConst(renderNodes[context.contentOffset].quads)) {
// We need to create new window quads with normalized texture coordinates.
// Normal quads divide the x/y position by width/height. This would not work
// as the texture is larger than the visible content in case of a decorated
// Client resulting in garbage being shown. So we calculate the normalized
// texture coordinate in the Client's new content space and map it to the
// previous Client's content space.
WindowQuad newQuad(WindowQuadContents);
for (int i = 0; i < 4; ++i) {
const qreal xFactor = (quad[i].textureX() - toplevel->clientPos().x())
/ qreal(toplevel->clientSize().width());
const qreal yFactor = (quad[i].textureY() - toplevel->clientPos().y())
/ qreal(toplevel->clientSize().height());
const qreal u = (xFactor * oldGeometry.width() + oldGeometry.x())
/ qreal(previous->size().width());
const qreal v = (yFactor * oldGeometry.height() + oldGeometry.y())
/ qreal(previous->size().height());
newQuad[i] = WindowVertex(quad[i].x(), quad[i].y(), u, v);
}
previousContentRenderNode.quads.append(newQuad);
}
previousContentRenderNode.texture = previous->texture();
previousContentRenderNode.hasAlpha = previous->hasAlphaChannel();
previousContentRenderNode.opacity = data.opacity() * (1.0 - data.crossFadeProgress());
previousContentRenderNode.coordinateType = NormalizedCoordinates;
previousContentRenderNode.leafType = PreviousContentLeaf;
context.quadCount += previousContentRenderNode.quads.count();
}
}
}
......@@ -1306,35 +1400,6 @@ QMatrix4x4 OpenGLWindow::modelViewProjectionMatrix(int mask, const WindowPaintDa
return scene->projectionMatrix() * mvMatrix;
}
void OpenGLWindow::renderSubSurface(GLShader *shader, const QMatrix4x4 &mvp, const QMatrix4x4 &windowMatrix, OpenGLWindowPixmap *pixmap, const QRegion &region, bool hardwareClipping)
{
QMatrix4x4 newWindowMatrix = windowMatrix;
newWindowMatrix.translate(pixmap->subSurface()->position().x(), pixmap->subSurface()->position().y());
qreal scale = 1.0;
if (pixmap->surface()) {
scale = pixmap->surface()->scale();
}
if (!pixmap->texture()->isNull()) {
setBlendEnabled(pixmap->buffer() && pixmap->buffer()->hasAlphaChannel());
// render this texture
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp * newWindowMatrix);
auto texture = pixmap->texture();
texture->bind();
texture->render(region, QRect(0, 0, texture->width() / scale, texture->height() / scale), hardwareClipping);
texture->unbind();
}
const auto &children = pixmap->children();
for (auto pixmap : children) {
if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) {
continue;
}
renderSubSurface(shader, mvp, newWindowMatrix, static_cast<OpenGLWindowPixmap*>(pixmap), region, hardwareClipping);
}
}
void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPaintData &_data)
{
WindowPaintData data = _data;
......@@ -1345,7 +1410,6 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
const QMatrix4x4 modelViewProjection = modelViewProjectionMatrix(mask, data);
const QMatrix4x4 mvpMatrix = modelViewProjection * windowMatrix;
bool useX11TextureClamp = false;
GLShader *shader = data.shader;
......@@ -1382,76 +1446,30 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
shader->setUniform(GLShader::Saturation, data.saturation());
WindowQuadList quads[LeafCount];
// Split the quads into separate lists for each type
foreach (const WindowQuad &quad, data.quads) {
switch (quad.type()) {
case WindowQuadDecoration:
quads[DecorationLeaf].append(quad);
continue;
case WindowQuadContents:
quads[ContentLeaf].append(quad);
continue;
case WindowQuadShadow:
quads[ShadowLeaf].append(quad);
continue;
default:
continue;
}
}
if (data.crossFadeProgress() != 1.0) {
OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>();
if (previous) {
const QRect &oldGeometry = previous->contentsRect();
for (const WindowQuad &quad : quads[ContentLeaf]) {
// we need to create new window quads with normalize texture coordinates
// normal quads divide the x/y position by width/height. This would not work as the texture
// is larger than the visible content in case of a decorated Client resulting in garbage being shown.
// So we calculate the normalized texture coordinate in the Client's new content space and map it to
// the previous Client's content space.
WindowQuad newQuad(WindowQuadContents);
for (int i = 0; i < 4; ++i) {
const qreal xFactor = qreal(quad[i].textureX() - toplevel->clientPos().x())/qreal(toplevel->clientSize().width());
const qreal yFactor = qreal(quad[i].textureY() - toplevel->clientPos().y())/qreal(toplevel->clientSize().height());
WindowVertex vertex(quad[i].x(), quad[i].y(),
(xFactor * oldGeometry.width() + oldGeometry.x())/qreal(previous->size().width()),
(yFactor * oldGeometry.height() + oldGeometry.y())/qreal(previous->size().height()));
newQuad[i] = vertex;
}
quads[PreviousContentLeaf].append(newQuad);
}
}
}
RenderContext renderContext;
initializeRenderContext(renderContext, data);
const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads();
const GLenum primitiveType = indexedQuads ? GL_QUADS : GL_TRIANGLES;
const int verticesPerQuad = indexedQuads ? 4 : 6;
const size_t size = verticesPerQuad *
(quads[0].count() + quads[1].count() + quads[2].count() + quads[3].count()) * sizeof(GLVertex2D);
const size_t size = verticesPerQuad * renderContext.quadCount * sizeof(GLVertex2D);
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
GLVertex2D *map = (GLVertex2D *) vbo->map(size);
LeafNode nodes[LeafCount];
setupLeafNodes(nodes, quads, data);
for (int i = 0, v = 0; i < LeafCount; i++) {
if (quads[i].isEmpty() || !nodes[i].texture)
for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) {
RenderNode &renderNode = renderContext.renderNodes[i];
if (renderNode.quads.isEmpty() || !renderNode.texture)
continue;
nodes[i].firstVertex = v;
nodes[i].vertexCount = quads[i].count() * verticesPerQuad;
renderNode.firstVertex = v;
renderNode.vertexCount = renderNode.quads.count() * verticesPerQuad;
const QMatrix4x4 matrix = nodes[i].texture->matrix(nodes[i].coordinateType);
const QMatrix4x4 matrix = renderNode.texture->matrix(renderNode.coordinateType);
quads[i].makeInterleavedArrays(primitiveType, &map[v], matrix);
v += quads[i].count() * verticesPerQuad;
renderNode.quads.makeInterleavedArrays(primitiveType, &map[v], matrix);
v += renderNode.quads.count() * verticesPerQuad;
}
vbo->unmap();
......@@ -1462,23 +1480,24 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
float opacity = -1.0;
for (int i = 0; i < LeafCount; i++) {
if (nodes[i].vertexCount == 0)
for (int i = 0; i < renderContext.renderNodes.count(); i++) {
const RenderNode &renderNode = renderContext.renderNodes[i];
if (renderNode.vertexCount == 0)
continue;
setBlendEnabled(nodes[i].hasAlpha || nodes[i].opacity < 1.0);
setBlendEnabled(renderNode.hasAlpha || renderNode.opacity < 1.0);
if (opacity != nodes[i].opacity) {
if (opacity != renderNode.opacity) {
shader->setUniform(GLShader::ModulationConstant,
modulate(nodes[i].opacity, data.brightness()));
opacity = nodes[i].opacity;
modulate(renderNode.opacity, data.brightness()));
opacity = renderNode.opacity;
}
nodes[i].texture->setFilter(filter);
nodes[i].texture->setWrapMode(GL_CLAMP_TO_EDGE);
nodes[i].texture->bind();
renderNode.texture->setFilter(filter);
renderNode.texture->setWrapMode(GL_CLAMP_TO_EDGE);
renderNode.texture->bind();
if (i == ContentLeaf && useX11TextureClamp) {
if (renderNode.leafType == ContentLeaf && useX11TextureClamp) {
// X11 windows are reparented to have their buffer in the middle of a larger texture
// holding the frame window.
// This code passes the texture geometry to the fragment shader
......@@ -1497,23 +1516,12 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
shader->setUniform(GLShader::TextureClamp, QVector4D({0, 0, 1, 1}));
}
vbo->draw(region, primitiveType, nodes[i].firstVertex, nodes[i].vertexCount, m_hardwareClipping);
vbo->draw(region, primitiveType, renderNode.firstVertex,
renderNode.vertexCount, m_hardwareClipping);
}
vbo->unbindArrays();
// render sub-surfaces
auto wp = windowPixmap<OpenGLWindowPixmap>();
const auto &children = wp ? wp->children() : QVector<WindowPixmap*>();
const QPoint mainSurfaceOffset = bufferOffset();
windowMatrix.translate(mainSurfaceOffset.x(), mainSurfaceOffset.y());
for (auto pixmap : children) {
if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) {
continue;
}
renderSubSurface(shader, modelViewProjection, windowMatrix, static_cast<OpenGLWindowPixmap*>(pixmap), region, m_hardwareClipping);
}
setBlendEnabled(false);
if (!data.shader)
......
......@@ -148,26 +148,38 @@ class OpenGLWindowPixmap;
class OpenGLWindow final : public Scene::Window
{
public:
enum Leaf { ShadowLeaf = 0, DecorationLeaf, ContentLeaf, PreviousContentLeaf, LeafCount };
enum Leaf { ShadowLeaf, DecorationLeaf, ContentLeaf, PreviousContentLeaf };
struct LeafNode
struct RenderNode
{
LeafNode()
: texture(nullptr),
firstVertex(0),
vertexCount(0),
opacity(1.0),
hasAlpha(false),
coordinateType(UnnormalizedCoordinates)
RenderNode()
: 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;
quads.reserve(contentsRegion.rectCount());
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 = rect.top() + geometryOffset.y();
const qreal x1 = rect.right() + geometryOffset.x();
const qreal y1 = rect.bottom() + geometryOffset.y();
QStack<WindowPixmap *> stack;
stack.push(currentPixmap);
const qreal u0 = rect.left() * textureScale;
const qreal v0 = rect.top() * 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())
continue;
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;
......@@ -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() + (