From 150b098ba73d455eed7eeb26689ecd025f567b0c Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 14 Feb 2023 16:05:00 +0100 Subject: [PATCH] screencasting: Minimise pixel format conversions while streaming Instead of best-guessing, at BGR (which in retrospect was a bad guess), offer whatever resembles most the internal representation. This way the frame gets to be least treated as it goes into the client. --- src/backends/drm/drm_egl_cursor_layer.cpp | 5 ++ src/backends/drm/drm_egl_cursor_layer.h | 1 + src/backends/drm/drm_layer.cpp | 6 ++ src/backends/drm/drm_layer.h | 1 + src/backends/drm/drm_qpainter_layer.cpp | 5 ++ src/backends/drm/drm_qpainter_layer.h | 1 + src/backends/drm/drm_virtual_egl_layer.cpp | 5 ++ src/backends/drm/drm_virtual_egl_layer.h | 1 + src/backends/virtual/virtual_egl_backend.cpp | 7 ++ src/backends/virtual/virtual_egl_backend.h | 1 + .../virtual/virtual_qpainter_backend.cpp | 6 ++ .../virtual/virtual_qpainter_backend.h | 1 + src/backends/wayland/wayland_egl_backend.cpp | 15 ++++ src/backends/wayland/wayland_egl_backend.h | 3 + .../wayland/wayland_qpainter_backend.cpp | 11 +++ .../wayland/wayland_qpainter_backend.h | 2 + .../standalone/x11_standalone_egl_backend.cpp | 7 +- .../standalone/x11_standalone_egl_backend.h | 1 + .../standalone/x11_standalone_glx_backend.cpp | 6 ++ .../standalone/x11_standalone_glx_backend.h | 1 + .../x11/windowed/x11_windowed_egl_backend.cpp | 11 +++ .../x11/windowed/x11_windowed_egl_backend.h | 2 + .../x11_windowed_qpainter_backend.cpp | 17 +++++ .../windowed/x11_windowed_qpainter_backend.h | 2 + src/composite.cpp | 6 ++ src/composite.h | 7 ++ src/core/outputlayer.h | 5 ++ .../screencast/outputscreencastsource.cpp | 5 ++ .../screencast/outputscreencastsource.h | 1 + .../screencast/regionscreencastsource.cpp | 8 ++- .../screencast/regionscreencastsource.h | 1 + src/plugins/screencast/screencastsource.h | 1 + src/plugins/screencast/screencaststream.cpp | 71 ++++++++++++------- src/plugins/screencast/screencaststream.h | 1 + src/plugins/screencast/screencastutils.h | 24 ++++++- .../screencast/windowscreencastsource.cpp | 6 ++ .../screencast/windowscreencastsource.h | 1 + 37 files changed, 226 insertions(+), 29 deletions(-) diff --git a/src/backends/drm/drm_egl_cursor_layer.cpp b/src/backends/drm/drm_egl_cursor_layer.cpp index 4a9711ba59..127c91531b 100644 --- a/src/backends/drm/drm_egl_cursor_layer.cpp +++ b/src/backends/drm/drm_egl_cursor_layer.cpp @@ -7,6 +7,7 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "drm_egl_cursor_layer.h" +#include "drm_buffer.h" #include "drm_egl_backend.h" #include "drm_gpu.h" #include "drm_pipeline.h" @@ -57,4 +58,8 @@ void EglGbmCursorLayer::releaseBuffers() m_surface.destroyResources(); } +quint32 EglGbmCursorLayer::format() const +{ + return m_surface.currentBuffer()->buffer()->format(); +} } diff --git a/src/backends/drm/drm_egl_cursor_layer.h b/src/backends/drm/drm_egl_cursor_layer.h index d971017600..800337f9a1 100644 --- a/src/backends/drm/drm_egl_cursor_layer.h +++ b/src/backends/drm/drm_egl_cursor_layer.h @@ -36,6 +36,7 @@ public: QRegion currentDamage() const override; bool checkTestBuffer() override; void releaseBuffers() override; + quint32 format() const override; private: EglGbmLayerSurface m_surface; diff --git a/src/backends/drm/drm_layer.cpp b/src/backends/drm/drm_layer.cpp index 55b9494f74..d847808fcd 100644 --- a/src/backends/drm/drm_layer.cpp +++ b/src/backends/drm/drm_layer.cpp @@ -10,6 +10,7 @@ #include "drm_pipeline.h" #include +#include namespace KWin { @@ -21,6 +22,11 @@ QRegion DrmOutputLayer::currentDamage() const return {}; } +quint32 DrmOutputLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + std::shared_ptr DrmOutputLayer::texture() const { return nullptr; diff --git a/src/backends/drm/drm_layer.h b/src/backends/drm/drm_layer.h index 562ed86c45..abcd922823 100644 --- a/src/backends/drm/drm_layer.h +++ b/src/backends/drm/drm_layer.h @@ -29,6 +29,7 @@ public: virtual std::shared_ptr texture() const; virtual QRegion currentDamage() const; virtual void releaseBuffers() = 0; + quint32 format() const override; }; class DrmPipelineLayer : public DrmOutputLayer diff --git a/src/backends/drm/drm_qpainter_layer.cpp b/src/backends/drm/drm_qpainter_layer.cpp index 003fa5d4b2..64f17577a2 100644 --- a/src/backends/drm/drm_qpainter_layer.cpp +++ b/src/backends/drm/drm_qpainter_layer.cpp @@ -92,6 +92,11 @@ void DrmQPainterLayer::releaseBuffers() m_swapchain.reset(); } +quint32 DrmQPainterLayer::format() const +{ + return DRM_FORMAT_XRGB8888; +} + DrmCursorQPainterLayer::DrmCursorQPainterLayer(DrmPipeline *pipeline) : DrmOverlayLayer(pipeline) { diff --git a/src/backends/drm/drm_qpainter_layer.h b/src/backends/drm/drm_qpainter_layer.h index 01b183b81e..dd5cc30cf4 100644 --- a/src/backends/drm/drm_qpainter_layer.h +++ b/src/backends/drm/drm_qpainter_layer.h @@ -32,6 +32,7 @@ public: std::shared_ptr currentBuffer() const override; QRegion currentDamage() const override; void releaseBuffers() override; + quint32 format() const override; private: bool doesSwapchainFit() const; diff --git a/src/backends/drm/drm_virtual_egl_layer.cpp b/src/backends/drm/drm_virtual_egl_layer.cpp index 91858845fd..09bc53c1ce 100644 --- a/src/backends/drm/drm_virtual_egl_layer.cpp +++ b/src/backends/drm/drm_virtual_egl_layer.cpp @@ -175,4 +175,9 @@ void VirtualEglGbmLayer::releaseBuffers() m_gbmSurface.reset(); m_oldGbmSurface.reset(); } + +quint32 VirtualEglGbmLayer::format() const +{ + return m_gbmSurface->format(); +} } diff --git a/src/backends/drm/drm_virtual_egl_layer.h b/src/backends/drm/drm_virtual_egl_layer.h index 46efd89c3a..b804210e2e 100644 --- a/src/backends/drm/drm_virtual_egl_layer.h +++ b/src/backends/drm/drm_virtual_egl_layer.h @@ -42,6 +42,7 @@ public: QRegion currentDamage() const override; std::shared_ptr texture() const override; void releaseBuffers() override; + quint32 format() const override; private: bool createGbmSurface(); diff --git a/src/backends/virtual/virtual_egl_backend.cpp b/src/backends/virtual/virtual_egl_backend.cpp index f4e1f0f97f..d43b6221fd 100644 --- a/src/backends/virtual/virtual_egl_backend.cpp +++ b/src/backends/virtual/virtual_egl_backend.cpp @@ -15,6 +15,7 @@ #include "virtual_backend.h" #include "virtual_output.h" // kwin libs +#include #include #ifndef EGL_PLATFORM_SURFACELESS_MESA @@ -61,6 +62,12 @@ bool VirtualEglLayer::endFrame(const QRegion &renderedRegion, const QRegion &dam return true; } +quint32 VirtualEglLayer::format() const +{ + // the texture format is hardcoded in VirtualEglLayer::beginFrame + return DRM_FORMAT_RGB888; +} + VirtualEglBackend::VirtualEglBackend(VirtualBackend *b) : AbstractEglBackend() , m_backend(b) diff --git a/src/backends/virtual/virtual_egl_backend.h b/src/backends/virtual/virtual_egl_backend.h index d084a5f418..20fafbb9c3 100644 --- a/src/backends/virtual/virtual_egl_backend.h +++ b/src/backends/virtual/virtual_egl_backend.h @@ -29,6 +29,7 @@ public: bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; std::shared_ptr texture() const; + quint32 format() const override; private: VirtualEglBackend *const m_backend; diff --git a/src/backends/virtual/virtual_qpainter_backend.cpp b/src/backends/virtual/virtual_qpainter_backend.cpp index b26d37f38d..f9fe1bd779 100644 --- a/src/backends/virtual/virtual_qpainter_backend.cpp +++ b/src/backends/virtual/virtual_qpainter_backend.cpp @@ -12,6 +12,7 @@ #include "virtual_output.h" #include +#include namespace KWin { @@ -41,6 +42,11 @@ QImage *VirtualQPainterLayer::image() return &m_image; } +quint32 VirtualQPainterLayer::format() const +{ + return DRM_FORMAT_RGBX8888; +} + VirtualQPainterBackend::VirtualQPainterBackend(VirtualBackend *backend) { connect(backend, &VirtualBackend::outputAdded, this, &VirtualQPainterBackend::addOutput); diff --git a/src/backends/virtual/virtual_qpainter_backend.h b/src/backends/virtual/virtual_qpainter_backend.h index 51d5d443d6..d7d5727e35 100644 --- a/src/backends/virtual/virtual_qpainter_backend.h +++ b/src/backends/virtual/virtual_qpainter_backend.h @@ -29,6 +29,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; QImage *image(); + quint32 format() const override; private: Output *const m_output; diff --git a/src/backends/wayland/wayland_egl_backend.cpp b/src/backends/wayland/wayland_egl_backend.cpp index 3f3ba83c25..e567892d0c 100644 --- a/src/backends/wayland/wayland_egl_backend.cpp +++ b/src/backends/wayland/wayland_egl_backend.cpp @@ -125,6 +125,11 @@ int WaylandEglLayerBuffer::age() const return m_age; } +gbm_bo *WaylandEglLayerBuffer::bo() const +{ + return m_bo; +} + WaylandEglLayerSwapchain::WaylandEglLayerSwapchain(const QSize &size, uint32_t format, const QVector &modifiers, WaylandEglBackend *backend) : m_backend(backend) , m_size(size) @@ -284,6 +289,16 @@ bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegio return true; } +quint32 WaylandEglCursorLayer::format() const +{ + return gbm_bo_get_format(m_buffer->bo()); +} + +quint32 WaylandEglPrimaryLayer::format() const +{ + return gbm_bo_get_format(m_buffer->bo()); +} + WaylandEglBackend::WaylandEglBackend(WaylandBackend *b) : AbstractEglBackend() , m_backend(b) diff --git a/src/backends/wayland/wayland_egl_backend.h b/src/backends/wayland/wayland_egl_backend.h index 784097c67c..155d307ccd 100644 --- a/src/backends/wayland/wayland_egl_backend.h +++ b/src/backends/wayland/wayland_egl_backend.h @@ -42,6 +42,7 @@ public: GLFramebuffer *framebuffer() const; std::shared_ptr texture() const; int age() const; + gbm_bo *bo() const; private: WaylandEglBackend *m_backend; @@ -83,6 +84,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; private: WaylandOutput *m_waylandOutput; @@ -104,6 +106,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; private: WaylandOutput *m_output; diff --git a/src/backends/wayland/wayland_qpainter_backend.cpp b/src/backends/wayland/wayland_qpainter_backend.cpp index 11c59b41cd..a3b3756577 100644 --- a/src/backends/wayland/wayland_qpainter_backend.cpp +++ b/src/backends/wayland/wayland_qpainter_backend.cpp @@ -18,6 +18,7 @@ #include #include +#include namespace KWin { @@ -129,6 +130,11 @@ bool WaylandQPainterPrimaryLayer::endFrame(const QRegion &renderedRegion, const return true; } +quint32 WaylandQPainterPrimaryLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + WaylandQPainterCursorLayer::WaylandQPainterCursorLayer(WaylandOutput *output) : m_output(output) { @@ -159,6 +165,11 @@ bool WaylandQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const Q return true; } +quint32 WaylandQPainterCursorLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b) : QPainterBackend() , m_backend(b) diff --git a/src/backends/wayland/wayland_qpainter_backend.h b/src/backends/wayland/wayland_qpainter_backend.h index 74ed832148..56b2b930e8 100644 --- a/src/backends/wayland/wayland_qpainter_backend.h +++ b/src/backends/wayland/wayland_qpainter_backend.h @@ -56,6 +56,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; void remapBuffer(); @@ -88,6 +89,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; private: WaylandOutput *m_output; diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp index e5a069d6f1..15e2097029 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp @@ -23,6 +23,7 @@ #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #endif +#include namespace KWin { @@ -43,6 +44,11 @@ bool EglLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedReg return true; } +uint EglLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + EglBackend::EglBackend(Display *display, X11StandaloneBackend *backend) : EglOnXBackend(kwinApp()->x11Connection(), display, kwinApp()->x11RootWindow()) , m_backend(backend) @@ -162,7 +168,6 @@ OutputLayerBeginFrameInfo EglBackend::beginFrame() if (supportsBufferAge()) { repaint = m_damageJournal.accumulate(m_bufferAge, infiniteRegion()); } - eglWaitNative(EGL_CORE_NATIVE_ENGINE); return OutputLayerBeginFrameInfo{ diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.h b/src/backends/x11/standalone/x11_standalone_egl_backend.h index 4ff31741ee..e336e756e5 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.h +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.h @@ -29,6 +29,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + uint format() const override; private: EglBackend *const m_backend; diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp index eb091d425a..163731c2bd 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp @@ -74,6 +74,7 @@ typedef struct xcb_glx_buffer_swap_complete_event_t } xcb_glx_buffer_swap_complete_event_t; #endif +#include #include namespace KWin @@ -120,6 +121,11 @@ bool GlxLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedReg return true; } +uint GlxLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + GlxBackend::GlxBackend(Display *display, X11StandaloneBackend *backend) : OpenGLBackend() , m_overlayWindow(std::make_unique()) diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.h b/src/backends/x11/standalone/x11_standalone_glx_backend.h index e42e2486df..3ea308ab6f 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.h +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.h @@ -65,6 +65,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + uint format() const override; private: GlxBackend *const m_backend; diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp index 9757e268ef..57c7c284b9 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp @@ -13,6 +13,7 @@ #include "x11_windowed_backend.h" #include "x11_windowed_output.h" // kwin libs +#include #include namespace KWin @@ -72,6 +73,11 @@ GLFramebuffer *X11WindowedEglPrimaryLayer::fbo() const return m_fbo.get(); } +quint32 X11WindowedEglPrimaryLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + X11WindowedEglCursorLayer::X11WindowedEglCursorLayer(X11WindowedEglBackend *backend, X11WindowedOutput *output) : m_output(output) , m_backend(backend) @@ -115,6 +121,11 @@ bool X11WindowedEglCursorLayer::endFrame(const QRegion &renderedRegion, const QR return true; } +quint32 X11WindowedEglCursorLayer::format() const +{ + return DRM_FORMAT_RGBA8888; +} + X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend) : EglOnXBackend(backend->connection(), backend->display(), backend->rootWindow()) , m_backend(backend) diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.h b/src/backends/x11/windowed/x11_windowed_egl_backend.h index 4df7e66922..d5da09cf21 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.h +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.h @@ -29,6 +29,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; EGLSurface surface() const; QRegion lastDamage() const; GLFramebuffer *fbo() const; @@ -54,6 +55,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; private: X11WindowedOutput *const m_output; diff --git a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp index 1345f90468..bc13f274a0 100644 --- a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -173,6 +174,17 @@ void X11WindowedQPainterPrimaryLayer::present() m_swapchain->release(m_buffer); } +quint32 X11WindowedQPainterPrimaryLayer::format() const +{ + switch (m_buffer->view()->format()) { + case QImage::Format_A2RGB30_Premultiplied: + return DRM_FORMAT_ARGB2101010; + case QImage::Format_ARGB32_Premultiplied: + default: + return DRM_FORMAT_ARGB8888; + } +} + X11WindowedQPainterCursorLayer::X11WindowedQPainterCursorLayer(X11WindowedOutput *output) : m_output(output) { @@ -192,6 +204,11 @@ std::optional X11WindowedQPainterCursorLayer::beginFr }; } +quint32 X11WindowedQPainterCursorLayer::format() const +{ + return DRM_FORMAT_ARGB8888; +} + bool X11WindowedQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { m_output->cursor()->update(m_buffer, hotspot()); diff --git a/src/backends/x11/windowed/x11_windowed_qpainter_backend.h b/src/backends/x11/windowed/x11_windowed_qpainter_backend.h index 696afb0796..4dec8f3173 100644 --- a/src/backends/x11/windowed/x11_windowed_qpainter_backend.h +++ b/src/backends/x11/windowed/x11_windowed_qpainter_backend.h @@ -66,6 +66,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; void present(); @@ -84,6 +85,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; + quint32 format() const override; private: QImage m_buffer; diff --git a/src/composite.cpp b/src/composite.cpp index 6cd0ee0722..eefc8a7a2b 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -638,6 +638,12 @@ void Compositor::handleFrameRequested(RenderLoop *renderLoop) composite(renderLoop); } +uint Compositor::outputFormat(Output *output) +{ + OutputLayer *primaryLayer = m_backend->primaryLayer(output); + return primaryLayer->format(); +} + void Compositor::composite(RenderLoop *renderLoop) { if (m_backend->checkGraphicsReset()) { diff --git a/src/composite.h b/src/composite.h index 753cd2896e..6a411fa5d0 100644 --- a/src/composite.h +++ b/src/composite.h @@ -139,6 +139,13 @@ public: */ virtual void createOpenGLSafePoint(OpenGLSafePoint safePoint); + /** + * @returns the format of the contents in the @p output + * + * This format is provided using the drm fourcc encoding + */ + uint outputFormat(Output *output); + Q_SIGNALS: void compositingToggled(bool active); void aboutToDestroy(); diff --git a/src/core/outputlayer.h b/src/core/outputlayer.h index dbc9536db2..2bc8d3700f 100644 --- a/src/core/outputlayer.h +++ b/src/core/outputlayer.h @@ -53,6 +53,11 @@ public: virtual std::optional beginFrame() = 0; virtual bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) = 0; + /** + * Format in which the output data is internally stored in a drm fourcc format + */ + virtual quint32 format() const = 0; + /** * Tries to import the newest buffer of the surface for direct scanout * Returns @c true if scanout succeeds, @c false if rendering is necessary diff --git a/src/plugins/screencast/outputscreencastsource.cpp b/src/plugins/screencast/outputscreencastsource.cpp index 3e8ecf3ca5..17788d2640 100644 --- a/src/plugins/screencast/outputscreencastsource.cpp +++ b/src/plugins/screencast/outputscreencastsource.cpp @@ -29,6 +29,11 @@ bool OutputScreenCastSource::hasAlphaChannel() const return true; } +quint32 OutputScreenCastSource::drmFormat() const +{ + return Compositor::self()->outputFormat(m_output); +} + QSize OutputScreenCastSource::textureSize() const { return m_output->pixelSize(); diff --git a/src/plugins/screencast/outputscreencastsource.h b/src/plugins/screencast/outputscreencastsource.h index df53b48edb..81bf511b18 100644 --- a/src/plugins/screencast/outputscreencastsource.h +++ b/src/plugins/screencast/outputscreencastsource.h @@ -24,6 +24,7 @@ public: bool hasAlphaChannel() const override; QSize textureSize() const override; + quint32 drmFormat() const override; void render(GLFramebuffer *target) override; void render(QImage *image) override; diff --git a/src/plugins/screencast/regionscreencastsource.cpp b/src/plugins/screencast/regionscreencastsource.cpp index 7dc46c3aa6..daeb69d7cb 100644 --- a/src/plugins/screencast/regionscreencastsource.cpp +++ b/src/plugins/screencast/regionscreencastsource.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,11 @@ bool RegionScreenCastSource::hasAlphaChannel() const return true; } +quint32 RegionScreenCastSource::drmFormat() const +{ + return DRM_FORMAT_ARGB8888; +} + void RegionScreenCastSource::updateOutput(Output *output) { m_last = output->renderLoop()->lastPresentationTimestamp(); @@ -73,7 +79,7 @@ std::chrono::nanoseconds RegionScreenCastSource::clock() const void RegionScreenCastSource::ensureTexture() { if (!m_renderedTexture) { - m_renderedTexture.reset(new GLTexture(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize())); + m_renderedTexture.reset(new GLTexture(GL_RGBA8, textureSize())); m_target.reset(new GLFramebuffer(m_renderedTexture.get())); const auto allOutputs = workspace()->outputs(); for (auto output : allOutputs) { diff --git a/src/plugins/screencast/regionscreencastsource.h b/src/plugins/screencast/regionscreencastsource.h index 5b702d9e23..09ea033248 100644 --- a/src/plugins/screencast/regionscreencastsource.h +++ b/src/plugins/screencast/regionscreencastsource.h @@ -23,6 +23,7 @@ class RegionScreenCastSource : public ScreenCastSource public: explicit RegionScreenCastSource(const QRect ®ion, qreal scale, QObject *parent = nullptr); + quint32 drmFormat() const override; bool hasAlphaChannel() const override; QSize textureSize() const override; diff --git a/src/plugins/screencast/screencastsource.h b/src/plugins/screencast/screencastsource.h index 667d0ed549..81d78762c9 100644 --- a/src/plugins/screencast/screencastsource.h +++ b/src/plugins/screencast/screencastsource.h @@ -21,6 +21,7 @@ public: explicit ScreenCastSource(QObject *parent = nullptr); virtual bool hasAlphaChannel() const = 0; + virtual quint32 drmFormat() const = 0; virtual QSize textureSize() const = 0; virtual void render(GLFramebuffer *target) = 0; diff --git a/src/plugins/screencast/screencaststream.cpp b/src/plugins/screencast/screencaststream.cpp index 8af91c0112..fad5114c06 100644 --- a/src/plugins/screencast/screencaststream.cpp +++ b/src/plugins/screencast/screencaststream.cpp @@ -41,24 +41,30 @@ namespace KWin { -uint32_t spaVideoFormatToDrmFormat(spa_video_format spa_format) +static spa_video_format drmFourCCToSpaVideoFormat(quint32 format) { - switch (spa_format) { - case SPA_VIDEO_FORMAT_RGBA: - return DRM_FORMAT_ABGR8888; - case SPA_VIDEO_FORMAT_RGBx: - return DRM_FORMAT_XBGR8888; - case SPA_VIDEO_FORMAT_BGRA: - return DRM_FORMAT_ARGB8888; - case SPA_VIDEO_FORMAT_BGRx: - return DRM_FORMAT_XRGB8888; - case SPA_VIDEO_FORMAT_BGR: - return DRM_FORMAT_BGR888; - case SPA_VIDEO_FORMAT_RGB: - return DRM_FORMAT_RGB888; + switch (format) { + case DRM_FORMAT_ARGB8888: + return SPA_VIDEO_FORMAT_BGRA; + case DRM_FORMAT_XRGB8888: + return SPA_VIDEO_FORMAT_BGRx; + case DRM_FORMAT_RGBA8888: + return SPA_VIDEO_FORMAT_ABGR; + case DRM_FORMAT_RGBX8888: + return SPA_VIDEO_FORMAT_xBGR; + case DRM_FORMAT_ABGR8888: + return SPA_VIDEO_FORMAT_RGBA; + case DRM_FORMAT_XBGR8888: + return SPA_VIDEO_FORMAT_RGBx; + case DRM_FORMAT_BGRA8888: + return SPA_VIDEO_FORMAT_ARGB; + case DRM_FORMAT_BGRX8888: + return SPA_VIDEO_FORMAT_xRGB; + case DRM_FORMAT_NV12: + return SPA_VIDEO_FORMAT_NV12; default: - qCDebug(KWIN_SCREENCAST) << "unknown format" << spa_format; - return DRM_FORMAT_INVALID; + qCDebug(KWIN_SCREENCAST) << "unknown format" << format; + return SPA_VIDEO_FORMAT_xRGB; } } @@ -158,9 +164,9 @@ void ScreenCastStream::onStreamParamChanged(void *data, uint32_t id, const struc } if (modifierProperty && (!pw->m_dmabufParams || !receivedModifiers.contains(pw->m_dmabufParams->modifier))) { if (modifierProperty->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) { - pw->m_dmabufParams = kwinApp()->outputBackend()->testCreateDmaBuf(pw->m_resolution, spaVideoFormatToDrmFormat(pw->videoFormat.format), receivedModifiers); + pw->m_dmabufParams = kwinApp()->outputBackend()->testCreateDmaBuf(pw->m_resolution, pw->m_drmFormat, receivedModifiers); } else { - pw->m_dmabufParams = kwinApp()->outputBackend()->testCreateDmaBuf(pw->m_resolution, spaVideoFormatToDrmFormat(pw->videoFormat.format), {DRM_FORMAT_MOD_INVALID}); + pw->m_dmabufParams = kwinApp()->outputBackend()->testCreateDmaBuf(pw->m_resolution, pw->m_drmFormat, {DRM_FORMAT_MOD_INVALID}); } qCDebug(KWIN_SCREENCAST) << "Stream dmabuf modifiers received, offering our best suited modifier" << pw->m_dmabufParams.has_value(); @@ -333,11 +339,25 @@ bool ScreenCastStream::createStream() const QByteArray objname = "kwin-screencast-" + objectName().toUtf8(); pwStream = pw_stream_new(pwCore->pwCore, objname, nullptr); - // it could make sense to offer the same format as the source - const auto format = m_source->hasAlphaChannel() ? SPA_VIDEO_FORMAT_BGRA : SPA_VIDEO_FORMAT_BGR; - const int drmFormat = spaVideoFormatToDrmFormat(format); - m_hasDmaBuf = kwinApp()->outputBackend()->testCreateDmaBuf(m_resolution, drmFormat, {DRM_FORMAT_MOD_INVALID}).has_value(); - m_modifiers = Compositor::self()->backend()->supportedFormats().value(drmFormat); + const auto supported = Compositor::self()->backend()->supportedFormats(); + auto itModifiers = supported.constFind(m_source->drmFormat()); + + // If the offered format is not available for dmabuf, prefer converting to another one than resorting to memfd + if (itModifiers == supported.constEnd() && !supported.isEmpty()) { + itModifiers = supported.constFind(DRM_FORMAT_ARGB8888); + if (itModifiers == supported.constEnd()) { + m_drmFormat = itModifiers.key(); + } + } + + if (itModifiers == supported.constEnd()) { + m_drmFormat = m_source->drmFormat(); + m_modifiers = {}; + } else { + m_drmFormat = itModifiers.key(); + m_modifiers = *itModifiers; + } + m_hasDmaBuf = kwinApp()->outputBackend()->testCreateDmaBuf(m_resolution, m_drmFormat, {DRM_FORMAT_MOD_INVALID}).has_value(); char buffer[2048]; QVector params = buildFormats(false, buffer); @@ -633,7 +653,7 @@ void ScreenCastStream::enqueue() QVector ScreenCastStream::buildFormats(bool fixate, char buffer[2048]) { - const auto format = m_source->hasAlphaChannel() ? SPA_VIDEO_FORMAT_BGRA : SPA_VIDEO_FORMAT_BGR; + const auto format = drmFourCCToSpaVideoFormat(m_drmFormat); spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, 2048); spa_fraction minFramerate = SPA_FRACTION(1, 1); spa_fraction maxFramerate = SPA_FRACTION(25, 1); @@ -672,6 +692,9 @@ spa_pod *ScreenCastStream::buildFormat(struct spa_pod_builder *b, enum spa_video if (format == SPA_VIDEO_FORMAT_BGRA) { /* announce equivalent format without alpha */ spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(3, format, format, SPA_VIDEO_FORMAT_BGRx), 0); + } else if (format == SPA_VIDEO_FORMAT_RGBA) { + /* announce equivalent format without alpha */ + spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(3, format, format, SPA_VIDEO_FORMAT_RGBx), 0); } else { spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); } diff --git a/src/plugins/screencast/screencaststream.h b/src/plugins/screencast/screencaststream.h index 57311efb58..403e29bfa7 100644 --- a/src/plugins/screencast/screencaststream.h +++ b/src/plugins/screencast/screencaststream.h @@ -129,6 +129,7 @@ private: quint64 m_sequential = 0; bool m_hasDmaBuf = false; bool m_waitForNewBuffers = false; + quint32 m_drmFormat = 0; }; } // namespace KWin diff --git a/src/plugins/screencast/screencastutils.h b/src/plugins/screencast/screencastutils.h index 767ccc57d2..55efb604fc 100644 --- a/src/plugins/screencast/screencastutils.h +++ b/src/plugins/screencast/screencastutils.h @@ -25,6 +25,24 @@ static void mirrorVertically(uchar *data, int height, int stride) } } +static GLenum closestGLType(const QImage &image) +{ + switch (image.format()) { + case QImage::Format_RGB888: + return GL_RGB; + case QImage::Format_BGR888: + return GL_BGR; + case QImage::Format_RGB32: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBA8888_Premultiplied: + return GL_RGBA; + default: + qDebug() << "unknown format" << image.format(); + return GL_RGBA; + } +} + static void grabTexture(GLTexture *texture, QImage *image) { const bool invert = !texture->isYInverted(); @@ -40,11 +58,11 @@ static void grabTexture(GLTexture *texture, QImage *image) texture->bind(); if (GLPlatform::instance()->isGLES()) { - glReadPixels(0, 0, image->width(), image->height(), image->hasAlphaChannel() ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, (GLvoid *)image->bits()); + glReadPixels(0, 0, image->width(), image->height(), closestGLType(*image), GL_UNSIGNED_BYTE, (GLvoid *)image->bits()); } else if (GLPlatform::instance()->glVersion() >= kVersionNumber(4, 5)) { - glGetTextureImage(texture->texture(), 0, image->hasAlphaChannel() ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, image->sizeInBytes(), image->bits()); + glGetTextureImage(texture->texture(), 0, closestGLType(*image), GL_UNSIGNED_BYTE, image->sizeInBytes(), image->bits()); } else { - glGetTexImage(texture->target(), 0, image->hasAlphaChannel() ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, image->bits()); + glGetTexImage(texture->target(), 0, closestGLType(*image), GL_UNSIGNED_BYTE, image->bits()); } if (invertNeededAndSupported) { diff --git a/src/plugins/screencast/windowscreencastsource.cpp b/src/plugins/screencast/windowscreencastsource.cpp index 6aea2abe66..3c247a3c92 100644 --- a/src/plugins/screencast/windowscreencastsource.cpp +++ b/src/plugins/screencast/windowscreencastsource.cpp @@ -18,6 +18,7 @@ #include "scene/itemrenderer.h" #include "scene/windowitem.h" #include "scene/workspacescene.h" +#include namespace KWin { @@ -30,6 +31,11 @@ WindowScreenCastSource::WindowScreenCastSource(Window *window, QObject *parent) connect(m_window, &Window::windowClosed, this, &ScreenCastSource::closed); } +quint32 WindowScreenCastSource::drmFormat() const +{ + return DRM_FORMAT_RGBA8888; +} + bool WindowScreenCastSource::hasAlphaChannel() const { return true; diff --git a/src/plugins/screencast/windowscreencastsource.h b/src/plugins/screencast/windowscreencastsource.h index 3e8bf8595f..69cd989d53 100644 --- a/src/plugins/screencast/windowscreencastsource.h +++ b/src/plugins/screencast/windowscreencastsource.h @@ -22,6 +22,7 @@ class WindowScreenCastSource : public ScreenCastSource public: explicit WindowScreenCastSource(Window *window, QObject *parent = nullptr); + quint32 drmFormat() const override; bool hasAlphaChannel() const override; QSize textureSize() const override;