File indexing completed on 2024-11-10 04:56:55
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