File indexing completed on 2025-04-20 10:57:34

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "drm_gbm_surface.h"
0011 
0012 #include <errno.h>
0013 #include <gbm.h>
0014 
0015 #include "drm_backend.h"
0016 #include "drm_egl_backend.h"
0017 #include "drm_gpu.h"
0018 #include "drm_logging.h"
0019 #include "kwineglutils_p.h"
0020 #include "kwinglplatform.h"
0021 
0022 namespace KWin
0023 {
0024 
0025 GbmSurface::GbmSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, uint32_t flags, gbm_surface *surface, EGLSurface eglSurface)
0026     : m_surface(surface)
0027     , m_eglBackend(backend)
0028     , m_eglSurface(eglSurface)
0029     , m_size(size)
0030     , m_format(format)
0031     , m_modifiers(modifiers)
0032     , m_flags(flags)
0033     , m_fbo(std::make_unique<GLFramebuffer>(0, size))
0034 {
0035 }
0036 
0037 GbmSurface::~GbmSurface()
0038 {
0039     if (m_eglSurface != EGL_NO_SURFACE) {
0040         eglDestroySurface(m_eglBackend->eglDisplay(), m_eglSurface);
0041     }
0042     if (m_surface) {
0043         gbm_surface_destroy(m_surface);
0044     }
0045 }
0046 
0047 bool GbmSurface::makeContextCurrent() const
0048 {
0049     if (eglMakeCurrent(m_eglBackend->eglDisplay(), m_eglSurface, m_eglSurface, m_eglBackend->context()) == EGL_FALSE) {
0050         qCCritical(KWIN_DRM) << "eglMakeCurrent failed:" << getEglErrorString();
0051         return false;
0052     }
0053     if (!GLPlatform::instance()->isGLES()) {
0054         glDrawBuffer(GL_BACK);
0055         glReadBuffer(GL_BACK);
0056     }
0057     return true;
0058 }
0059 
0060 std::shared_ptr<GbmBuffer> GbmSurface::swapBuffers(const QRegion &dirty)
0061 {
0062     auto error = eglSwapBuffers(m_eglBackend->eglDisplay(), m_eglSurface);
0063     if (error != EGL_TRUE) {
0064         qCCritical(KWIN_DRM) << "an error occurred while swapping buffers" << getEglErrorString();
0065         return nullptr;
0066     }
0067     auto bo = gbm_surface_lock_front_buffer(m_surface);
0068     if (!bo) {
0069         return nullptr;
0070     }
0071     if (m_eglBackend->supportsBufferAge()) {
0072         eglQuerySurface(m_eglBackend->eglDisplay(), m_eglSurface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
0073         m_damageJournal.add(dirty);
0074     }
0075     return std::make_shared<GbmBuffer>(m_eglBackend->gpu(), bo, shared_from_this());
0076 }
0077 
0078 void GbmSurface::releaseBuffer(GbmBuffer *buffer)
0079 {
0080     gbm_surface_release_buffer(m_surface, buffer->bo());
0081 }
0082 
0083 GLFramebuffer *GbmSurface::fbo() const
0084 {
0085     return m_fbo.get();
0086 }
0087 
0088 EGLSurface GbmSurface::eglSurface() const
0089 {
0090     return m_eglSurface;
0091 }
0092 
0093 QSize GbmSurface::size() const
0094 {
0095     return m_size;
0096 }
0097 
0098 uint32_t GbmSurface::format() const
0099 {
0100     return m_format;
0101 }
0102 
0103 QVector<uint64_t> GbmSurface::modifiers() const
0104 {
0105     return m_modifiers;
0106 }
0107 
0108 int GbmSurface::bufferAge() const
0109 {
0110     return m_bufferAge;
0111 }
0112 
0113 QRegion GbmSurface::repaintRegion() const
0114 {
0115     if (m_eglBackend->supportsBufferAge()) {
0116         return m_damageJournal.accumulate(m_bufferAge, infiniteRegion());
0117     } else {
0118         return infiniteRegion();
0119     }
0120 }
0121 
0122 uint32_t GbmSurface::flags() const
0123 {
0124     return m_flags;
0125 }
0126 
0127 std::variant<std::shared_ptr<GbmSurface>, GbmSurface::Error> GbmSurface::createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, uint32_t flags, EGLConfig config)
0128 {
0129     gbm_surface *surface = gbm_surface_create(backend->gpu()->gbmDevice(), size.width(), size.height(), format, flags);
0130     if (!surface) {
0131         qCWarning(KWIN_DRM) << "Creating gbm surface failed!" << strerror(errno);
0132         return Error::Unknown;
0133     }
0134     EGLSurface eglSurface = eglCreatePlatformWindowSurfaceEXT(backend->eglDisplay(), config, surface, nullptr);
0135     if (eglSurface == EGL_NO_SURFACE) {
0136         qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
0137         gbm_surface_destroy(surface);
0138         return Error::Unknown;
0139     }
0140     return std::make_shared<GbmSurface>(backend, size, format, QVector<uint64_t>{}, flags, surface, eglSurface);
0141 }
0142 
0143 std::variant<std::shared_ptr<GbmSurface>, GbmSurface::Error> GbmSurface::createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, QVector<uint64_t> modifiers, EGLConfig config)
0144 {
0145     gbm_surface *surface = gbm_surface_create_with_modifiers(backend->gpu()->gbmDevice(), size.width(), size.height(), format, modifiers.data(), modifiers.size());
0146     if (!surface) {
0147         if (errno == ENOSYS) {
0148             return Error::ModifiersUnsupported;
0149         } else {
0150             qCWarning(KWIN_DRM) << "Creating gbm surface failed!" << strerror(errno);
0151             return Error::Unknown;
0152         }
0153     }
0154     EGLSurface eglSurface = eglCreatePlatformWindowSurfaceEXT(backend->eglDisplay(), config, surface, nullptr);
0155     if (eglSurface == EGL_NO_SURFACE) {
0156         qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
0157         gbm_surface_destroy(surface);
0158         return Error::Unknown;
0159     }
0160     return std::make_shared<GbmSurface>(backend, size, format, modifiers, 0, surface, eglSurface);
0161 }
0162 }