Skip to content
Permalink
Browse files

[platforms/drm] EGLStream DRM Backend Initial Implementation

Summary:
This is the initial implementation of a DRM backend based on the EGLDevice,
EGLOutput, and EGLStream extensions, supporting NVIDIA graphics hardware using
their proprietary driver. The new backend will be used if the environment
variable KWIN_DRM_USE_EGL_STREAMS is set. On initialization, it will attempt to
create an EGLDevice based on the DRM device currently in use and create
EGLOutputs and EGLStreams for any attached displays. These are used to control
presentation of the final composited frame. Additionally, it will register the
wl_eglstream_controller Wayland interface so that native EGL windows created by
clients can be attached to an EGLStream allowing buffer contents to be shared
with the compositor as a GL texture.

At this time there are two known bugs in the NVIDIA driver's EGL implementation
affecting desktop functionality. The first can result in tooltip windows drawn
by plasmashell to contain incorrect contents. The second prevents KWayland from
being able to query the format of EGLStream-backed buffers which interferes
with the blur effect. Fixes for both of these are currently in development and
should appear in an upcoming NVIDIA driver release.

Additionally, hardware cursors are currently not supported with this backend.
Enabling them causes the desktop to intermittently hang for several seconds.
This is also likely a bug in the NVIDIA DRM-KMS implementation but the root
cause is still under investigation.

Test Plan:
On a system with an NVIDIA graphics card running a recent release of their
proprietary driver

    * Ensure the nvidia_drm kernel module is loaded with the option "modeset=1"
      ("# cat /sys/module/nvidia_drm/parameters/modeset" should print "Y")
    * Ensure EGL external platform support is installed
      https://github.com/NVIDIA/eglexternalplatform
    * Ensure KWin was build with the CMake option
      KWIN_BUILD_EGL_STREAM_BACKEND=ON (this is the default)
    * Start a plasma wayland session with the environment variable
      KWIN_DRM_USE_EGL_STREAMS set
    * Ensure output from KWin OpenGL initialization indicates the NVIDIA EGL
      driver is in use (as opposed to Mesa / llvmpipe).
    * Desktop should be fully functional and perform smoothly.

Reviewers: #kwin, romangg, davidedmundson

Reviewed By: #kwin, romangg, davidedmundson

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D18570
  • Loading branch information...
erik-kz committed Apr 15, 2019
1 parent e3d1ddd commit c898f96df3e3a2d418ca511fdb6610cf087ca464
@@ -210,6 +210,11 @@ if(HAVE_DRM AND gbm_FOUND)
set(HAVE_GBM TRUE)
endif()

option(KWIN_BUILD_EGL_STREAM_BACKEND "Enable building of EGLStream based DRM backend" ON)
if(HAVE_DRM AND KWIN_BUILD_EGL_STREAM_BACKEND)
set(HAVE_EGL_STREAMS TRUE)
endif()

find_package(libhybris)
set_package_properties(libhybris PROPERTIES TYPE OPTIONAL PURPOSE "Required for libhybris backend")
set(HAVE_LIBHYBRIS ${libhybris_FOUND})
@@ -13,6 +13,7 @@
#cmakedefine01 HAVE_X11_XINPUT
#cmakedefine01 HAVE_DRM
#cmakedefine01 HAVE_GBM
#cmakedefine01 HAVE_EGL_STREAMS
#cmakedefine01 HAVE_LIBHYBRIS
#cmakedefine01 HAVE_WAYLAND_EGL
#cmakedefine01 HAVE_SYS_PRCTL_H
@@ -21,6 +21,12 @@ if(HAVE_GBM)
)
endif()

if(HAVE_EGL_STREAMS)
set(DRM_SOURCES ${DRM_SOURCES}
egl_stream_backend.cpp
)
endif()

include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl)

add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES})
@@ -35,6 +35,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "egl_gbm_backend.h"
#include <gbm.h>
#endif
#if HAVE_EGL_STREAMS
#include "egl_stream_backend.h"
#endif
// KWayland
#include <KWayland/Server/seat_interface.h>
#include <KWayland/Server/outputconfiguration_interface.h>
@@ -74,6 +77,11 @@ DrmBackend::DrmBackend(QObject *parent)
, m_udevMonitor(m_udev->monitor())
, m_dpmsFilter()
{
#if HAVE_EGL_STREAMS
if (qEnvironmentVariableIsSet("KWIN_DRM_USE_EGL_STREAMS")) {
m_useEglStreams = true;
}
#endif
setSupportsGammaControl(true);
handleOutputs();
}
@@ -250,9 +258,10 @@ void DrmBackend::openDrm()
qCWarning(KWIN_DRM) << "Did not find a GPU";
return;
}
int fd = LogindIntegration::self()->takeDevice(device->devNode());
m_devNode = device->devNode();
int fd = LogindIntegration::self()->takeDevice(m_devNode.constData());
if (fd < 0) {
qCWarning(KWIN_DRM) << "failed to open drm device at" << device->devNode();
qCWarning(KWIN_DRM) << "failed to open drm device at" << m_devNode;
return;
}
m_fd = fd;
@@ -605,27 +614,38 @@ DrmOutput *DrmBackend::findOutput(const QByteArray &uuid)
return nullptr;
}

void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output)
bool DrmBackend::present(DrmBuffer *buffer, DrmOutput *output)
{
if (!buffer || buffer->bufferId() == 0) {
if (m_deleteBufferAfterPageFlip) {
delete buffer;
}
return;
return false;
}

if (output->present(buffer)) {
m_pageFlipsPending++;
if (m_pageFlipsPending == 1 && Compositor::self()) {
Compositor::self()->aboutToSwapBuffers();
}
return true;
} else if (m_deleteBufferAfterPageFlip) {
delete buffer;
}
return false;
}

void DrmBackend::initCursor()
{

#if HAVE_EGL_STREAMS
// Hardware cursors aren't currently supported with EGLStream backend,
// possibly an NVIDIA driver bug
if (m_useEglStreams) {
setSoftWareCursor(true);
}
#endif

m_cursorEnabled = waylandServer()->seat()->hasPointer();
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::hasPointerChanged, this,
[this] {
@@ -733,6 +753,13 @@ QPainterBackend *DrmBackend::createQPainterBackend()

OpenGLBackend *DrmBackend::createOpenGLBackend()
{
#if HAVE_EGL_STREAMS
if (m_useEglStreams) {
m_deleteBufferAfterPageFlip = false;
return new EglStreamBackend(this);
}
#endif

#if HAVE_GBM
m_deleteBufferAfterPageFlip = true;
return new EglGbmBackend(this);
@@ -774,6 +801,10 @@ QVector<CompositingType> DrmBackend::supportedCompositors() const
}
#if HAVE_GBM
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
#elif HAVE_EGL_STREAMS
return m_useEglStreams ?
QVector<CompositingType>{OpenGLCompositing, QPainterCompositing} :
QVector<CompositingType>{QPainterCompositing};
#else
return QVector<CompositingType>{QPainterCompositing};
#endif
@@ -787,6 +818,9 @@ QString DrmBackend::supportInformation() const
s << "Name: " << "DRM" << endl;
s << "Active: " << m_active << endl;
s << "Atomic Mode Setting: " << m_atomicModeSetting << endl;
#if HAVE_EGL_STREAMS
s << "Using EGL Streams: " << m_useEglStreams << endl;
#endif
return supportInfo;
}

@@ -85,7 +85,7 @@ class KWIN_EXPORT DrmBackend : public Platform
#if HAVE_GBM
DrmSurfaceBuffer *createBuffer(const std::shared_ptr<GbmSurface> &surface);
#endif
void present(DrmBuffer *buffer, DrmOutput *output);
bool present(DrmBuffer *buffer, DrmOutput *output);

int fd() const {
return m_fd;
@@ -125,6 +125,16 @@ class KWIN_EXPORT DrmBackend : public Platform
return m_gbmDevice;
}

QByteArray devNode() const {
return m_devNode;
}

#if HAVE_EGL_STREAMS
bool useEglStreams() const {
return m_useEglStreams;
}
#endif

QVector<CompositingType> supportedCompositors() const override;

QString supportInformation() const override;
@@ -182,6 +192,10 @@ public Q_SLOTS:
QSize m_cursorSize;
int m_pageFlipsPending = 0;
bool m_active = false;
QByteArray m_devNode;
#if HAVE_EGL_STREAMS
bool m_useEglStreams = false;
#endif
// all available planes: primarys, cursors and overlays
QVector<DrmPlane*> m_planes;
QVector<DrmPlane*> m_overlayPlanes;
@@ -909,6 +909,15 @@ bool DrmOutput::presentAtomically(DrmBuffer *buffer)
return false;
}

#if HAVE_EGL_STREAMS
if (m_backend->useEglStreams() && !m_modesetRequested) {
// EglStreamBackend queues normal page flips through EGL,
// modesets are still performed through DRM-KMS
m_pageFlipPending = true;
return true;
}
#endif

m_primaryPlane->setNext(buffer);
m_nextPlanesFlipList << m_primaryPlane;

@@ -1052,7 +1061,13 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
// TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10.
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
flags |= DRM_MODE_PAGE_FLIP_EVENT;

#if HAVE_EGL_STREAMS
if (!m_backend->useEglStreams())
// EglStreamBackend uses the NV_output_drm_flip_event EGL extension
// to register the flip event through eglStreamConsumerAcquireAttribNV
#endif
flags |= DRM_MODE_PAGE_FLIP_EVENT;
}
} else {
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
@@ -83,6 +83,13 @@ class KWIN_EXPORT DrmOutput : public AbstractOutput
return m_uuid;
}

const DrmCrtc *crtc() const {
return m_crtc;
}
const DrmPlane *primaryPlane() const {
return m_primaryPlane;
}

bool initCursor(const QSize &cursorSize);

bool supportsTransformations() const;
Oops, something went wrong.

0 comments on commit c898f96

Please sign in to comment.
You can’t perform that action at this time.