diff --git a/backends/drm/CMakeLists.txt b/backends/drm/CMakeLists.txt --- a/backends/drm/CMakeLists.txt +++ b/backends/drm/CMakeLists.txt @@ -9,14 +9,20 @@ ) if(HAVE_GBM) - set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp) + set(DRM_SOURCES ${DRM_SOURCES} + egl_gbm_backend.cpp + remoteaccess_manager.cpp + ) endif() add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) target_link_libraries(KWinWaylandDrmBackend kwin Libdrm::Libdrm) if(HAVE_GBM) - target_link_libraries(KWinWaylandDrmBackend gbm::gbm) + target_link_libraries(KWinWaylandDrmBackend + gbm::gbm + KF5::WaylandServer + ) endif() install( diff --git a/backends/drm/drm_backend.h b/backends/drm/drm_backend.h --- a/backends/drm/drm_backend.h +++ b/backends/drm/drm_backend.h @@ -134,4 +134,3 @@ } #endif - diff --git a/backends/drm/drm_backend.cpp b/backends/drm/drm_backend.cpp --- a/backends/drm/drm_backend.cpp +++ b/backends/drm/drm_backend.cpp @@ -86,6 +86,7 @@ void DrmBackend::init() { + qCInfo(KWIN_DRM) << "Initializing DRM backend"; LogindIntegration *logind = LogindIntegration::self(); auto takeControl = [logind, this]() { if (logind->hasSessionControl()) { diff --git a/backends/drm/drm_buffer.h b/backends/drm/drm_buffer.h --- a/backends/drm/drm_buffer.h +++ b/backends/drm/drm_buffer.h @@ -37,9 +37,25 @@ ~DrmBuffer(); bool map(QImage::Format format = QImage::Format_RGB32); + /** + * @brief defer - defer this buffer deletion. Valid only in case of GBM + */ + void defer() { + m_deferred = true; + } + + /** + * @brief Image - return image created on the base of buffer bits. + Valid only after call to map() + * @return image representing a buffer + */ QImage *image() const { return m_image; } + /** + * @brief handle - return DRM handle of dumb-buffer. Returns 0 in case of GBM + * @return handle of underlying buffer + */ quint32 handle() const { return m_handle; } @@ -55,6 +71,9 @@ gbm_bo *gbm() const { return m_bo; } + gbm_surface *surface() const { + return m_surface; + } bool isGbm() const { return m_bo != nullptr; } @@ -72,6 +91,7 @@ quint32 m_bufferId = 0; quint32 m_stride = 0; quint64 m_bufferSize = 0; + bool m_deferred = false; void *m_memory = nullptr; QImage *m_image = nullptr; }; @@ -79,4 +99,3 @@ } #endif - diff --git a/backends/drm/drm_buffer.cpp b/backends/drm/drm_buffer.cpp --- a/backends/drm/drm_buffer.cpp +++ b/backends/drm/drm_buffer.cpp @@ -128,6 +128,10 @@ void DrmBuffer::releaseGbm() { #if HAVE_GBM + if (m_deferred) { + return; + } + if (m_bo) { gbm_surface_release_buffer(m_surface, m_bo); m_bo = nullptr; diff --git a/backends/drm/egl_gbm_backend.h b/backends/drm/egl_gbm_backend.h --- a/backends/drm/egl_gbm_backend.h +++ b/backends/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 "scene_opengl.h" struct gbm_device; @@ -58,6 +59,7 @@ bool initializeEgl(); bool initBufferConfigs(); bool initRenderingContext(); + void initRemotePresent(); struct Output { DrmOutput *output = nullptr; DrmBuffer *buffer = nullptr; @@ -76,6 +78,7 @@ DrmBackend *m_backend; gbm_device *m_device = nullptr; QVector m_outputs; + QScopedPointer m_remoteaccessManager; friend class EglGbmTexture; }; diff --git a/backends/drm/egl_gbm_backend.cpp b/backends/drm/egl_gbm_backend.cpp --- a/backends/drm/egl_gbm_backend.cpp +++ b/backends/drm/egl_gbm_backend.cpp @@ -128,6 +128,7 @@ initKWinGL(); initBufferAge(); initWayland(); + initRemotePresent(); } bool EglGbmBackend::initRenderingContext() @@ -152,6 +153,21 @@ return makeContextCurrent(m_outputs.first()); } +void EglGbmBackend::initRemotePresent() +{ + bool supportRemotePresent = false; + const QByteArray remoteOption = qgetenv("KWIN_REMOTE"); + + if (!remoteOption.isEmpty()) { + supportRemotePresent = remoteOption == QByteArrayLiteral("1"); + } + + if(supportRemotePresent) { + qCDebug(KWIN_DRM) << "Support for remote present enabled"; + m_remoteaccessManager.reset(new RemoteAccessManager); + } +} + void EglGbmBackend::createOutput(DrmOutput *drmOutput) { Output o; @@ -236,7 +252,13 @@ eglSwapBuffers(eglDisplay(), o.eglSurface); auto oldBuffer = o.buffer; o.buffer = m_backend->createBuffer(o.gbmSurface); + if(m_remoteaccessManager && !m_remoteaccessManager->holdsBuffer()) { + // GBM surface is released on page flip so + // we should pass the buffer before it's presented + m_remoteaccessManager->passBuffer(o.buffer); + } m_backend->present(o.buffer, o.output); + delete oldBuffer; if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); diff --git a/backends/drm/remoteaccess_manager.h b/backends/drm/remoteaccess_manager.h new file mode 100644 --- /dev/null +++ b/backends/drm/remoteaccess_manager.h @@ -0,0 +1,65 @@ +/******************************************************************** + 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 . +*********************************************************************/ +#ifndef REMOTECONTROLWAYLANDSERVER_H +#define REMOTECONTROLWAYLANDSERVER_H + +#include "drm_backend.h" +#include "../../wayland_server.h" +// KWayland +#include +#include +// Qt +#include + +class gbm_bo; +class gbm_surface; + +namespace KWin +{ + +using KWayland::Server::RemoteAccessInterface; + +class RemoteAccessManager : public QObject +{ + Q_OBJECT +public: + explicit RemoteAccessManager(QObject *parent = nullptr); + virtual ~RemoteAccessManager(); + + void passBuffer(DrmBuffer *buffer); + bool holdsBuffer() const; + +signals: + void bufferNoLongerNeeded(qint32 gbm_handle); + +private: + void releaseBuffer(); + + RemoteAccessInterface *m_interface = nullptr; + struct { + int fd = 0; + gbm_bo *bo = nullptr; + gbm_surface *surface = nullptr; + } m_gbmInfo; +}; + +} // KWin namespace + +#endif // REMOTECONTROLWAYLANDSERVER_H diff --git a/backends/drm/remoteaccess_manager.cpp b/backends/drm/remoteaccess_manager.cpp new file mode 100644 --- /dev/null +++ b/backends/drm/remoteaccess_manager.cpp @@ -0,0 +1,103 @@ +/******************************************************************** + 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 "remoteaccess_manager.h" +#include "logging.h" + +// KWayland +#include +// system +#include +// system +#include + +namespace KWin +{ + +RemoteAccessManager::RemoteAccessManager(QObject *parent) + : QObject(parent) +{ + if (waylandServer()) { + m_interface = waylandServer()->display()->createRemoteAccess(this); + m_interface->create(); + + connect(m_interface, &RemoteAccessInterface::bufferNoLongerNeeded, + this, &RemoteAccessManager::releaseBuffer); + } +} + +RemoteAccessManager::~RemoteAccessManager() +{ + if (m_interface) { + m_interface->destroy(); + } + + // free the BO if not used + // at this point we know the notification from the client will not come + if (holdsBuffer()) { + releaseBuffer(); + } +} + +void RemoteAccessManager::releaseBuffer() +{ + int ret = close(m_gbmInfo.fd); + if (Q_UNLIKELY(ret)) { + qCWarning(KWIN_DRM) << "Couldn't close released GBM fd:" << strerror(errno); + } + gbm_surface_release_buffer(m_gbmInfo.surface, m_gbmInfo.bo); + + m_gbmInfo = {}; +} + +void RemoteAccessManager::passBuffer(DrmBuffer *buffer) +{ + // no connected RemoteAccess instance + if (!m_interface || !m_interface->isBound()) { + return; + } + + // first buffer may be null + if (!buffer || !buffer->gbm()) { + return; + } + + qCDebug(KWIN_DRM) << "Handing over GBM object to remote framebuffer"; + buffer->defer(); + m_gbmInfo.bo = buffer->gbm(); + m_gbmInfo.surface = buffer->surface(); + m_gbmInfo.fd = gbm_bo_get_fd(m_gbmInfo.bo); + + quint32 height = gbm_bo_get_height(m_gbmInfo.bo); + quint32 width = gbm_bo_get_width(m_gbmInfo.bo); + quint32 stride = gbm_bo_get_stride(m_gbmInfo.bo); + quint32 format = gbm_bo_get_format(m_gbmInfo.bo); + qCDebug(KWIN_DRM) << "Buffer passed: surface" << m_gbmInfo.surface + << ", bo" << m_gbmInfo.bo + << ", fd" << m_gbmInfo.fd; + + m_interface->passBuffer(m_gbmInfo.fd, width, height, stride, format); +} + +bool RemoteAccessManager::holdsBuffer() const +{ + return m_gbmInfo.bo != nullptr; +} + +} // KWin namespace diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -672,6 +672,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(),