/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 "egl_gbm_backend.h" // kwin #include "composite.h" #include "virtual_backend.h" #include "options.h" #include "screens.h" // kwin libs #include // Qt #include namespace KWin { EglGbmBackend::EglGbmBackend(VirtualBackend *b) : QObject(nullptr) , AbstractEglBackend() , m_backend(b) { initializeEgl(); init(); // Egl is always direct rendering setIsDirectRendering(true); } EglGbmBackend::~EglGbmBackend() { while (GLRenderTarget::isRenderTargetBound()) { GLRenderTarget::popRenderTarget(); } delete m_fbo; delete m_backBuffer; cleanup(); } bool EglGbmBackend::initializeEgl() { initClientExtensions(); EGLDisplay display = EGL_NO_DISPLAY; // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); return false; } display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, EGL_DEFAULT_DISPLAY, nullptr); if (display == EGL_NO_DISPLAY) return false; setEglDisplay(display); return initEglAPI(); } void EglGbmBackend::init() { if (!initRenderingContext()) { setFailed("Could not initialize rendering context"); return; } initKWinGL(); m_backBuffer = new GLTexture(GL_RGB8, screens()->size().width(), screens()->size().height()); m_fbo = new GLRenderTarget(*m_backBuffer); if (!m_fbo->valid()) { setFailed("Could not create framebuffer object"); return; } GLRenderTarget::pushRenderTarget(m_fbo); if (!m_fbo->isRenderTargetBound()) { setFailed("Failed to bind framebuffer object"); return; } if (checkGLError("Init")) { setFailed("Error during init of EglGbmBackend"); return; } setSupportsBufferAge(false); initWayland(); } bool EglGbmBackend::initRenderingContext() { initBufferConfigs(); const char* eglExtensionsCString = eglQueryString(eglDisplay(), EGL_EXTENSIONS); const QList extensions = QByteArray::fromRawData(eglExtensionsCString, qstrlen(eglExtensionsCString)).split(' '); if (!extensions.contains(QByteArrayLiteral("EGL_KHR_surfaceless_context"))) { return false; } EGLContext context = EGL_NO_CONTEXT; #ifdef KWIN_HAVE_OPENGLES const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs); #else const EGLint context_attribs_31_core[] = { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, EGL_NONE }; const EGLint context_attribs_legacy[] = { EGL_NONE }; // Try to create a 3.1 core context if (options->glCoreProfile() && extensions.contains(QByteArrayLiteral("EGL_KHR_create_context"))) context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_31_core); if (context == EGL_NO_CONTEXT) context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_legacy); #endif if (context == EGL_NO_CONTEXT) { // qCCritical(KWIN_DRM) << "Create Context failed"; return false; } setContext(context); setSurfaceLessContext(true); return makeCurrent(); } bool EglGbmBackend::initBufferConfigs() { const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, #ifdef KWIN_HAVE_OPENGLES EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, #else EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, #endif EGL_CONFIG_CAVEAT, EGL_NONE, EGL_NONE, }; EGLint count; EGLConfig configs[1024]; if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) { return false; } if (count != 1) { return false; } setConfig(configs[0]); return true; } void EglGbmBackend::present() { } void EglGbmBackend::screenGeometryChanged(const QSize &size) { Q_UNUSED(size) // TODO, create new buffer? } SceneOpenGL::TexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGL::Texture *texture) { return new EglGbmTexture(texture, this); } QRegion EglGbmBackend::prepareRenderingFrame() { startRenderTimer(); if (!GLRenderTarget::isRenderTargetBound()) { GLRenderTarget::pushRenderTarget(m_fbo); } return QRegion(0, 0, screens()->size().width(), screens()->size().height()); } static void convertFromGLImage(QImage &img, int w, int h) { // from QtOpenGL/qgl.cpp // Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) // see http://qt.gitorious.org/qt/qt/blobs/master/src/opengl/qgl.cpp if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { // OpenGL gives RGBA; Qt wants ARGB uint *p = (uint*)img.bits(); uint *end = p + w * h; while (p < end) { uint a = *p << 24; *p = (*p >> 8) | a; p++; } } else { // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB for (int y = 0; y < h; y++) { uint *q = (uint*)img.scanLine(y); for (int x = 0; x < w; ++x) { const uint pixel = *q; *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00); q++; } } } img = img.mirrored(); } void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { Q_UNUSED(renderedRegion) Q_UNUSED(damagedRegion) glFlush(); if (m_backend->saveFrames()) { QImage img = QImage(QSize(m_backBuffer->width(), m_backBuffer->height()), QImage::Format_ARGB32); glReadnPixels(0, 0, m_backBuffer->width(), m_backBuffer->height(), GL_RGBA, GL_UNSIGNED_BYTE, img.byteCount(), (GLvoid*)img.bits()); convertFromGLImage(img, m_backBuffer->width(), m_backBuffer->height()); img.save(QStringLiteral("%1/%2.png").arg(m_backend->saveFrames()).arg(QString::number(m_frameCounter++))); } GLRenderTarget::popRenderTarget(); } bool EglGbmBackend::usesOverlayWindow() const { return false; } /************************************************ * EglTexture ************************************************/ EglGbmTexture::EglGbmTexture(KWin::SceneOpenGL::Texture *texture, EglGbmBackend *backend) : AbstractEglTexture(texture, backend) { } EglGbmTexture::~EglGbmTexture() = default; } // namespace