From 02042908aaa03eb7ccbd3dd41f690ad0e77fb8e6 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Wed, 22 Jul 2020 19:38:57 +0200 Subject: [PATCH] screencasting: implement wayland output streaming for egl and wayland backends --- plugins/platforms/drm/egl_gbm_backend.cpp | 40 +++++++++++++++---- plugins/platforms/drm/egl_gbm_backend.h | 8 +++- .../platforms/wayland/egl_wayland_backend.cpp | 17 +++++--- .../platforms/wayland/egl_wayland_backend.h | 2 +- plugins/platforms/wayland/wayland_backend.cpp | 8 ++-- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index c65ff299f5..5a220cea59 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #include // Qt #include +#include // system #include @@ -251,6 +252,7 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput) if (it == m_outputs.end()) { return; } + cleanupOutput(*it); m_outputs.erase(it); } @@ -438,17 +440,12 @@ void EglGbmBackend::present() // Not in use. This backend does per-screen rendering. } -void EglGbmBackend::presentOnOutput(Output &output) +void EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion) { eglSwapBuffers(eglDisplay(), output.eglSurface); output.buffer = m_backend->createBuffer(output.gbmSurface); - if(m_remoteaccessManager && gbm_surface_has_free_buffers(output.gbmSurface->surface())) { - // GBM surface is released on page flip so - // we should pass the buffer before it's presented. - m_remoteaccessManager->passBuffer(output.output, output.buffer); - } - + Q_EMIT output.output->outputChange(damagedRegion); m_backend->present(output.buffer, output.output); if (supportsBufferAge()) { @@ -537,7 +534,7 @@ void EglGbmBackend::endRenderingFrameForScreen(int screenId, } return; } - presentOnOutput(output); + presentOnOutput(output, damagedRegion); // Save the damaged region to history // Note: damage history is only collected for the first screen. For any other screen full @@ -565,6 +562,33 @@ bool EglGbmBackend::perScreenRendering() const return true; } +QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *abstractOutput) const +{ + const QVector::const_iterator itOutput = std::find_if(m_outputs.begin(), m_outputs.end(), + [abstractOutput] (const auto &output) { + return output.output == abstractOutput; + } + ); + if (itOutput == m_outputs.end()) { + return {}; + } + + DrmOutput *drmOutput = itOutput->output; + if (!drmOutput->hardwareTransforms()) { + const auto glTexture = QSharedPointer::create(itOutput->render.texture, GL_RGBA8, drmOutput->pixelSize()); + glTexture->setYInverted(true); + return glTexture; + } + + EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr); + if (image == EGL_NO_IMAGE_KHR) { + qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError(); + return {}; + } + + return QSharedPointer::create(eglDisplay(), image, GL_RGBA8, drmOutput->modeSize()); +} + /************************************************ * EglTexture ************************************************/ diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h index d45fbd7e5b..3b2e967e4d 100644 --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -28,8 +28,10 @@ struct gbm_surface; namespace KWin { +class AbstractOutput; class DrmBackend; class DrmBuffer; +class DrmSurfaceBuffer; class DrmOutput; class GbmSurface; @@ -52,6 +54,8 @@ public: QRegion prepareRenderingForScreen(int screenId) override; void init() override; + QSharedPointer textureForOutput(AbstractOutput *requestedOutput) const override; + protected: void present() override; void cleanupSurfaces() override; @@ -63,7 +67,7 @@ private: void initRemotePresent(); struct Output { DrmOutput *output = nullptr; - DrmBuffer *buffer = nullptr; + DrmSurfaceBuffer *buffer = nullptr; std::shared_ptr gbmSurface; EGLSurface eglSurface = EGL_NO_SURFACE; int bufferAge = 0; @@ -93,7 +97,7 @@ private: void prepareRenderFramebuffer(const Output &output) const; void renderFramebufferToSurface(Output &output); - void presentOnOutput(Output &output); + void presentOnOutput(Output &output, const QRegion &damagedRegion); void removeOutput(DrmOutput *drmOutput); void cleanupOutput(Output &output); diff --git a/plugins/platforms/wayland/egl_wayland_backend.cpp b/plugins/platforms/wayland/egl_wayland_backend.cpp index adac99831d..83db47e6b4 100644 --- a/plugins/platforms/wayland/egl_wayland_backend.cpp +++ b/plugins/platforms/wayland/egl_wayland_backend.cpp @@ -32,6 +32,9 @@ along with this program. If not, see . #include "wayland_server.h" #include "screens.h" +#include +#include + // kwin libs #include @@ -41,6 +44,7 @@ along with this program. If not, see . #include // Qt +#include #include namespace KWin @@ -288,11 +292,11 @@ void EglWaylandBackend::present() { for (auto *output: qAsConst(m_outputs)) { makeContextCurrent(output); - presentOnSurface(output); + presentOnSurface(output, output->m_waylandOutput->geometry()); } } -void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output) +void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output, const QRegion &damage) { output->m_waylandOutput->surface()->setupFrameCallback(); if (!m_swapping) { @@ -300,6 +304,8 @@ void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output) Compositor::self()->aboutToSwapBuffers(); } + Q_EMIT output->m_waylandOutput->outputChange(damage); + if (supportsBufferAge()) { eglSwapBuffers(eglDisplay(), output->m_eglSurface); eglQuerySurface(eglDisplay(), output->m_eglSurface, EGL_BUFFER_AGE_EXT, &output->m_bufferAge); @@ -363,7 +369,8 @@ void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const Q void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion) { EglWaylandOutput *output = m_outputs[screenId]; - if (damagedRegion.intersected(output->m_waylandOutput->geometry()).isEmpty() && screenId == 0) { + QRegion damage = damagedRegion.intersected(output->m_waylandOutput->geometry()); + if (damage.isEmpty() && screenId == 0) { // If the damaged region of a window is fully occluded, the only // rendering done, if any, will have been to repair a reused back @@ -381,7 +388,7 @@ void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion & } return; } - presentOnSurface(output); + presentOnSurface(output, damage); // Save the damaged region to history // Note: damage history is only collected for the first screen. See EglGbmBackend @@ -391,7 +398,7 @@ void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion & output->m_damageHistory.removeLast(); } - output->m_damageHistory.prepend(damagedRegion.intersected(output->m_waylandOutput->geometry())); + output->m_damageHistory.prepend(damage); } } diff --git a/plugins/platforms/wayland/egl_wayland_backend.h b/plugins/platforms/wayland/egl_wayland_backend.h index 23ebbaba1a..d723892ea9 100644 --- a/plugins/platforms/wayland/egl_wayland_backend.h +++ b/plugins/platforms/wayland/egl_wayland_backend.h @@ -105,7 +105,7 @@ private: bool makeContextCurrent(EglWaylandOutput *output); void present() override; - void presentOnSurface(EglWaylandOutput *output); + void presentOnSurface(EglWaylandOutput *output, const QRegion &damagedRegion); WaylandBackend *m_backend; QVector m_outputs; diff --git a/plugins/platforms/wayland/wayland_backend.cpp b/plugins/platforms/wayland/wayland_backend.cpp index bd8bffd7fd..1c3339b73d 100644 --- a/plugins/platforms/wayland/wayland_backend.cpp +++ b/plugins/platforms/wayland/wayland_backend.cpp @@ -460,13 +460,13 @@ WaylandBackend::WaylandBackend(QObject *parent) char const *drm_render_node = "/dev/dri/renderD128"; - m_drm_fd = open(drm_render_node, O_RDWR); - if (m_drm_fd < 0) { + m_drmFileDescriptor = open(drm_render_node, O_RDWR); + if (m_drmFileDescriptor < 0) { qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to open drm render node" << drm_render_node; m_gbmDevice = nullptr; return; } - m_gbmDevice = gbm_create_device(m_drm_fd); + m_gbmDevice = gbm_create_device(m_drmFileDescriptor); } WaylandBackend::~WaylandBackend() @@ -492,7 +492,7 @@ WaylandBackend::~WaylandBackend() m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); gbm_device_destroy(m_gbmDevice); - close(m_drm_fd); + close(m_drmFileDescriptor); qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display"; }