diff options
Diffstat (limited to 'src/gui/opengl/platform/unix/qglxconvenience.cpp')
-rw-r--r-- | src/gui/opengl/platform/unix/qglxconvenience.cpp | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/gui/opengl/platform/unix/qglxconvenience.cpp b/src/gui/opengl/platform/unix/qglxconvenience.cpp new file mode 100644 index 0000000000..ce70818042 --- /dev/null +++ b/src/gui/opengl/platform/unix/qglxconvenience.cpp @@ -0,0 +1,437 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// We have to include this before the X11 headers dragged in by +// qglxconvenience_p.h. +#include <QtCore/qbytearray.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qscopedpointer.h> +#include <QtCore/qtextstream.h> +#include <QtGui/qcolorspace.h> +#include "qglxconvenience_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtCore/qvarlengtharray.h> + + +#include <GL/glxext.h> + +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 + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcGlx, "qt.glx") + +QList<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags) +{ + QList<int> 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() == QColorSpace::SRgb) + 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", "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 <class T> +struct QXlibScopedPointerDeleter { + static inline void cleanup(T *pointer) { + if (pointer) + XFree(pointer); + } +}; + +template <class T> +using QXlibPointer = QScopedPointer<T, QXlibScopedPointerDeleter<T>>; + +template <class T> +using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>; +} + +GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags) +{ + QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer; + + GLXFBConfig config = nullptr; + + do { + const QList<int> spec = qglx_buildSpec(format, drawableBit, flags); + + int confcount = 0; + QXlibArrayPointer<GLXFBConfig> 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() == QColorSpace::SRgb) { + int srgbCapable = 0; + glXGetFBConfigAttrib(display, candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable); + if (!srgbCapable) + continue; + } + + QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(display, candidate)); + if (!visual) + continue; + 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; + } + } while (qglx_reduceFormat(&format)); + + if (!config) + qCWarning(lcGlx) << "qglx_findConfig: Failed to finding matching FBConfig for" << format; + + return config; +} + +XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags) +{ + Q_ASSERT(format); + + XVisualInfo *visualInfo = nullptr; + + 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 { + QList<int> 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_SAMPLE_BUFFERS_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); + } + if (srgbCapable) + format->setColorSpace(QColorSpace::SRgb); + else + format->setColorSpace(QColorSpace()); + + 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_SAMPLE_BUFFERS_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); + } + if (srgbCapable) + format->setColorSpace(QColorSpace::SRgb); + else + format->setColorSpace(QColorSpace()); + + 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() == QColorSpace::SRgb) { + format->setColorSpace(QColorSpace()); + return true; + } + + return false; +} + +QT_END_NAMESPACE |