diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -777,6 +777,7 @@ if (pluginName.isEmpty()) { std::cerr << "No backend specified through command line argument, trying auto resolution" << std::endl; pluginName = KWin::automaticBackendSelection(); + std::cerr << "Selected backend " << pluginName.toStdString() << std::endl; } auto pluginIt = std::find_if(availablePlugins.begin(), availablePlugins.end(), diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt --- a/plugins/platforms/drm/CMakeLists.txt +++ b/plugins/platforms/drm/CMakeLists.txt @@ -13,7 +13,12 @@ ) if(HAVE_GBM) - set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp drm_buffer_gbm.cpp gbm_surface.cpp) + set(DRM_SOURCES ${DRM_SOURCES} + egl_gbm_backend.cpp + drm_buffer_gbm.cpp + gbm_surface.cpp + remoteaccess_manager.cpp + ) endif() include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl) diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -100,6 +100,7 @@ void DrmBackend::init() { + qCInfo(KWIN_DRM) << "Initializing DRM backend"; LogindIntegration *logind = LogindIntegration::self(); auto takeControl = [logind, this]() { if (logind->hasSessionControl()) { diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/drm_buffer_gbm.h --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/drm_buffer_gbm.h @@ -49,6 +49,11 @@ bool hasBo() const { return m_bo != nullptr; } + + gbm_bo* getBo() const { + return m_bo; + } + void releaseGbm() override; private: diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -136,6 +136,7 @@ void modeChanged(); private: + friend class RemoteAccessManager; friend class DrmBackend; friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc // and save the connector ids in the DrmCrtc instance. diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -20,6 +20,7 @@ #ifndef KWIN_EGL_GBM_BACKEND_H #define KWIN_EGL_GBM_BACKEND_H #include "abstract_egl_backend.h" +#include "remoteaccess_manager.h" #include @@ -59,6 +60,7 @@ bool initializeEgl(); bool initBufferConfigs(); bool initRenderingContext(); + void initRemotePresent(); struct Output { DrmOutput *output = nullptr; DrmBuffer *buffer = nullptr; @@ -77,6 +79,7 @@ void createOutput(DrmOutput *output); DrmBackend *m_backend; QVector m_outputs; + QScopedPointer m_remoteaccessManager; friend class EglGbmTexture; }; diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -130,6 +130,7 @@ initKWinGL(); initBufferAge(); initWayland(); + initRemotePresent(); } bool EglGbmBackend::initRenderingContext() @@ -154,6 +155,15 @@ return makeContextCurrent(m_outputs.first()); } +void EglGbmBackend::initRemotePresent() +{ + if (!qEnvironmentVariableIsSet("KWIN_REMOTE")) + return; + + qCDebug(KWIN_DRM) << "Support for remote access enabled"; + m_remoteaccessManager.reset(new RemoteAccessManager); +} + bool EglGbmBackend::resetOutput(Output &o, DrmOutput *drmOutput) { o.output = drmOutput; @@ -272,7 +282,13 @@ { eglSwapBuffers(eglDisplay(), o.eglSurface); o.buffer = m_backend->createBuffer(o.gbmSurface); + if(m_remoteaccessManager && gbm_surface_has_free_buffers(o.gbmSurface->surface())) { + // GBM surface is released on page flip so + // we should pass the buffer before it's presented + m_remoteaccessManager->passBuffer(o.output, o.buffer); + } m_backend->present(o.buffer, o.output); + if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); } diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/remoteaccess_manager.h copy from plugins/platforms/drm/drm_buffer_gbm.h copy to plugins/platforms/drm/remoteaccess_manager.h --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/remoteaccess_manager.h @@ -2,8 +2,7 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright 2017 Roman Gilg -Copyright 2015 Martin Gräßlin +Copyright (C) 2016 Oleg Chernovskiy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,45 +17,45 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#ifndef KWIN_DRM_BUFFER_GBM_H -#define KWIN_DRM_BUFFER_GBM_H +#ifndef REMOTEACCESSMANAGER_H +#define REMOTEACCESSMANAGER_H -#include "drm_buffer.h" - -#include +// KWayland +#include +#include +// Qt +#include struct gbm_bo; +struct gbm_surface; namespace KWin { -class GbmSurface; +class DrmOutput; +class DrmBuffer; + +using KWayland::Server::RemoteAccessManagerInterface; +using KWayland::Server::BufferHandle; -class DrmSurfaceBuffer : public DrmBuffer +class RemoteAccessManager : public QObject { + Q_OBJECT public: - DrmSurfaceBuffer(int fd, const std::shared_ptr &surface); - ~DrmSurfaceBuffer(); - - bool needsModeChange(DrmBuffer *b) const override { - if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { - return hasBo() != sb->hasBo(); - } else { - return true; - } - } - - bool hasBo() const { - return m_bo != nullptr; - } - void releaseGbm() override; + explicit RemoteAccessManager(QObject *parent = nullptr); + virtual ~RemoteAccessManager(); + + void passBuffer(DrmOutput *output, DrmBuffer *buffer); + +signals: + void bufferNoLongerNeeded(qint32 gbm_handle); private: - std::shared_ptr m_surface; - gbm_bo *m_bo = nullptr; -}; + void releaseBuffer(const BufferHandle *buf); -} + RemoteAccessManagerInterface *m_interface = nullptr; +}; -#endif +} // KWin namespace +#endif // REMOTEACCESSMANAGER_H diff --git a/plugins/platforms/drm/remoteaccess_manager.cpp b/plugins/platforms/drm/remoteaccess_manager.cpp new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/remoteaccess_manager.cpp @@ -0,0 +1,90 @@ +/******************************************************************** + * + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Oleg Chernovskiy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "drm_output.h" +#include "remoteaccess_manager.h" +#include "logging.h" +#include "drm_backend.h" +#include "../../../wayland_server.h" + +// system +#include +#include +#include + +namespace KWin +{ + +RemoteAccessManager::RemoteAccessManager(QObject *parent) + : QObject(parent) +{ + if (waylandServer()) { + m_interface = waylandServer()->display()->createRemoteAccessManager(this); + m_interface->create(); + + connect(m_interface, &RemoteAccessManagerInterface::bufferReleased, + this, &RemoteAccessManager::releaseBuffer); + } +} + +RemoteAccessManager::~RemoteAccessManager() +{ + if (m_interface) { + m_interface->destroy(); + } +} + +void RemoteAccessManager::releaseBuffer(const BufferHandle *buf) +{ + int ret = close(buf->fd()); + if (Q_UNLIKELY(ret)) { + qCWarning(KWIN_DRM) << "Couldn't close released GBM fd:" << strerror(errno); + } + delete buf; +} + +void RemoteAccessManager::passBuffer(DrmOutput *output, DrmBuffer *buffer) +{ + DrmSurfaceBuffer* gbmbuf = static_cast(buffer); + + // no connected RemoteAccess instance + if (!m_interface || !m_interface->isBound()) { + return; + } + + // first buffer may be null + if (!gbmbuf || !gbmbuf->hasBo()) { + return; + } + + qCDebug(KWIN_DRM) << "Handing over GBM object to remote framebuffer"; + auto buf = new BufferHandle; + auto bo = gbmbuf->getBo(); + buf->setFd(gbm_bo_get_fd(bo)); + buf->setSize(gbm_bo_get_width(bo), gbm_bo_get_height(bo)); + buf->setStride(gbm_bo_get_stride(bo)); + buf->setFormat(gbm_bo_get_format(bo)); + + qCDebug(KWIN_DRM) << "Buffer passed: bo" << gbmbuf->getBo() << ", fd" << buf->fd(); + + m_interface->sendBufferReady(output->m_waylandOutput.data(), buf); +} + +} // KWin namespace