scene: Generate quads in item local coordinate space

Window quads need to be in some coordinate space. Since we want items to
be used not only for rendering windows, window-local coordinates do not
suffice.

This change makes scene items generate quads in the item-local coordinate
space.

The model matrix is used to map the quads to the global screen coordinate
system from the item coordinate space.

Since the quads are in the item local coordinate space, the mvp matrix
needs to be updated on every draw call. Given the render data, tracking
the last mvp matrix won't result in less glUniform calls. If this indeed
becomes a serious performance bottleneck, we can explore the possibility
of dumping mvp matrices in a UBO, which have been introduced in OpenGL
3.1.
This commit is contained in:
Vlad Zahorodnii 2021-06-10 13:02:10 +03:00
parent 29cc319455
commit a6268595fb
9 changed files with 63 additions and 115 deletions

View file

@ -1110,10 +1110,10 @@ OpenGLWindow::~OpenGLWindow()
{
}
QMatrix4x4 OpenGLWindow::transformation(int mask, const WindowPaintData &data) const
static QMatrix4x4 transformation(const QPoint &position, int mask, const WindowPaintData &data)
{
QMatrix4x4 matrix;
matrix.translate(x(), y());
matrix.translate(position.x(), position.y());
if (!(mask & Scene::PAINT_WINDOW_TRANSFORMED))
return matrix;
@ -1145,11 +1145,13 @@ bool OpenGLWindow::beginRenderWindow(int mask, const QRegion &region, WindowPain
WindowQuadList quads;
quads.reserve(data.quads.count());
const QRegion filterRegion = region.translated(-x(), -y());
// split all quads in bounding rect with the actual rects in the region
for (const WindowQuad &quad : qAsConst(data.quads)) {
for (const QRect &r : filterRegion) {
const QRectF rf(r);
const Item *item = static_cast<const Item *>(quad.userData());
const QPoint position = item->rootPosition();
for (const QRect &r : region) {
const QRectF rf(r.translated(-position));
const QRectF quadRect(QPointF(quad.left(), quad.top()), QPointF(quad.right(), quad.bottom()));
const QRectF &intersected = rf.intersected(quadRect);
if (intersected.isValid()) {
@ -1243,7 +1245,7 @@ static GLTexture *bindSurfaceTexture(SurfaceItem *surfaceItem)
return platformSurfaceTexture->texture();
}
void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const WindowPaintData &data)
void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, int mask, const WindowPaintData &data)
{
if (auto shadowItem = qobject_cast<ShadowItem *>(item)) {
WindowQuadList quads = context->quads.value(item);
@ -1252,6 +1254,7 @@ void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const Wi
context->renderNodes.append(RenderNode{
.texture = shadow->shadowTexture(),
.quads = quads,
.transformMatrix = transformation(item->rootPosition(), mask, data),
.opacity = data.opacity(),
.hasAlpha = true,
.coordinateType = NormalizedCoordinates,
@ -1264,6 +1267,7 @@ void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const Wi
context->renderNodes.append(RenderNode{
.texture = renderer->texture(),
.quads = quads,
.transformMatrix = transformation(item->rootPosition(), mask, data),
.opacity = data.opacity(),
.hasAlpha = true,
.coordinateType = UnnormalizedCoordinates,
@ -1277,6 +1281,7 @@ void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const Wi
context->renderNodes.append(RenderNode{
.texture = bindSurfaceTexture(surfaceItem),
.quads = quads,
.transformMatrix = transformation(item->rootPosition(), mask, data),
.opacity = data.opacity(),
.hasAlpha = pixmap->hasAlphaChannel(),
.coordinateType = UnnormalizedCoordinates,
@ -1288,7 +1293,7 @@ void OpenGLWindow::createRenderNode(Item *item, RenderContext *context, const Wi
const QList<Item *> childItems = item->childItems();
for (Item *childItem : childItems) {
if (childItem->isVisible()) {
createRenderNode(childItem, context, data);
createRenderNode(childItem, context, mask ,data);
}
}
}
@ -1323,10 +1328,6 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
if (!beginRenderWindow(mask, region, data))
return;
QMatrix4x4 windowMatrix = transformation(mask, data);
const QMatrix4x4 modelViewProjection = modelViewProjectionMatrix(mask, data);
const QMatrix4x4 mvpMatrix = modelViewProjection * windowMatrix;
GLShader *shader = data.shader;
GLenum filter;
@ -1353,8 +1354,6 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
shader = ShaderManager::instance()->pushShader(traits);
}
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix);
shader->setUniform(GLShader::Saturation, data.saturation());
RenderContext renderContext;
@ -1364,7 +1363,7 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
Q_ASSERT(item);
renderContext.quads[item].append(quad);
}
createRenderNode(windowItem(), &renderContext, data);
createRenderNode(windowItem(), &renderContext, mask, data);
const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads();
const GLenum primitiveType = indexedQuads ? GL_QUADS : GL_TRIANGLES;
@ -1397,6 +1396,7 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
float opacity = -1.0;
const QMatrix4x4 modelViewProjection = modelViewProjectionMatrix(mask, data);
for (int i = 0; i < renderContext.renderNodes.count(); i++) {
const RenderNode &renderNode = renderContext.renderNodes[i];
if (renderNode.vertexCount == 0)
@ -1404,6 +1404,8 @@ void OpenGLWindow::performPaint(int mask, const QRegion &region, const WindowPai
setBlendEnabled(renderNode.hasAlpha || renderNode.opacity < 1.0);
shader->setUniform(GLShader::ModelViewProjectionMatrix,
modelViewProjection * renderNode.transformMatrix);
if (opacity != renderNode.opacity) {
shader->setUniform(GLShader::ModulationConstant,
modulate(renderNode.opacity, data.brightness()));

View file

@ -136,6 +136,7 @@ public:
{
GLTexture *texture = nullptr;
WindowQuadList quads;
QMatrix4x4 transformMatrix;
int firstVertex = 0;
int vertexCount = 0;
qreal opacity = 1;
@ -156,11 +157,10 @@ public:
QSharedPointer<GLTexture> windowTexture() override;
private:
QMatrix4x4 transformation(int mask, const WindowPaintData &data) const;
QMatrix4x4 modelViewProjectionMatrix(int mask, const WindowPaintData &data) const;
QVector4D modulate(float opacity, float brightness) const;
void setBlendEnabled(bool enabled);
void createRenderNode(Item *item, RenderContext *context, const WindowPaintData &data);
void createRenderNode(Item *item, RenderContext *context, int mask, const WindowPaintData &data);
bool beginRenderWindow(int mask, const QRegion &region, WindowPaintData &data);
void endRenderWindow();

View file

@ -195,14 +195,13 @@ SceneQPainter::Window::~Window()
void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const WindowPaintData &data)
{
QRegion region = _region;
const QRect boundingRect = windowItem()->mapToGlobal(windowItem()->boundingRect());
if (!(mask & (PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED)))
region &= toplevel->visibleGeometry();
region &= boundingRect;
if (region.isEmpty())
return;
if (!surfaceItem()) {
return;
}
QPainter *scenePainter = m_scene->scenePainter();
QPainter *painter = scenePainter;
@ -210,7 +209,6 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
painter->setClipRegion(region);
painter->setClipping(true);
painter->translate(x(), y());
if (mask & PAINT_WINDOW_TRANSFORMED) {
painter->translate(data.xTranslation(), data.yTranslation());
painter->scale(data.xScale(), data.yScale());
@ -221,11 +219,11 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
QPainter tempPainter;
if (!opaque) {
// need a temp render target which we later on blit to the screen
tempImage = QImage(toplevel->visibleGeometry().size(), QImage::Format_ARGB32_Premultiplied);
tempImage = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
tempImage.fill(Qt::transparent);
tempPainter.begin(&tempImage);
tempPainter.save();
tempPainter.translate(toplevel->frameGeometry().topLeft() - toplevel->visibleGeometry().topLeft());
tempPainter.translate(-boundingRect.topLeft());
painter = &tempPainter;
}
@ -236,10 +234,10 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
tempPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
QColor translucent(Qt::transparent);
translucent.setAlphaF(data.opacity());
tempPainter.fillRect(QRect(QPoint(0, 0), toplevel->visibleGeometry().size()), translucent);
tempPainter.fillRect(QRect(QPoint(0, 0), boundingRect.size()), translucent);
tempPainter.end();
painter = scenePainter;
painter->drawImage(toplevel->visibleGeometry().topLeft() - toplevel->frameGeometry().topLeft(), tempImage);
painter->drawImage(boundingRect.topLeft(), tempImage);
}
painter->restore();
@ -247,6 +245,9 @@ void SceneQPainter::Window::performPaint(int mask, const QRegion &_region, const
void SceneQPainter::Window::renderItem(QPainter *painter, Item *item) const
{
painter->save();
painter->translate(item->position());
if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
renderSurfaceItem(painter, surfaceItem);
} else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) {
@ -259,6 +260,8 @@ void SceneQPainter::Window::renderItem(QPainter *painter, Item *item) const
renderItem(painter, childItem);
}
}
painter->restore();
}
void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *surfaceItem) const
@ -279,14 +282,10 @@ void SceneQPainter::Window::renderSurfaceItem(QPainter *painter, SurfaceItem *su
const QRegion shape = surfaceItem->shape();
for (const QRectF rect : shape) {
const QPointF windowTopLeft = surfaceItem->mapToWindow(rect.topLeft());
const QPointF windowBottomRight = surfaceItem->mapToWindow(rect.bottomRight());
const QPointF bufferTopLeft = surfaceItem->mapToBuffer(rect.topLeft());
const QPointF bufferBottomRight = surfaceItem->mapToBuffer(rect.bottomRight());
painter->drawImage(QRectF(windowTopLeft, windowBottomRight),
platformSurfaceTexture->image(),
painter->drawImage(rect, platformSurfaceTexture->image(),
QRectF(bufferTopLeft, bufferBottomRight));
}
}

View file

@ -176,11 +176,11 @@ bool Shadow::init(const QVector< uint32_t > &data)
m_shadowElements[i] = QPixmap::fromImage(image);
free(reply);
}
m_topOffset = data[ShadowElementsCount];
m_rightOffset = data[ShadowElementsCount+1];
m_bottomOffset = data[ShadowElementsCount+2];
m_leftOffset = data[ShadowElementsCount+3];
updateShadowRegion();
m_offset = QMargins(data[ShadowElementsCount + 3],
data[ShadowElementsCount],
data[ShadowElementsCount + 1],
data[ShadowElementsCount + 2]);
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
@ -205,12 +205,8 @@ bool Shadow::init(KDecoration2::Decoration *decoration)
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_topLevel, &Toplevel::updateShadow);
connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_topLevel, &Toplevel::updateShadow);
const QMargins &p = m_decorationShadow->padding();
m_topOffset = p.top();
m_rightOffset = p.right();
m_bottomOffset = p.bottom();
m_leftOffset = p.left();
updateShadowRegion();
m_offset = m_decorationShadow->padding();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
@ -233,12 +229,8 @@ bool Shadow::init(const QPointer< KWaylandServer::ShadowInterface > &shadow)
m_shadowElements[ShadowElementLeft] = shadow->left() ? QPixmap::fromImage(shadow->left()->data().copy()) : QPixmap();
m_shadowElements[ShadowElementTopLeft] = shadow->topLeft() ? QPixmap::fromImage(shadow->topLeft()->data().copy()) : QPixmap();
const QMarginsF &p = shadow->offset();
m_topOffset = p.top();
m_rightOffset = p.right();
m_bottomOffset = p.bottom();
m_leftOffset = p.left();
updateShadowRegion();
m_offset = shadow->offset().toMargins();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
}
@ -271,14 +263,8 @@ bool Shadow::init(const QWindow *window)
m_shadowElements[ShadowElementBottom] = QPixmap::fromImage(bottomTile);
m_shadowElements[ShadowElementBottomLeft] = QPixmap::fromImage(bottomLeftTile);
const QMargins padding = window->property("kwin_shadow_padding").value<QMargins>();
m_leftOffset = padding.left();
m_topOffset = padding.top();
m_rightOffset = padding.right();
m_bottomOffset = padding.bottom();
updateShadowRegion();
m_offset = window->property("kwin_shadow_padding").value<QMargins>();
Q_EMIT offsetChanged();
if (!prepareBackend()) {
return false;
@ -287,16 +273,6 @@ bool Shadow::init(const QWindow *window)
return true;
}
void Shadow::updateShadowRegion()
{
const QRect top(0, - m_topOffset, m_topLevel->width(), m_topOffset);
const QRect right(m_topLevel->width(), - m_topOffset, m_rightOffset, m_topLevel->height() + m_topOffset + m_bottomOffset);
const QRect bottom(0, m_topLevel->height(), m_topLevel->width(), m_bottomOffset);
const QRect left(- m_leftOffset, - m_topOffset, m_leftOffset, m_topLevel->height() + m_topOffset + m_bottomOffset);
m_shadowRegion = QRegion(top).united(right).united(bottom).united(left);
Q_EMIT regionChanged();
}
bool Shadow::updateShadow()
{
if (!m_topLevel) {
@ -351,7 +327,7 @@ void Shadow::geometryChanged()
return;
}
m_cachedSize = m_topLevel->size();
updateShadowRegion();
Q_EMIT rectChanged();
}
QImage Shadow::decorationShadowImage() const

View file

@ -50,13 +50,6 @@ class KWIN_EXPORT Shadow : public QObject
public:
~Shadow() override;
/**
* @return Region of the shadow.
*/
const QRegion &shadowRegion() const {
return m_shadowRegion;
};
/**
* This method updates the Shadow when the property has been changed.
* It is the responsibility of the owner of the Shadow to call this method
@ -109,21 +102,16 @@ public:
};
QSize elementSize(ShadowElements element) const;
int topOffset() const {
return m_topOffset;
};
int rightOffset() const {
return m_rightOffset;
};
int bottomOffset() const {
return m_bottomOffset;
};
int leftOffset() const {
return m_leftOffset;
};
QRect rect() const {
return QRect(QPoint(0, 0), m_cachedSize);
}
QMargins offset() const {
return m_offset;
}
Q_SIGNALS:
void regionChanged();
void offsetChanged();
void rectChanged();
void textureChanged();
public Q_SLOTS:
@ -136,7 +124,6 @@ protected:
return m_shadowElements[element];
};
void updateShadowRegion();
virtual bool prepareBackend() = 0;
void setShadowElement(const QPixmap &shadow, ShadowElements element);
@ -154,12 +141,8 @@ private:
// shadow pixmaps
QPixmap m_shadowElements[ShadowElementsCount];
// shadow offsets
int m_topOffset;
int m_rightOffset;
int m_bottomOffset;
int m_leftOffset;
QMargins m_offset;
// caches
QRegion m_shadowRegion;
QSize m_cachedSize;
// Decoration based shadows
QSharedPointer<KDecoration2::DecorationShadow> m_decorationShadow;

View file

@ -14,10 +14,11 @@ ShadowItem::ShadowItem(Shadow *shadow, Scene::Window *window, Item *parent)
: Item(window, parent)
, m_shadow(shadow)
{
connect(shadow, &Shadow::regionChanged, this, &ShadowItem::handleRegionChanged);
connect(shadow, &Shadow::offsetChanged, this, &ShadowItem::updateGeometry);
connect(shadow, &Shadow::rectChanged, this, &ShadowItem::updateGeometry);
connect(shadow, &Shadow::textureChanged, this, &ShadowItem::handleTextureChanged);
handleRegionChanged();
updateGeometry();
handleTextureChanged();
}
@ -30,9 +31,9 @@ Shadow *ShadowItem::shadow() const
return m_shadow.data();
}
void ShadowItem::handleRegionChanged()
void ShadowItem::updateGeometry()
{
const QRect rect = shadow()->shadowRegion().boundingRect();
const QRect rect = m_shadow->rect() + m_shadow->offset();
setPosition(rect.topLeft());
setSize(rect.size());
@ -91,9 +92,7 @@ WindowQuadList ShadowItem::buildQuads() const
std::max({topRight.width(), right.width(), bottomRight.width()}),
std::max({bottomRight.height(), bottom.height(), bottomLeft.height()}));
const QRectF outerRect(QPointF(-m_shadow->leftOffset(), -m_shadow->topOffset()),
QPointF(toplevel->width() + m_shadow->rightOffset(),
toplevel->height() + m_shadow->bottomOffset()));
const QRectF outerRect = rect();
const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right();
const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom();

View file

@ -29,7 +29,7 @@ protected:
private Q_SLOTS:
void handleTextureChanged();
void handleRegionChanged();
void updateGeometry();
private:
QScopedPointer<Shadow> m_shadow;

View file

@ -14,11 +14,6 @@ SurfaceItem::SurfaceItem(Scene::Window *window, Item *parent)
{
}
QPointF SurfaceItem::mapToWindow(const QPointF &point) const
{
return rootPosition() + point - window()->pos();
}
QRegion SurfaceItem::shape() const
{
return QRegion();
@ -127,20 +122,15 @@ WindowQuadList SurfaceItem::buildQuads() const
for (const QRectF rect : region) {
WindowQuad quad(const_cast<SurfaceItem *>(this));
const QPointF windowTopLeft = mapToWindow(rect.topLeft());
const QPointF windowTopRight = mapToWindow(rect.topRight());
const QPointF windowBottomRight = mapToWindow(rect.bottomRight());
const QPointF windowBottomLeft = mapToWindow(rect.bottomLeft());
const QPointF bufferTopLeft = mapToBuffer(rect.topLeft());
const QPointF bufferTopRight = mapToBuffer(rect.topRight());
const QPointF bufferBottomRight = mapToBuffer(rect.bottomRight());
const QPointF bufferBottomLeft = mapToBuffer(rect.bottomLeft());
quad[0] = WindowVertex(windowTopLeft, bufferTopLeft);
quad[1] = WindowVertex(windowTopRight, bufferTopRight);
quad[2] = WindowVertex(windowBottomRight, bufferBottomRight);
quad[3] = WindowVertex(windowBottomLeft, bufferBottomLeft);
quad[0] = WindowVertex(rect.topLeft(), bufferTopLeft);
quad[1] = WindowVertex(rect.topRight(), bufferTopRight);
quad[2] = WindowVertex(rect.bottomRight(), bufferBottomRight);
quad[3] = WindowVertex(rect.bottomLeft(), bufferBottomLeft);
quads << quad;
}

View file

@ -21,7 +21,6 @@ class KWIN_EXPORT SurfaceItem : public Item
Q_OBJECT
public:
QPointF mapToWindow(const QPointF &point) const;
virtual QPointF mapToBuffer(const QPointF &point) const = 0;
virtual QRegion shape() const;