Package: kwin-wayland
Version: 4:5.27.2-2
Severity: important
Tags: patch
Dear Maintainer,
current version of KWin in bookworm has a bug that causes colors (red
and blue) to be inverted when sharing screen (e.g. in the browser) on
certain configurations using Wayland, as reported here:
https://bugs.kde.org/show_bug.cgi?id=466655
There is already a patch for that issue upstream (included in 5.27.3,
also attached):
https://invent.kde.org/plasma/kwin/commit/85b614e75c41cbdeb63b276162ed9777232daf28
It would be great if this problem could be fixed in Debian.
-- System Information:
Debian Release: 12.0
APT prefers testing-security
APT policy: (500, 'testing-security'), (500, 'stable-security'),
(500, 'testing')
Architecture: amd64 (x86_64)
Kernel: Linux 6.1.0-9-amd64 (SMP w/8 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN
Locale: LANG=pl_PL.UTF-8, LC_CTYPE=pl_PL.UTF-8 (charmap=UTF-8),
LANGUAGE=en_US
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages kwin-wayland depends on:
ii kwayland-integration 5.27.2-1
ii kwin-common 4:5.27.2-2
ii libc6 2.36-9
ii libcap2-bin 1:2.66-3
ii libepoxy0 1.5.10-1
ii libfontconfig1 2.14.1-4
ii libfreetype6 2.12.1+dfsg-5
ii libkdecorations2-5v5 4:5.27.2-1
ii libkf5configcore5 5.103.0-1
ii libkf5configgui5 5.103.0-1
ii libkf5configwidgets5 5.103.0-1
ii libkf5coreaddons5 5.103.0-1
ii libkf5crash5 5.103.0-1
ii libkf5dbusaddons5 5.103.0-1
ii libkf5globalaccel-bin 5.103.0-1
ii libkf5globalaccel5 5.103.0-1
ii libkf5globalaccelprivate5 5.103.0-1
ii libkf5i18n5 5.103.0-1
ii libkf5idletime5 5.103.0-2
ii libkf5notifications5 5.103.0-1
ii libkf5plasma5 5.103.0-1
ii libkf5service-bin 5.103.0-1
ii libkf5service5 5.103.0-1
ii libkf5windowsystem5 5.103.0-1
ii libkwineffects14 4:5.27.2-2
ii libkwinglutils14 4:5.27.2-2
ii libpipewire-0.3-0 0.3.65-3
ii libqaccessibilityclient-qt5-0 0.4.1-1+b1
ii libqt5core5a [qtbase-abi-5-15-8] 5.15.8+dfsg-7
ii libqt5dbus5 5.15.8+dfsg-7
ii libqt5gui5 5.15.8+dfsg-7
ii libqt5network5 5.15.8+dfsg-7
ii libqt5qml5 5.15.8+dfsg-3
ii libqt5quick5 5.15.8+dfsg-3
ii libqt5widgets5 5.15.8+dfsg-7
ii libstdc++6 12.2.0-14
ii libxcb-randr0 1.15-1
ii libxcb-xfixes0 1.15-1
ii libxcb1 1.15-1
ii xwayland 2:22.1.9-1
kwin-wayland recommends no packages.
kwin-wayland suggests no packages.
-- no debconf information
>From 85b614e75c41cbdeb63b276162ed9777232daf28 Mon Sep 17 00:00:00 2001
From: Aleix Pol <aleix...@kde.org>
Date: Fri, 3 Mar 2023 19:34:17 +0100
Subject: [PATCH] screencasting: on memfd, skip the QImage step
We were using QImage as an intermediary step. GL -> QImage -> spa
buffer. While it abstracted things out neatly and eventually helped with
debugging, it was unnecessary and woudl present some handicaps, such as
the lack of a QImage::Format_BGRA.
So we just it out to download right into the buffer.
BUG: 466655
(cherry picked from commit 121454580711c409b612d06865ab9d221dcbac6b)
---
.../screencast/outputscreencastsource.cpp | 4 +--
.../screencast/outputscreencastsource.h | 2 +-
.../screencast/regionscreencastsource.cpp | 4 +--
.../screencast/regionscreencastsource.h | 2 +-
src/plugins/screencast/screencastsource.h | 4 ++-
src/plugins/screencast/screencaststream.cpp | 10 +++---
src/plugins/screencast/screencastutils.h | 33 ++++++++++---------
.../screencast/windowscreencastsource.cpp | 4 +--
.../screencast/windowscreencastsource.h | 2 +-
9 files changed, 35 insertions(+), 30 deletions(-)
diff --git a/src/plugins/screencast/outputscreencastsource.cpp
b/src/plugins/screencast/outputscreencastsource.cpp
index 0c0706a2a0d..ac853171da9 100644
--- a/src/plugins/screencast/outputscreencastsource.cpp
+++ b/src/plugins/screencast/outputscreencastsource.cpp
@@ -44,11 +44,11 @@ QSize OutputScreenCastSource::textureSize() const
return m_output->pixelSize();
}
-void OutputScreenCastSource::render(QImage *image)
+void OutputScreenCastSource::render(spa_data *spa, spa_video_format format)
{
const std::shared_ptr<GLTexture> outputTexture =
Compositor::self()->scene()->textureForOutput(m_output);
if (outputTexture) {
- grabTexture(outputTexture.get(), image);
+ grabTexture(outputTexture.get(), spa, format);
}
}
diff --git a/src/plugins/screencast/outputscreencastsource.h
b/src/plugins/screencast/outputscreencastsource.h
index 81bf511b18a..1e12eba2005 100644
--- a/src/plugins/screencast/outputscreencastsource.h
+++ b/src/plugins/screencast/outputscreencastsource.h
@@ -27,7 +27,7 @@ public:
quint32 drmFormat() const override;
void render(GLFramebuffer *target) override;
- void render(QImage *image) override;
+ void render(spa_data *spa, spa_video_format format) override;
std::chrono::nanoseconds clock() const override;
private:
diff --git a/src/plugins/screencast/regionscreencastsource.cpp
b/src/plugins/screencast/regionscreencastsource.cpp
index daeb69d7cbf..8b747f57a62 100644
--- a/src/plugins/screencast/regionscreencastsource.cpp
+++ b/src/plugins/screencast/regionscreencastsource.cpp
@@ -110,10 +110,10 @@ void RegionScreenCastSource::render(GLFramebuffer *target)
GLFramebuffer::popFramebuffer();
}
-void RegionScreenCastSource::render(QImage *image)
+void RegionScreenCastSource::render(spa_data *spa, spa_video_format format)
{
ensureTexture();
- grabTexture(m_renderedTexture.get(), image);
+ grabTexture(m_renderedTexture.get(), spa, format);
}
}
diff --git a/src/plugins/screencast/regionscreencastsource.h
b/src/plugins/screencast/regionscreencastsource.h
index 09ea033248b..560c5d64dc1 100644
--- a/src/plugins/screencast/regionscreencastsource.h
+++ b/src/plugins/screencast/regionscreencastsource.h
@@ -28,7 +28,7 @@ public:
QSize textureSize() const override;
void render(GLFramebuffer *target) override;
- void render(QImage *image) override;
+ void render(spa_data *spa, spa_video_format format) override;
std::chrono::nanoseconds clock() const override;
QRect region() const
diff --git a/src/plugins/screencast/screencastsource.h
b/src/plugins/screencast/screencastsource.h
index 81d78762c99..6dcafc84c2e 100644
--- a/src/plugins/screencast/screencastsource.h
+++ b/src/plugins/screencast/screencastsource.h
@@ -7,6 +7,8 @@
#pragma once
#include <QObject>
+#include <spa/buffer/buffer.h>
+#include <spa/param/video/raw.h>
namespace KWin
{
@@ -25,7 +27,7 @@ public:
virtual QSize textureSize() const = 0;
virtual void render(GLFramebuffer *target) = 0;
- virtual void render(QImage *image) = 0;
+ virtual void render(spa_data *spa, spa_video_format format) = 0;
virtual std::chrono::nanoseconds clock() const = 0;
Q_SIGNALS:
diff --git a/src/plugins/screencast/screencaststream.cpp
b/src/plugins/screencast/screencaststream.cpp
index fad5114c061..b79c422bd68 100644
--- a/src/plugins/screencast/screencaststream.cpp
+++ b/src/plugins/screencast/screencaststream.cpp
@@ -450,20 +450,20 @@ void ScreenCastStream::recordFrame(const QRegion
&_damagedRegion)
const int bpp = data && !hasAlpha ? 3 : 4;
const uint stride = SPA_ROUND_UP_N(size.width() * bpp, 4);
- QImage dest(data, size.width(), size.height(), stride, hasAlpha ?
QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
- if (dest.sizeInBytes() > spa_data->maxsize) {
+ if ((stride * size.height()) > spa_data->maxsize) {
qCDebug(KWIN_SCREENCAST) << "Failed to record frame: frame is too
big";
pw_stream_queue_buffer(pwStream, buffer);
return;
}
- spa_data->chunk->size = dest.sizeInBytes();
- spa_data->chunk->stride = dest.bytesPerLine();
+ spa_data->chunk->stride = stride;
+ spa_data->chunk->size = stride * size.height();
- m_source->render(&dest);
+ m_source->render(spa_data, videoFormat.format);
auto cursor = Cursors::self()->currentCursor();
if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded
&& exclusiveContains(m_cursor.viewport, cursor->pos())) {
+ QImage dest(data, size.width(), size.height(), stride, hasAlpha ?
QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
QPainter painter(&dest);
const auto position = (cursor->pos() - m_cursor.viewport.topLeft()
- cursor->hotspot()) * m_cursor.scale;
painter.drawImage(QRect{position.toPoint(),
cursor->image().size()}, cursor->image());
diff --git a/src/plugins/screencast/screencastutils.h
b/src/plugins/screencast/screencastutils.h
index 55efb604fc4..f20ec322c86 100644
--- a/src/plugins/screencast/screencastutils.h
+++ b/src/plugins/screencast/screencastutils.h
@@ -8,6 +8,8 @@
#include "kwinglplatform.h"
#include "kwingltexture.h"
+#include <spa/buffer/buffer.h>
+#include <spa/param/video/raw.h>
namespace KWin
{
@@ -25,28 +27,29 @@ static void mirrorVertically(uchar *data, int height, int
stride)
}
}
-static GLenum closestGLType(const QImage &image)
+static GLenum closestGLType(spa_video_format format)
{
- switch (image.format()) {
- case QImage::Format_RGB888:
+ switch (format) {
+ case SPA_VIDEO_FORMAT_RGB:
return GL_RGB;
- case QImage::Format_BGR888:
+ case SPA_VIDEO_FORMAT_BGR:
return GL_BGR;
- case QImage::Format_RGB32:
- case QImage::Format_RGBX8888:
- case QImage::Format_RGBA8888:
- case QImage::Format_RGBA8888_Premultiplied:
+ case SPA_VIDEO_FORMAT_RGBx:
+ case SPA_VIDEO_FORMAT_RGBA:
return GL_RGBA;
+ case SPA_VIDEO_FORMAT_BGRA:
+ case SPA_VIDEO_FORMAT_BGRx:
+ return GL_BGRA;
default:
- qDebug() << "unknown format" << image.format();
+ qDebug() << "unknown format" << format;
return GL_RGBA;
}
}
-static void grabTexture(GLTexture *texture, QImage *image)
+static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format
format)
{
const bool invert = !texture->isYInverted();
- Q_ASSERT(texture->size() == image->size());
+ const QSize size = texture->size();
bool isGLES = GLPlatform::instance()->isGLES();
bool invertNeeded = isGLES ^ invert;
const bool invertNeededAndSupported = invertNeeded &&
GLPlatform::instance()->supports(PackInvert);
@@ -58,11 +61,11 @@ static void grabTexture(GLTexture *texture, QImage *image)
texture->bind();
if (GLPlatform::instance()->isGLES()) {
- glReadPixels(0, 0, image->width(), image->height(),
closestGLType(*image), GL_UNSIGNED_BYTE, (GLvoid *)image->bits());
+ glReadPixels(0, 0, size.width(), size.height(), closestGLType(format),
GL_UNSIGNED_BYTE, spa->data);
} else if (GLPlatform::instance()->glVersion() >= kVersionNumber(4, 5)) {
- glGetTextureImage(texture->texture(), 0, closestGLType(*image),
GL_UNSIGNED_BYTE, image->sizeInBytes(), image->bits());
+ glGetTextureImage(texture->texture(), 0, closestGLType(format),
GL_UNSIGNED_BYTE, spa->chunk->size, spa->data);
} else {
- glGetTexImage(texture->target(), 0, closestGLType(*image),
GL_UNSIGNED_BYTE, image->bits());
+ glGetTexImage(texture->target(), 0, closestGLType(format),
GL_UNSIGNED_BYTE, spa->data);
}
if (invertNeededAndSupported) {
@@ -70,7 +73,7 @@ static void grabTexture(GLTexture *texture, QImage *image)
glPixelStorei(GL_PACK_INVERT_MESA, prev);
}
} else if (invertNeeded) {
- mirrorVertically(image->bits(), image->height(),
image->bytesPerLine());
+ mirrorVertically(static_cast<uchar *>(spa->data), size.height(),
spa->chunk->stride);
}
}
diff --git a/src/plugins/screencast/windowscreencastsource.cpp
b/src/plugins/screencast/windowscreencastsource.cpp
index 3c247a3c923..fe651ce8544 100644
--- a/src/plugins/screencast/windowscreencastsource.cpp
+++ b/src/plugins/screencast/windowscreencastsource.cpp
@@ -46,13 +46,13 @@ QSize WindowScreenCastSource::textureSize() const
return m_window->clientGeometry().size().toSize();
}
-void WindowScreenCastSource::render(QImage *image)
+void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
{
GLTexture offscreenTexture(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8,
textureSize());
GLFramebuffer offscreenTarget(&offscreenTexture);
render(&offscreenTarget);
- grabTexture(&offscreenTexture, image);
+ grabTexture(&offscreenTexture, spa, format);
}
void WindowScreenCastSource::render(GLFramebuffer *target)
diff --git a/src/plugins/screencast/windowscreencastsource.h
b/src/plugins/screencast/windowscreencastsource.h
index 69cd989d534..4efb7850a3e 100644
--- a/src/plugins/screencast/windowscreencastsource.h
+++ b/src/plugins/screencast/windowscreencastsource.h
@@ -27,7 +27,7 @@ public:
QSize textureSize() const override;
void render(GLFramebuffer *target) override;
- void render(QImage *image) override;
+ void render(spa_data *spa, spa_video_format format) override;
std::chrono::nanoseconds clock() const override;
private:
--
GitLab