/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // We have to include this before the X11 headers dragged in by // qglxconvenience_p.h. #include #include #include #include #include "qglxconvenience_p.h" #include #include #include #include Q_LOGGING_CATEGORY(lcGlx, "qt.glx") enum { XFocusOut = FocusOut, XFocusIn = FocusIn, XKeyPress = KeyPress, XKeyRelease = KeyRelease, XNone = None, XRevertToParent = RevertToParent, XGrayScale = GrayScale, XCursorShape = CursorShape }; #undef FocusOut #undef FocusIn #undef KeyPress #undef KeyRelease #undef None #undef RevertToParent #undef GrayScale #undef CursorShape #ifdef FontChange #undef FontChange #endif #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 #endif QVector qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags) { QVector spec; spec << GLX_LEVEL << 0 << GLX_RENDER_TYPE << GLX_RGBA_BIT << GLX_RED_SIZE << qMax(1, format.redBufferSize()) << GLX_GREEN_SIZE << qMax(1, format.greenBufferSize()) << GLX_BLUE_SIZE << qMax(1, format.blueBufferSize()) << GLX_ALPHA_SIZE << qMax(0, format.alphaBufferSize()); if (format.swapBehavior() != QSurfaceFormat::SingleBuffer) spec << GLX_DOUBLEBUFFER << True; if (format.stereo()) spec << GLX_STEREO << True; if (format.depthBufferSize() != -1) spec << GLX_DEPTH_SIZE << format.depthBufferSize(); if (format.stencilBufferSize() != -1) spec << GLX_STENCIL_SIZE << format.stencilBufferSize(); if (format.samples() > 1) spec << GLX_SAMPLE_BUFFERS_ARB << 1 << GLX_SAMPLES_ARB << format.samples(); if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB << True; spec << GLX_DRAWABLE_TYPE << drawableBit << XNone; return spec; } namespace { struct QXcbSoftwareOpenGLEnforcer { QXcbSoftwareOpenGLEnforcer() { // Allow forcing LIBGL_ALWAYS_SOFTWARE for Qt 5 applications only. // This is most useful with drivers that only support OpenGL 1. // We need OpenGL 2, but the user probably doesn't want // LIBGL_ALWAYS_SOFTWARE in OpenGL 1 apps. if (!checkedForceSoftwareOpenGL) { // If LIBGL_ALWAYS_SOFTWARE is already set, don't mess with it. // We want to unset LIBGL_ALWAYS_SOFTWARE at the end so it does not // get inherited by other processes, of course only if it wasn't // already set before. if (!qEnvironmentVariableIsEmpty("QT_XCB_FORCE_SOFTWARE_OPENGL") && !qEnvironmentVariableIsSet("LIBGL_ALWAYS_SOFTWARE")) forceSoftwareOpenGL = true; checkedForceSoftwareOpenGL = true; } if (forceSoftwareOpenGL) qputenv("LIBGL_ALWAYS_SOFTWARE", QByteArrayLiteral("1")); } ~QXcbSoftwareOpenGLEnforcer() { // unset LIBGL_ALWAYS_SOFTWARE now so other processes don't inherit it if (forceSoftwareOpenGL) qunsetenv("LIBGL_ALWAYS_SOFTWARE"); } static bool checkedForceSoftwareOpenGL; static bool forceSoftwareOpenGL; }; bool QXcbSoftwareOpenGLEnforcer::checkedForceSoftwareOpenGL = false; bool QXcbSoftwareOpenGLEnforcer::forceSoftwareOpenGL = false; template struct QXlibScopedPointerDeleter { static inline void cleanup(T *pointer) { if (pointer) XFree(pointer); } }; template using QXlibPointer = QScopedPointer>; template using QXlibArrayPointer = QScopedArrayPointer>; } GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags) { QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer; GLXFBConfig config = 0; do { const QVector spec = qglx_buildSpec(format, drawableBit, flags); int confcount = 0; QXlibArrayPointer configs(glXChooseFBConfig(display, screen, spec.constData(), &confcount)); if (!config && confcount > 0) { config = configs[0]; if (highestPixelFormat && !format.hasAlpha()) break; } const int requestedRed = qMax(0, format.redBufferSize()); const int requestedGreen = qMax(0, format.greenBufferSize()); const int requestedBlue = qMax(0, format.blueBufferSize()); const int requestedAlpha = qMax(0, format.alphaBufferSize()); GLXFBConfig compatibleCandidate = nullptr; for (int i = 0; i < confcount; i++) { GLXFBConfig candidate = configs[i]; if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) { int srgbCapable = 0; glXGetFBConfigAttrib(display, candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); if (!srgbCapable) continue; } QXlibPointer visual(glXGetVisualFromFBConfig(display, candidate)); int actualRed; int actualGreen; int actualBlue; int actualAlpha; glXGetFBConfigAttrib(display, candidate, GLX_RED_SIZE, &actualRed); glXGetFBConfigAttrib(display, candidate, GLX_GREEN_SIZE, &actualGreen); glXGetFBConfigAttrib(display, candidate, GLX_BLUE_SIZE, &actualBlue); glXGetFBConfigAttrib(display, candidate, GLX_ALPHA_SIZE, &actualAlpha); // Sometimes the visuals don't have a depth that includes the alpha channel. actualAlpha = qMin(actualAlpha, visual->depth - actualRed - actualGreen - actualBlue); if (requestedRed && actualRed < requestedRed) continue; if (requestedGreen && actualGreen < requestedGreen) continue; if (requestedBlue && actualBlue < requestedBlue) continue; if (requestedAlpha && actualAlpha < requestedAlpha) continue; if (!compatibleCandidate) // Only pick up the first compatible one offered by the server compatibleCandidate = candidate; if (requestedRed && actualRed != requestedRed) continue; if (requestedGreen && actualGreen != requestedGreen) continue; if (requestedBlue && actualBlue != requestedBlue) continue; if (requestedAlpha && actualAlpha != requestedAlpha) continue; return candidate; } if (compatibleCandidate) { qCDebug(lcGlx) << "qglx_findConfig: Found non-matching but compatible FBConfig"; return compatibleCandidate; } qCWarning(lcGlx, "qglx_findConfig: Failed to finding matching FBConfig (%d %d %d %d)", requestedRed, requestedGreen, requestedBlue, requestedAlpha); } while (qglx_reduceFormat(&format)); return config; } XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags) { Q_ASSERT(format); XVisualInfo *visualInfo = 0; GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit, flags); if (config) visualInfo = glXGetVisualFromFBConfig(display, config); if (visualInfo) { qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags); return visualInfo; } // attempt to fall back to glXChooseVisual do { QVector attribs = qglx_buildSpec(*format, drawableBit, flags); visualInfo = glXChooseVisual(display, screen, attribs.data()); if (visualInfo) { qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags); return visualInfo; } } while (qglx_reduceFormat(format)); return visualInfo; } void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags) { int redSize = 0; int greenSize = 0; int blueSize = 0; int alphaSize = 0; int depthSize = 0; int stencilSize = 0; int sampleBuffers = 0; int sampleCount = 0; int stereo = 0; int srgbCapable = 0; glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize); glXGetFBConfigAttrib(display, config, GLX_GREEN_SIZE, &greenSize); glXGetFBConfigAttrib(display, config, GLX_BLUE_SIZE, &blueSize); glXGetFBConfigAttrib(display, config, GLX_ALPHA_SIZE, &alphaSize); glXGetFBConfigAttrib(display, config, GLX_DEPTH_SIZE, &depthSize); glXGetFBConfigAttrib(display, config, GLX_STENCIL_SIZE, &stencilSize); glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleBuffers); glXGetFBConfigAttrib(display, config, GLX_STEREO, &stereo); if (flags & QGLX_SUPPORTS_SRGB) glXGetFBConfigAttrib(display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); format->setRedBufferSize(redSize); format->setGreenBufferSize(greenSize); format->setBlueBufferSize(blueSize); format->setAlphaBufferSize(alphaSize); format->setDepthBufferSize(depthSize); format->setStencilBufferSize(stencilSize); if (sampleBuffers) { glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleCount); format->setSamples(sampleCount); } format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace); format->setStereo(stereo); } void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags) { int redSize = 0; int greenSize = 0; int blueSize = 0; int alphaSize = 0; int depthSize = 0; int stencilSize = 0; int sampleBuffers = 0; int sampleCount = 0; int stereo = 0; int srgbCapable = 0; glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize); glXGetConfig(display, visualInfo, GLX_GREEN_SIZE, &greenSize); glXGetConfig(display, visualInfo, GLX_BLUE_SIZE, &blueSize); glXGetConfig(display, visualInfo, GLX_ALPHA_SIZE, &alphaSize); glXGetConfig(display, visualInfo, GLX_DEPTH_SIZE, &depthSize); glXGetConfig(display, visualInfo, GLX_STENCIL_SIZE, &stencilSize); glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleBuffers); glXGetConfig(display, visualInfo, GLX_STEREO, &stereo); if (flags & QGLX_SUPPORTS_SRGB) glXGetConfig(display, visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); format->setRedBufferSize(redSize); format->setGreenBufferSize(greenSize); format->setBlueBufferSize(blueSize); format->setAlphaBufferSize(alphaSize); format->setDepthBufferSize(depthSize); format->setStencilBufferSize(stencilSize); if (sampleBuffers) { glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount); format->setSamples(sampleCount); } format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace); format->setStereo(stereo); } bool qglx_reduceFormat(QSurfaceFormat *format) { Q_ASSERT(format); if (std::max(std::max(format->redBufferSize(), format->greenBufferSize()), format->blueBufferSize()) > 8) { if (format->alphaBufferSize() > 2) { // First try to match 10 10 10 2 format->setAlphaBufferSize(2); return true; } format->setRedBufferSize(std::min(format->redBufferSize(), 8)); format->setGreenBufferSize(std::min(format->greenBufferSize(), 8)); format->setBlueBufferSize(std::min(format->blueBufferSize(), 8)); return true; } if (format->redBufferSize() > 1) { format->setRedBufferSize(1); return true; } if (format->greenBufferSize() > 1) { format->setGreenBufferSize(1); return true; } if (format->blueBufferSize() > 1) { format->setBlueBufferSize(1); return true; } if (format->swapBehavior() != QSurfaceFormat::SingleBuffer){ format->setSwapBehavior(QSurfaceFormat::SingleBuffer); return true; } if (format->samples() > 1) { format->setSamples(qMin(16, format->samples() / 2)); return true; } if (format->depthBufferSize() >= 32) { format->setDepthBufferSize(24); return true; } if (format->depthBufferSize() > 1) { format->setDepthBufferSize(1); return true; } if (format->depthBufferSize() > 0) { format->setDepthBufferSize(0); return true; } if (format->hasAlpha()) { format->setAlphaBufferSize(0); return true; } if (format->stencilBufferSize() > 1) { format->setStencilBufferSize(1); return true; } if (format->stencilBufferSize() > 0) { format->setStencilBufferSize(0); return true; } if (format->stereo()) { format->setStereo(false); return true; } if (format->colorSpace() == QSurfaceFormat::sRGBColorSpace) { format->setColorSpace(QSurfaceFormat::DefaultColorSpace); return true; } return false; }