Render GL Window decorations at the correct scale
Summary: Under wayland we support high DPI putting by putting a separation between the logical co-ordinate system and the resolution of rendered assets. When a window is on a high DPI screen, we should render at the higher resolution. Like the window scaling this handles any combination of a 2x scaled decoration being rendered on a 1x screen or vice versa. --- This patch is a bit different from the other scaling stuff. We have to generate the quads *before* we have an updated texture with the new scale. This means the scale isn't attached to the buffer like elsewhere. That's why I added a property in TopLevel so there's still one canonical source and things can't get out of sync. BUG: 384765 Test Plan: Crystal clear breeze and oxygen decos on my @2x display Drag windows to attached @1x display, things still look OK when across 2 screens Changing the scale of a screen updated the decos instantly Reviewers: #plasma, graesslin Reviewed By: #plasma, graesslin Subscribers: graesslin, plasma-devel, kwin, #kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D8600
This commit is contained in:
parent
449c93362b
commit
fc887ab907
8 changed files with 60 additions and 26 deletions
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "decoratedclient.h"
|
||||
#include "deleted.h"
|
||||
#include "abstract_client.h"
|
||||
#include "screens.h"
|
||||
|
||||
#include <KDecoration2/Decoration>
|
||||
#include <KDecoration2/DecoratedClient>
|
||||
|
@ -38,10 +39,10 @@ Renderer::Renderer(DecoratedClientImpl *client)
|
|||
, m_client(client)
|
||||
, m_imageSizesDirty(true)
|
||||
{
|
||||
auto markImageSizesDirty = [this]{ m_imageSizesDirty = true; };
|
||||
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
||||
connect(client->client(), &AbstractClient::screenChanged, this, markImageSizesDirty);
|
||||
}
|
||||
auto markImageSizesDirty = [this]{
|
||||
m_imageSizesDirty = true;
|
||||
};
|
||||
connect(client->client(), &AbstractClient::screenScaleChanged, this, markImageSizesDirty);
|
||||
connect(client->decoration(), &KDecoration2::Decoration::bordersChanged, this, markImageSizesDirty);
|
||||
connect(client->decoratedClient(), &KDecoration2::DecoratedClient::widthChanged, this, markImageSizesDirty);
|
||||
connect(client->decoratedClient(), &KDecoration2::DecoratedClient::heightChanged, this, markImageSizesDirty);
|
||||
|
@ -65,11 +66,13 @@ QRegion Renderer::getScheduled()
|
|||
QImage Renderer::renderToImage(const QRect &geo)
|
||||
{
|
||||
Q_ASSERT(m_client);
|
||||
QImage image(geo.width(), geo.height(), QImage::Format_ARGB32_Premultiplied);
|
||||
auto dpr = client()->client()->screenScale();
|
||||
QImage image(geo.width() * dpr, geo.height() * dpr, QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(dpr);
|
||||
image.fill(Qt::transparent);
|
||||
QPainter p(&image);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setWindow(geo);
|
||||
p.setWindow(QRect(geo.topLeft(), geo.size() * dpr));
|
||||
p.setClipRect(geo);
|
||||
client()->decoration()->paint(&p, geo);
|
||||
return image;
|
||||
|
|
|
@ -2336,13 +2336,16 @@ SceneOpenGLDecorationRenderer::~SceneOpenGLDecorationRenderer() = default;
|
|||
// and flips it vertically
|
||||
static QImage rotate(const QImage &srcImage, const QRect &srcRect)
|
||||
{
|
||||
QImage image(srcRect.height(), srcRect.width(), srcImage.format());
|
||||
auto dpr = srcImage.devicePixelRatio();
|
||||
QImage image(srcRect.height() * dpr, srcRect.width() * dpr, srcImage.format());
|
||||
image.setDevicePixelRatio(dpr);
|
||||
const QPoint srcPoint(srcRect.x() * dpr, srcRect.y() * dpr);
|
||||
|
||||
const uint32_t *src = reinterpret_cast<const uint32_t *>(srcImage.bits());
|
||||
uint32_t *dst = reinterpret_cast<uint32_t *>(image.bits());
|
||||
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
const uint32_t *s = src + (srcRect.y() + x) * srcImage.width() + srcRect.x();
|
||||
const uint32_t *s = src + (srcPoint.y() + x) * srcImage.width() + srcPoint.x();
|
||||
uint32_t *d = dst + x;
|
||||
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
|
@ -2357,10 +2360,10 @@ static QImage rotate(const QImage &srcImage, const QRect &srcRect)
|
|||
void SceneOpenGLDecorationRenderer::render()
|
||||
{
|
||||
const QRegion scheduled = getScheduled();
|
||||
if (scheduled.isEmpty()) {
|
||||
const bool dirty = areImageSizesDirty();
|
||||
if (scheduled.isEmpty() && !dirty) {
|
||||
return;
|
||||
}
|
||||
const bool dirty = areImageSizesDirty();
|
||||
if (dirty) {
|
||||
resizeTexture();
|
||||
resetImageSizesDirty();
|
||||
|
@ -2385,7 +2388,7 @@ void SceneOpenGLDecorationRenderer::render()
|
|||
// TODO: get this done directly when rendering to the image
|
||||
image = rotate(image, QRect(geo.topLeft() - partRect.topLeft(), geo.size()));
|
||||
}
|
||||
m_texture->update(image, geo.topLeft() - partRect.topLeft() + offset);
|
||||
m_texture->update(image, (geo.topLeft() - partRect.topLeft() + offset) * image.devicePixelRatio());
|
||||
};
|
||||
renderPart(left.intersected(geometry), left, QPoint(0, top.height() + bottom.height() + 2), true);
|
||||
renderPart(top.intersected(geometry), top, QPoint(0, 0));
|
||||
|
@ -2411,6 +2414,7 @@ void SceneOpenGLDecorationRenderer::resizeTexture()
|
|||
|
||||
size.rwidth() = align(size.width(), 128);
|
||||
|
||||
size *= client()->client()->screenScale();
|
||||
if (m_texture && m_texture->size() == size)
|
||||
return;
|
||||
|
||||
|
|
|
@ -632,7 +632,7 @@ void SceneQPainterDecorationRenderer::resizeImages()
|
|||
client()->client()->layoutDecorationRects(left, top, right, bottom);
|
||||
|
||||
auto checkAndCreate = [this](int index, const QSize &size) {
|
||||
auto dpr = screens()->scale(client()->client()->screen());
|
||||
auto dpr = client()->client()->screenScale();
|
||||
if (m_images[index].size() != size * dpr ||
|
||||
m_images[index].devicePixelRatio() != dpr)
|
||||
{
|
||||
|
|
|
@ -1230,6 +1230,7 @@ void SceneXRenderDecorationRenderer::render()
|
|||
return;
|
||||
}
|
||||
QImage image = renderToImage(geo);
|
||||
Q_ASSERT(image.devicePixelRatio() == 1);
|
||||
xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmaps[index], m_gc,
|
||||
image.width(), image.height(), geo.x() - offset.x(), geo.y() - offset.y(), 0, 32,
|
||||
image.byteCount(), image.constBits());
|
||||
|
|
18
scene.cpp
18
scene.cpp
|
@ -403,6 +403,7 @@ void Scene::windowAdded(Toplevel *c)
|
|||
if (c->surface()) {
|
||||
connect(c->surface(), &KWayland::Server::SurfaceInterface::scaleChanged, this, std::bind(&Scene::windowGeometryShapeChanged, this, c));
|
||||
}
|
||||
connect(c, &Toplevel::screenScaleChanged, std::bind(&Scene::windowGeometryShapeChanged, this, c));
|
||||
c->effectWindow()->setSceneWindow(w);
|
||||
c->getShadow();
|
||||
w->updateShadow(c->shadow());
|
||||
|
@ -851,22 +852,23 @@ WindowQuadList Scene::Window::buildQuads(bool force) const
|
|||
QRegion contents = clientShape();
|
||||
QRegion center = toplevel->transparentRect();
|
||||
QRegion decoration = (client ? QRegion(client->decorationRect()) : shape()) - center;
|
||||
qreal decorationScale = 1.0;
|
||||
ret = makeQuads(WindowQuadContents, contents, toplevel->clientContentPos(), scale);
|
||||
|
||||
|
||||
QRect rects[4];
|
||||
bool isShadedClient = false;
|
||||
|
||||
if (client) {
|
||||
client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
|
||||
decorationScale = client->screenScale();
|
||||
isShadedClient = client->isShade() || center.isEmpty();
|
||||
}
|
||||
|
||||
if (isShadedClient) {
|
||||
const QRect bounding = rects[0] | rects[1] | rects[2] | rects[3];
|
||||
ret += makeDecorationQuads(rects, bounding);
|
||||
ret += makeDecorationQuads(rects, bounding, decorationScale);
|
||||
} else {
|
||||
ret += makeDecorationQuads(rects, decoration);
|
||||
ret += makeDecorationQuads(rects, decoration, decorationScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -878,7 +880,7 @@ WindowQuadList Scene::Window::buildQuads(bool force) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion) const
|
||||
WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion, qreal textureScale) const
|
||||
{
|
||||
WindowQuadList list;
|
||||
|
||||
|
@ -908,10 +910,10 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg
|
|||
const int x1 = r.x() + r.width();
|
||||
const int y1 = r.y() + r.height();
|
||||
|
||||
const int u0 = x0 + offsets[i].x();
|
||||
const int v0 = y0 + offsets[i].y();
|
||||
const int u1 = x1 + offsets[i].x();
|
||||
const int v1 = y1 + offsets[i].y();
|
||||
const int u0 = (x0 + offsets[i].x()) * textureScale;
|
||||
const int v0 = (y0 + offsets[i].y()) * textureScale;
|
||||
const int u1 = (x1 + offsets[i].x()) * textureScale;
|
||||
const int v1 = (y1 + offsets[i].y()) * textureScale;
|
||||
|
||||
WindowQuad quad(WindowQuadDecoration);
|
||||
quad.setUVAxisSwapped(swap);
|
||||
|
|
2
scene.h
2
scene.h
|
@ -338,7 +338,7 @@ public:
|
|||
void unreferencePreviousPixmap();
|
||||
protected:
|
||||
WindowQuadList makeQuads(WindowQuadType type, const QRegion& reg, const QPoint &textureOffset = QPoint(0, 0), qreal textureScale = 1.0) const;
|
||||
WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion) const;
|
||||
WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion, qreal textureScale = 1.0) const;
|
||||
/**
|
||||
* @brief Returns the WindowPixmap for this Window.
|
||||
*
|
||||
|
|
20
toplevel.cpp
20
toplevel.cpp
|
@ -281,12 +281,17 @@ void Toplevel::checkScreen()
|
|||
m_screen = 0;
|
||||
emit screenChanged();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
const int s = screens()->number(geometry().center());
|
||||
if (s != m_screen) {
|
||||
m_screen = s;
|
||||
emit screenChanged();
|
||||
}
|
||||
}
|
||||
const int s = screens()->number(geometry().center());
|
||||
if (s != m_screen) {
|
||||
m_screen = s;
|
||||
emit screenChanged();
|
||||
qreal newScale = screens()->scale(m_screen);
|
||||
if (newScale != m_screenScale) {
|
||||
m_screenScale = newScale;
|
||||
emit screenScaleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,6 +313,11 @@ int Toplevel::screen() const
|
|||
return m_screen;
|
||||
}
|
||||
|
||||
qreal Toplevel::screenScale() const
|
||||
{
|
||||
return m_screenScale;
|
||||
}
|
||||
|
||||
bool Toplevel::isOnScreen(int screen) const
|
||||
{
|
||||
return screens()->geometry(screen).intersects(geometry());
|
||||
|
|
14
toplevel.h
14
toplevel.h
|
@ -236,6 +236,12 @@ public:
|
|||
bool isOnScreen(int screen) const; // true if it's at least partially there
|
||||
bool isOnActiveScreen() const;
|
||||
int screen() const; // the screen where the center is
|
||||
/**
|
||||
* The scale of the screen this window is currently on
|
||||
* @Note: The buffer scale can be different.
|
||||
* @since 5.12
|
||||
*/
|
||||
qreal screenScale() const; //
|
||||
virtual QPoint clientPos() const = 0; // inside of geometry()
|
||||
/**
|
||||
* Describes how the client's content maps to the window geometry including the frame.
|
||||
|
@ -490,6 +496,13 @@ Q_SIGNALS:
|
|||
**/
|
||||
void surfaceChanged();
|
||||
|
||||
/*
|
||||
* Emitted when the client's screen changes onto a screen of a different scale
|
||||
* or the screen we're on changes
|
||||
* @since 5.12
|
||||
*/
|
||||
void screenScaleChanged();
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* Checks whether the screen number for this Toplevel changed and updates if needed.
|
||||
|
@ -570,6 +583,7 @@ private:
|
|||
**/
|
||||
QSharedPointer<QOpenGLFramebufferObject> m_internalFBO;
|
||||
// when adding new data members, check also copyToDeleted()
|
||||
qreal m_screenScale = 1.0;
|
||||
};
|
||||
|
||||
inline xcb_window_t Toplevel::window() const
|
||||
|
|
Loading…
Reference in a new issue