File indexing completed on 2024-05-12 05:31:43

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
0006     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
0007     SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "opengl/glutils.h"
0013 #include "glplatform.h"
0014 #include "gltexture_p.h"
0015 #include "utils/common.h"
0016 
0017 namespace KWin
0018 {
0019 
0020 static QList<QByteArray> glExtensions;
0021 
0022 // Functions
0023 
0024 static void initDebugOutput()
0025 {
0026     const bool have_KHR_debug = hasGLExtension(QByteArrayLiteral("GL_KHR_debug"));
0027     const bool have_ARB_debug = hasGLExtension(QByteArrayLiteral("GL_ARB_debug_output"));
0028     if (!have_KHR_debug && !have_ARB_debug) {
0029         return;
0030     }
0031 
0032     if (!have_ARB_debug) {
0033         // if we don't have ARB debug, but only KHR debug we need to verify whether the context is a debug context
0034         // it should work without as well, but empirical tests show: no it doesn't
0035         if (GLPlatform::instance()->isGLES()) {
0036             if (!hasGLVersion(3, 2)) {
0037                 // empirical data shows extension doesn't work
0038                 return;
0039             }
0040         } else if (!hasGLVersion(3, 0)) {
0041             return;
0042         }
0043         // can only be queried with either OpenGL >= 3.0 or OpenGL ES of at least 3.1
0044         GLint value = 0;
0045         glGetIntegerv(GL_CONTEXT_FLAGS, &value);
0046         if (!(value & GL_CONTEXT_FLAG_DEBUG_BIT)) {
0047             return;
0048         }
0049     }
0050 
0051     // Set the callback function
0052     auto callback = [](GLenum source, GLenum type, GLuint id,
0053                        GLenum severity, GLsizei length,
0054                        const GLchar *message,
0055                        const GLvoid *userParam) {
0056         while (length && std::isspace(message[length - 1])) {
0057             --length;
0058         }
0059 
0060         switch (type) {
0061         case GL_DEBUG_TYPE_ERROR:
0062         case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
0063             qCWarning(KWIN_OPENGL, "%#x: %.*s", id, length, message);
0064             break;
0065 
0066         case GL_DEBUG_TYPE_OTHER:
0067         case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
0068         case GL_DEBUG_TYPE_PORTABILITY:
0069         case GL_DEBUG_TYPE_PERFORMANCE:
0070         default:
0071             qCDebug(KWIN_OPENGL, "%#x: %.*s", id, length, message);
0072             break;
0073         }
0074     };
0075 
0076     glDebugMessageCallback(callback, nullptr);
0077 
0078     // This state exists only in GL_KHR_debug
0079     if (have_KHR_debug) {
0080         glEnable(GL_DEBUG_OUTPUT);
0081     }
0082 
0083     if (qEnvironmentVariableIntValue("KWIN_GL_DEBUG")) {
0084         // Enable all debug messages
0085         glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
0086         // Insert a test message
0087         const QByteArray message = QByteArrayLiteral("OpenGL debug output initialized");
0088         glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0,
0089                              GL_DEBUG_SEVERITY_LOW, message.length(), message.constData());
0090     } else {
0091         // Only enable error messages
0092         glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, nullptr, GL_TRUE);
0093         glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, GL_DONT_CARE, 0, nullptr, GL_TRUE);
0094     }
0095 }
0096 
0097 void initGL(const std::function<resolveFuncPtr(const char *)> &resolveFunction)
0098 {
0099     // Get list of supported OpenGL extensions
0100     if (hasGLVersion(3, 0)) {
0101         int count;
0102         glGetIntegerv(GL_NUM_EXTENSIONS, &count);
0103 
0104         for (int i = 0; i < count; i++) {
0105             const QByteArray name = (const char *)glGetStringi(GL_EXTENSIONS, i);
0106             glExtensions << name;
0107         }
0108     } else {
0109         glExtensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
0110     }
0111 
0112     // handle OpenGL extensions functions
0113     glResolveFunctions(resolveFunction);
0114 
0115     initDebugOutput();
0116 
0117     GLTexturePrivate::initStatic();
0118     GLFramebuffer::initStatic();
0119     GLVertexBuffer::initStatic();
0120 }
0121 
0122 void cleanupGL()
0123 {
0124     ShaderManager::cleanup();
0125     GLTexturePrivate::cleanup();
0126     GLFramebuffer::cleanup();
0127     GLVertexBuffer::cleanup();
0128     GLPlatform::cleanup();
0129 
0130     glExtensions.clear();
0131 }
0132 
0133 bool hasGLVersion(int major, int minor, int release)
0134 {
0135     return GLPlatform::instance()->glVersion() >= Version(major, minor, release);
0136 }
0137 
0138 bool hasGLExtension(const QByteArray &extension)
0139 {
0140     return glExtensions.contains(extension);
0141 }
0142 
0143 QList<QByteArray> openGLExtensions()
0144 {
0145     return glExtensions;
0146 }
0147 
0148 static QString formatGLError(GLenum err)
0149 {
0150     switch (err) {
0151     case GL_NO_ERROR:
0152         return QStringLiteral("GL_NO_ERROR");
0153     case GL_INVALID_ENUM:
0154         return QStringLiteral("GL_INVALID_ENUM");
0155     case GL_INVALID_VALUE:
0156         return QStringLiteral("GL_INVALID_VALUE");
0157     case GL_INVALID_OPERATION:
0158         return QStringLiteral("GL_INVALID_OPERATION");
0159     case GL_STACK_OVERFLOW:
0160         return QStringLiteral("GL_STACK_OVERFLOW");
0161     case GL_STACK_UNDERFLOW:
0162         return QStringLiteral("GL_STACK_UNDERFLOW");
0163     case GL_OUT_OF_MEMORY:
0164         return QStringLiteral("GL_OUT_OF_MEMORY");
0165     default:
0166         return QLatin1String("0x") + QString::number(err, 16);
0167     }
0168 }
0169 
0170 bool checkGLError(const char *txt)
0171 {
0172     GLenum err = glGetError();
0173     if (err == GL_CONTEXT_LOST) {
0174         qCWarning(KWIN_OPENGL) << "GL error: context lost";
0175         return true;
0176     }
0177     bool hasError = false;
0178     while (err != GL_NO_ERROR) {
0179         qCWarning(KWIN_OPENGL) << "GL error (" << txt << "): " << formatGLError(err);
0180         hasError = true;
0181         err = glGetError();
0182         if (err == GL_CONTEXT_LOST) {
0183             qCWarning(KWIN_OPENGL) << "GL error: context lost";
0184             break;
0185         }
0186     }
0187     return hasError;
0188 }
0189 
0190 } // namespace