diff --git a/src/libkwineffects/kwinglplatform.cpp b/src/libkwineffects/kwinglplatform.cpp index 617fba59d8..01a4c962c4 100644 --- a/src/libkwineffects/kwinglplatform.cpp +++ b/src/libkwineffects/kwinglplatform.cpp @@ -721,6 +721,7 @@ GLPlatform::GLPlatform() m_limitedGLSL(false), m_textureNPOT(false), m_limitedNPOT(false), + m_packInvert(false), m_virtualMachine(false), m_preferBufferSubData(false), m_platformInterface(NoOpenGLPlatformInterface), @@ -801,6 +802,7 @@ void GLPlatform::detect(OpenGLPlatformInterface platformInterface) m_chipset = QByteArrayLiteral("Unknown"); m_preferBufferSubData = false; + m_packInvert = m_extensions.contains("GL_MESA_pack_invert"); // Mesa classic drivers @@ -1200,6 +1202,9 @@ bool GLPlatform::supports(GLFeature feature) const case LimitedNPOT: return m_limitedNPOT; + case PackInvert: + return m_packInvert; + default: return false; } diff --git a/src/libkwineffects/kwinglplatform.h b/src/libkwineffects/kwinglplatform.h index 94129ad42c..f799eea83f 100644 --- a/src/libkwineffects/kwinglplatform.h +++ b/src/libkwineffects/kwinglplatform.h @@ -72,6 +72,11 @@ enum GLFeature { * - GL_CLAMP_TO_BORDER */ LimitedNPOT, + + /** + * Set if the extension GL_MESA_pack_invert is present + */ + PackInvert, }; enum Driver { @@ -444,6 +449,7 @@ private: bool m_limitedGLSL: 1; bool m_textureNPOT: 1; bool m_limitedNPOT: 1; + bool m_packInvert: 1; bool m_virtualMachine: 1; bool m_preferBufferSubData: 1; OpenGLPlatformInterface m_platformInterface; diff --git a/src/plugins/screencast/pipewirestream.cpp b/src/plugins/screencast/pipewirestream.cpp index f014644c20..dc73befd1a 100644 --- a/src/plugins/screencast/pipewirestream.cpp +++ b/src/plugins/screencast/pipewirestream.cpp @@ -327,6 +327,19 @@ static GLTexture *copyTexture(GLTexture *texture) return copy; } +// in-place vertical mirroring +static void mirrorVertically(uchar *data, int height, int stride) +{ + const int halfHeight = height / 2; + std::vector temp(stride); + for (int y = 0; y < halfHeight; ++y) { + auto cur = &data[y * stride], dest = &data[(height - y - 1) * stride]; + memcpy(temp.data(), cur, stride); + memcpy(cur, dest, stride); + memcpy(dest, temp.data(), stride); + } +} + void PipeWireStream::recordFrame(GLTexture *frameTexture, const QRegion &damagedRegion) { Q_ASSERT(!m_stopped); @@ -383,6 +396,12 @@ void PipeWireStream::recordFrame(GLTexture *frameTexture, const QRegion &damaged spa_data->chunk->size = bufferSize; spa_data->chunk->stride = stride; + const bool invertNeededAndSupported = frameTexture->isYInverted() && GLPlatform::instance()->supports(PackInvert); + GLboolean prev; + if (invertNeededAndSupported) { + glGetBooleanv(GL_PACK_INVERT_MESA, &prev); + glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE); + } frameTexture->bind(); if (GLPlatform::instance()->isGLES()) { @@ -392,6 +411,14 @@ void PipeWireStream::recordFrame(GLTexture *frameTexture, const QRegion &damaged } else { glGetTexImage(frameTexture->target(), 0, m_hasAlpha ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, data); } + + if (invertNeededAndSupported) { + if (!prev) { + glPixelStorei(GL_PACK_INVERT_MESA, prev); + } + } else if (frameTexture->isYInverted()) { + mirrorVertically(data, size.height(), stride); + } auto cursor = Cursors::self()->currentCursor(); if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded && m_cursor.viewport.contains(cursor->pos())) { QImage dest(data, size.width(), size.height(), QImage::Format_RGBA8888_Premultiplied);