diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -404,3 +404,10 @@ ) add_test(kwin-testXkb testXkb) ecm_mark_as_test(testXkb) + +if(HAVE_GBM) + add_executable(testGbmSurface test_gbm_surface.cpp ../plugins/platforms/drm/gbm_surface.cpp) + target_link_libraries(testGbmSurface Qt5::Test) + add_test(kwin-testGbmSurface testGbmSurface) + ecm_mark_as_test(kwin-testGbmSurface) +endif() diff --git a/autotests/test_gbm_surface.cpp b/autotests/test_gbm_surface.cpp new file mode 100644 --- /dev/null +++ b/autotests/test_gbm_surface.cpp @@ -0,0 +1,119 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "../plugins/platforms/drm/gbm_surface.h" +#include + +#include + +// mocking + +struct gbm_device { + bool surfaceShouldFail = false; +}; + +struct gbm_surface { + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t flags; +}; + +struct gbm_bo { +}; + +struct gbm_surface *gbm_surface_create(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags) +{ + if (gbm && gbm->surfaceShouldFail) { + return nullptr; + } + auto ret = new gbm_surface{width, height, format, flags}; + return ret; +} + +void gbm_surface_destroy(struct gbm_surface *surface) +{ + delete surface; +} + +struct gbm_bo *gbm_surface_lock_front_buffer(struct gbm_surface *surface) +{ + Q_UNUSED(surface) + return new gbm_bo; +} + +void gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo) +{ + Q_UNUSED(surface) + delete bo; +} + +using KWin::GbmSurface; + +class GbmSurfaceTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testCreate(); + void testCreateFailure(); + void testBo(); +}; + +void GbmSurfaceTest::testCreate() +{ + GbmSurface surface(nullptr, 2, 3, 4, 5); + gbm_surface *native = surface; + QVERIFY(surface); + QCOMPARE(native->width, 2u); + QCOMPARE(native->height, 3u); + QCOMPARE(native->format, 4u); + QCOMPARE(native->flags, 5u); +} + +void GbmSurfaceTest::testCreateFailure() +{ + gbm_device dev{true}; + GbmSurface surface(&dev, 2, 3, 4, 5); + QVERIFY(!surface); + gbm_surface *native = surface; + QVERIFY(!native); +} + +void GbmSurfaceTest::testBo() +{ + GbmSurface surface(nullptr, 2, 3, 4, 5); + // release buffer on nullptr should not be a problem + surface.releaseBuffer(nullptr); + // now an actual buffer + auto bo = surface.lockFrontBuffer(); + surface.releaseBuffer(bo); + + // and a surface which fails + gbm_device dev{true}; + GbmSurface surface2(&dev, 2, 3, 4, 5); + QVERIFY(!surface2.lockFrontBuffer()); + auto bo2 = surface.lockFrontBuffer(); + // this won't do anything + surface2.releaseBuffer(bo2); + // so we need to clean up properly + surface.releaseBuffer(bo2); +} + +QTEST_GUILESS_MAIN(GbmSurfaceTest) +#include "test_gbm_surface.moc" 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,7 @@ ) if(HAVE_GBM) - set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp drm_buffer_gbm.cpp) + set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp drm_buffer_gbm.cpp gbm_surface.cpp) endif() add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -35,6 +35,8 @@ #include #include +#include + struct gbm_bo; struct gbm_device; struct gbm_surface; @@ -60,6 +62,7 @@ class DrmPlane; class DrmCrtc; class DrmConnector; +class GbmSurface; class KWIN_EXPORT DrmBackend : public Platform @@ -79,7 +82,7 @@ void init() override; DrmDumbBuffer *createBuffer(const QSize &size); #if HAVE_GBM - DrmSurfaceBuffer *createBuffer(gbm_surface *surface); + DrmSurfaceBuffer *createBuffer(const std::shared_ptr &surface); #endif void present(DrmBuffer *buffer, DrmOutput *output); 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 @@ -714,7 +714,7 @@ } #if HAVE_GBM -DrmSurfaceBuffer *DrmBackend::createBuffer(gbm_surface *surface) +DrmSurfaceBuffer *DrmBackend::createBuffer(const std::shared_ptr &surface) { DrmSurfaceBuffer *b = new DrmSurfaceBuffer(this, surface); return b; 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 @@ -23,18 +23,20 @@ #include "drm_buffer.h" +#include + struct gbm_bo; -struct gbm_surface; namespace KWin { class DrmBackend; +class GbmSurface; class DrmSurfaceBuffer : public DrmBuffer { public: - DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface); + DrmSurfaceBuffer(DrmBackend *backend, const std::shared_ptr &surface); ~DrmSurfaceBuffer(); bool needsModeChange(DrmBuffer *b) const override { @@ -51,7 +53,7 @@ void releaseGbm() override; private: - gbm_surface *m_surface = nullptr; + std::shared_ptr m_surface; gbm_bo *m_bo = nullptr; }; diff --git a/plugins/platforms/drm/drm_buffer_gbm.cpp b/plugins/platforms/drm/drm_buffer_gbm.cpp --- a/plugins/platforms/drm/drm_buffer_gbm.cpp +++ b/plugins/platforms/drm/drm_buffer_gbm.cpp @@ -20,6 +20,7 @@ *********************************************************************/ #include "drm_backend.h" #include "drm_buffer_gbm.h" +#include "gbm_surface.h" #include "logging.h" @@ -34,11 +35,11 @@ { // DrmSurfaceBuffer -DrmSurfaceBuffer::DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface) +DrmSurfaceBuffer::DrmSurfaceBuffer(DrmBackend *backend, const std::shared_ptr &surface) : DrmBuffer(backend) , m_surface(surface) { - m_bo = gbm_surface_lock_front_buffer(surface); + m_bo = m_surface->lockFrontBuffer(); if (!m_bo) { qCWarning(KWIN_DRM) << "Locking front buffer failed"; return; @@ -60,10 +61,8 @@ void DrmSurfaceBuffer::releaseGbm() { - if (m_bo) { - gbm_surface_release_buffer(m_surface, m_bo); - m_bo = nullptr; - } + m_surface->releaseBuffer(m_bo); + m_bo = nullptr; } } 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 @@ -22,13 +22,16 @@ #include "abstract_egl_backend.h" #include "scene_opengl.h" +#include + struct gbm_surface; namespace KWin { class DrmBackend; class DrmBuffer; class DrmOutput; +class GbmSurface; /** * @brief OpenGL Backend using Egl on a GBM surface. @@ -60,7 +63,7 @@ struct Output { DrmOutput *output = nullptr; DrmBuffer *buffer = nullptr; - gbm_surface *gbmSurface = nullptr; + std::shared_ptr gbmSurface; EGLSurface eglSurface = EGL_NO_SURFACE; int bufferAge = 0; /** 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 @@ -22,6 +22,7 @@ #include "composite.h" #include "drm_backend.h" #include "drm_output.h" +#include "gbm_surface.h" #include "logging.h" #include "options.h" #include "screens.h" @@ -69,6 +70,7 @@ for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { cleanupOutput(*it); } + m_outputs.clear(); } void EglGbmBackend::cleanupOutput(const Output &o) @@ -78,9 +80,6 @@ if (o.eglSurface != EGL_NO_SURFACE) { eglDestroySurface(eglDisplay(), o.eglSurface); } - if (o.gbmSurface) { - gbm_surface_destroy(o.gbmSurface); - } } bool EglGbmBackend::initializeEgl() @@ -157,16 +156,16 @@ o.output = drmOutput; auto size = drmOutput->pixelSize(); - o.gbmSurface = gbm_surface_create(m_backend->gbmDevice(), size.width(), size.height(), + o.gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!o.gbmSurface) { qCCritical(KWIN_DRM) << "Create gbm surface failed"; return; } - o.eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)o.gbmSurface, nullptr); + o.eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)((gbm_surface*)o.gbmSurface.get()), nullptr); if (o.eglSurface == EGL_NO_SURFACE) { qCCritical(KWIN_DRM) << "Create Window Surface failed"; - gbm_surface_destroy(o.gbmSurface); + o.gbmSurface.reset(); return; } m_outputs << o; diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/gbm_surface.h copy from plugins/platforms/drm/drm_buffer_gbm.h copy to plugins/platforms/drm/gbm_surface.h --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/gbm_surface.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) 2017 Martin Flöser 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,44 +17,39 @@ 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 KWIN_DRM_GBM_SURFACE_H +#define KWIN_DRM_GBM_SURFACE_H -#include "drm_buffer.h" +#include struct gbm_bo; +struct gbm_device; struct gbm_surface; namespace KWin { -class DrmBackend; - -class DrmSurfaceBuffer : public DrmBuffer +class GbmSurface { public: - DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface); - ~DrmSurfaceBuffer(); - - bool needsModeChange(DrmBuffer *b) const override { - if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { - return hasBo() != sb->hasBo(); - } else { - return true; - } + explicit GbmSurface(gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags); + ~GbmSurface(); + + gbm_bo *lockFrontBuffer(); + void releaseBuffer(gbm_bo *bo); + + operator bool() const { + return m_surface != nullptr; } - bool hasBo() const { - return m_bo != nullptr; + operator gbm_surface*() const { + return m_surface; } - void releaseGbm() override; private: - gbm_surface *m_surface = nullptr; - gbm_bo *m_bo = nullptr; + gbm_surface *m_surface; }; } #endif - diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/gbm_surface.cpp copy from plugins/platforms/drm/drm_buffer_gbm.h copy to plugins/platforms/drm/gbm_surface.cpp --- a/plugins/platforms/drm/drm_buffer_gbm.h +++ b/plugins/platforms/drm/gbm_surface.cpp @@ -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) 2017 Martin Flöser 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,44 +17,39 @@ 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 +#include "gbm_surface.h" -#include "drm_buffer.h" - -struct gbm_bo; -struct gbm_surface; +#include namespace KWin { -class DrmBackend; +GbmSurface::GbmSurface(gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags) + : m_surface(gbm_surface_create(gbm, width, height, format, flags)) +{ +} -class DrmSurfaceBuffer : public DrmBuffer +GbmSurface::~GbmSurface() { -public: - DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface); - ~DrmSurfaceBuffer(); - - bool needsModeChange(DrmBuffer *b) const override { - if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { - return hasBo() != sb->hasBo(); - } else { - return true; - } + if (m_surface) { + gbm_surface_destroy(m_surface); } +} - bool hasBo() const { - return m_bo != nullptr; +gbm_bo *GbmSurface::lockFrontBuffer() +{ + if (!m_surface) { + return nullptr; } - void releaseGbm() override; - -private: - gbm_surface *m_surface = nullptr; - gbm_bo *m_bo = nullptr; -}; - + return gbm_surface_lock_front_buffer(m_surface); } -#endif +void GbmSurface::releaseBuffer(gbm_bo *bo) +{ + if (!bo || !m_surface) { + return; + } + gbm_surface_release_buffer(m_surface, bo); +} +}