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