File indexing completed on 2024-11-10 04:57:05

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "eglplatformcontext.h"
0012 #include "core/outputbackend.h"
0013 #include "eglhelpers.h"
0014 #include "internalwindow.h"
0015 #include "offscreensurface.h"
0016 #include "opengl/eglcontext.h"
0017 #include "opengl/egldisplay.h"
0018 #include "opengl/glutils.h"
0019 #include "swapchain.h"
0020 #include "window.h"
0021 
0022 #include "logging.h"
0023 
0024 #include <QOpenGLContext>
0025 
0026 #include <private/qopenglcontext_p.h>
0027 
0028 namespace KWin
0029 {
0030 namespace QPA
0031 {
0032 
0033 EGLRenderTarget::EGLRenderTarget(GraphicsBuffer *buffer, std::unique_ptr<GLFramebuffer> fbo, std::shared_ptr<GLTexture> texture)
0034     : buffer(buffer)
0035     , fbo(std::move(fbo))
0036     , texture(std::move(texture))
0037 {
0038 }
0039 
0040 EGLRenderTarget::~EGLRenderTarget()
0041 {
0042     fbo.reset();
0043     texture.reset();
0044 }
0045 
0046 EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EglDisplay *display)
0047     : m_eglDisplay(display)
0048 {
0049     create(context->format(), kwinApp()->outputBackend()->sceneEglGlobalShareContext());
0050 }
0051 
0052 EGLPlatformContext::~EGLPlatformContext()
0053 {
0054     if (!m_renderTargets.empty() || !m_zombieRenderTargets.empty()) {
0055         m_eglContext->makeCurrent();
0056         m_renderTargets.clear();
0057         m_zombieRenderTargets.clear();
0058     }
0059 }
0060 
0061 bool EGLPlatformContext::makeCurrent(QPlatformSurface *surface)
0062 {
0063     const bool ok = m_eglContext->makeCurrent();
0064     if (!ok) {
0065         qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError());
0066         return false;
0067     }
0068 
0069     m_zombieRenderTargets.clear();
0070 
0071     if (surface->surface()->surfaceClass() == QSurface::Window) {
0072         // QOpenGLContextPrivate::setCurrentContext will be called after this
0073         // method returns, but that's too late, as we need a current context in
0074         // order to bind the content framebuffer object.
0075         QOpenGLContextPrivate::setCurrentContext(context());
0076 
0077         Window *window = static_cast<Window *>(surface);
0078         Swapchain *swapchain = window->swapchain(m_eglDisplay->nonExternalOnlySupportedDrmFormats());
0079 
0080         GraphicsBuffer *buffer = swapchain->acquire();
0081         if (!buffer) {
0082             return false;
0083         }
0084 
0085         auto it = m_renderTargets.find(buffer);
0086         if (it != m_renderTargets.end()) {
0087             m_current = it->second;
0088         } else {
0089             std::shared_ptr<GLTexture> texture = m_eglContext->importDmaBufAsTexture(*buffer->dmabufAttributes());
0090             if (!texture) {
0091                 return false;
0092             }
0093 
0094             std::unique_ptr<GLFramebuffer> fbo = std::make_unique<GLFramebuffer>(texture.get(), GLFramebuffer::CombinedDepthStencil);
0095             if (!fbo->valid()) {
0096                 return false;
0097             }
0098 
0099             auto target = std::make_shared<EGLRenderTarget>(buffer, std::move(fbo), std::move(texture));
0100             m_renderTargets[buffer] = target;
0101             QObject::connect(buffer, &QObject::destroyed, this, [this, buffer]() {
0102                 if (auto it = m_renderTargets.find(buffer); it != m_renderTargets.end()) {
0103                     m_zombieRenderTargets.push_back(std::move(it->second));
0104                     m_renderTargets.erase(it);
0105                 }
0106             });
0107 
0108             m_current = target;
0109         }
0110 
0111         glBindFramebuffer(GL_FRAMEBUFFER, m_current->fbo->handle());
0112     }
0113 
0114     return true;
0115 }
0116 
0117 void EGLPlatformContext::doneCurrent()
0118 {
0119     m_eglContext->doneCurrent();
0120 }
0121 
0122 bool EGLPlatformContext::isValid() const
0123 {
0124     return m_eglContext != nullptr;
0125 }
0126 
0127 bool EGLPlatformContext::isSharing() const
0128 {
0129     return false;
0130 }
0131 
0132 QSurfaceFormat EGLPlatformContext::format() const
0133 {
0134     return m_format;
0135 }
0136 
0137 QFunctionPointer EGLPlatformContext::getProcAddress(const char *procName)
0138 {
0139     return eglGetProcAddress(procName);
0140 }
0141 
0142 void EGLPlatformContext::swapBuffers(QPlatformSurface *surface)
0143 {
0144     if (surface->surface()->surfaceClass() == QSurface::Window) {
0145         Window *window = static_cast<Window *>(surface);
0146         InternalWindow *internalWindow = window->internalWindow();
0147         if (!internalWindow) {
0148             return;
0149         }
0150 
0151         glFlush(); // We need to flush pending rendering commands manually
0152 
0153         internalWindow->present(InternalWindowFrame{
0154             .buffer = m_current->buffer,
0155             .bufferDamage = QRect(QPoint(0, 0), m_current->buffer->size()),
0156             .bufferOrigin = GraphicsBufferOrigin::BottomLeft,
0157         });
0158 
0159         m_current.reset();
0160     }
0161 }
0162 
0163 GLuint EGLPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const
0164 {
0165     if (surface->surface()->surfaceClass() == QSurface::Window) {
0166         if (m_current) {
0167             return m_current->fbo->handle();
0168         }
0169         qCDebug(KWIN_QPA) << "No default framebuffer object for internal window";
0170     }
0171 
0172     return 0;
0173 }
0174 
0175 void EGLPlatformContext::create(const QSurfaceFormat &format, ::EGLContext shareContext)
0176 {
0177     if (!eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
0178         qCWarning(KWIN_QPA, "eglBindAPI failed: 0x%x", eglGetError());
0179         return;
0180     }
0181 
0182     m_config = configFromFormat(m_eglDisplay, format);
0183     if (m_config == EGL_NO_CONFIG_KHR) {
0184         qCWarning(KWIN_QPA) << "Could not find suitable EGLConfig for" << format;
0185         return;
0186     }
0187 
0188     m_format = formatFromConfig(m_eglDisplay, m_config);
0189     m_eglContext = EglContext::create(m_eglDisplay, m_config, shareContext);
0190     if (!m_eglContext) {
0191         qCWarning(KWIN_QPA) << "Failed to create EGL context";
0192         return;
0193     }
0194     updateFormatFromContext();
0195 }
0196 
0197 void EGLPlatformContext::updateFormatFromContext()
0198 {
0199     const EGLSurface oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
0200     const EGLSurface oldReadSurface = eglGetCurrentSurface(EGL_READ);
0201     const ::EGLContext oldContext = eglGetCurrentContext();
0202 
0203     m_eglContext->makeCurrent();
0204 
0205     const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
0206     int major, minor;
0207     if (parseOpenGLVersion(version, major, minor)) {
0208         m_format.setMajorVersion(major);
0209         m_format.setMinorVersion(minor);
0210     } else {
0211         qCWarning(KWIN_QPA) << "Unrecognized OpenGL version:" << version;
0212     }
0213 
0214     GLint value;
0215 
0216     if (m_format.version() >= qMakePair(3, 0)) {
0217         glGetIntegerv(GL_CONTEXT_FLAGS, &value);
0218         if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
0219             m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
0220         }
0221         if (value & GL_CONTEXT_FLAG_DEBUG_BIT) {
0222             m_format.setOption(QSurfaceFormat::DebugContext);
0223         }
0224     } else {
0225         m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
0226     }
0227 
0228     if (m_format.version() >= qMakePair(3, 2)) {
0229         glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
0230         if (value & GL_CONTEXT_CORE_PROFILE_BIT) {
0231             m_format.setProfile(QSurfaceFormat::CoreProfile);
0232         } else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
0233             m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
0234         } else {
0235             m_format.setProfile(QSurfaceFormat::NoProfile);
0236         }
0237     } else {
0238         m_format.setProfile(QSurfaceFormat::NoProfile);
0239     }
0240 
0241     eglMakeCurrent(m_eglDisplay->handle(), oldDrawSurface, oldReadSurface, oldContext);
0242 }
0243 
0244 } // namespace QPA
0245 } // namespace KWin