1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4// We have to include this before the X11 headers dragged in by
5// qglxconvenience_p.h.
6#include <QtCore/qbytearray.h>
7#include <QtCore/qmetatype.h>
8#include <QtCore/qscopedpointer.h>
9#include <QtCore/qtextstream.h>
10#include <QtGui/qcolorspace.h>
11#include "qglxconvenience_p.h"
12
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qvarlengtharray.h>
15
16
17#include <GL/glxext.h>
18
19enum {
20 XFocusOut = FocusOut,
21 XFocusIn = FocusIn,
22 XKeyPress = KeyPress,
23 XKeyRelease = KeyRelease,
24 XNone = None,
25 XRevertToParent = RevertToParent,
26 XGrayScale = GrayScale,
27 XCursorShape = CursorShape
28};
29#undef FocusOut
30#undef FocusIn
31#undef KeyPress
32#undef KeyRelease
33#undef None
34#undef RevertToParent
35#undef GrayScale
36#undef CursorShape
37
38#ifdef FontChange
39#undef FontChange
40#endif
41
42#ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
43#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
44#endif
45
46QT_BEGIN_NAMESPACE
47
48Q_LOGGING_CATEGORY(lcGlx, "qt.glx")
49
50QList<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags)
51{
52 QList<int> spec;
53
54 spec << GLX_LEVEL
55 << 0
56
57 << GLX_RENDER_TYPE
58 << GLX_RGBA_BIT
59
60 << GLX_RED_SIZE
61 << qMax(a: 1, b: format.redBufferSize())
62
63 << GLX_GREEN_SIZE
64 << qMax(a: 1, b: format.greenBufferSize())
65
66 << GLX_BLUE_SIZE
67 << qMax(a: 1, b: format.blueBufferSize())
68
69 << GLX_ALPHA_SIZE
70 << qMax(a: 0, b: format.alphaBufferSize());
71
72 if (format.swapBehavior() != QSurfaceFormat::SingleBuffer)
73 spec << GLX_DOUBLEBUFFER
74 << True;
75
76 if (format.stereo())
77 spec << GLX_STEREO
78 << True;
79
80 if (format.depthBufferSize() != -1)
81 spec << GLX_DEPTH_SIZE
82 << format.depthBufferSize();
83
84 if (format.stencilBufferSize() != -1)
85 spec << GLX_STENCIL_SIZE
86 << format.stencilBufferSize();
87
88 if (format.samples() > 1)
89 spec << GLX_SAMPLE_BUFFERS_ARB
90 << 1
91 << GLX_SAMPLES_ARB
92 << format.samples();
93
94 if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QColorSpace::SRgb)
95 spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
96 << True;
97
98 spec << GLX_DRAWABLE_TYPE
99 << drawableBit
100
101 << XNone;
102
103 return spec;
104}
105
106namespace {
107struct QXcbSoftwareOpenGLEnforcer {
108 QXcbSoftwareOpenGLEnforcer() {
109 // Allow forcing LIBGL_ALWAYS_SOFTWARE for Qt 5 applications only.
110 // This is most useful with drivers that only support OpenGL 1.
111 // We need OpenGL 2, but the user probably doesn't want
112 // LIBGL_ALWAYS_SOFTWARE in OpenGL 1 apps.
113
114 if (!checkedForceSoftwareOpenGL) {
115 // If LIBGL_ALWAYS_SOFTWARE is already set, don't mess with it.
116 // We want to unset LIBGL_ALWAYS_SOFTWARE at the end so it does not
117 // get inherited by other processes, of course only if it wasn't
118 // already set before.
119 if (!qEnvironmentVariableIsEmpty(varName: "QT_XCB_FORCE_SOFTWARE_OPENGL")
120 && !qEnvironmentVariableIsSet(varName: "LIBGL_ALWAYS_SOFTWARE"))
121 forceSoftwareOpenGL = true;
122
123 checkedForceSoftwareOpenGL = true;
124 }
125
126 if (forceSoftwareOpenGL)
127 qputenv(varName: "LIBGL_ALWAYS_SOFTWARE", value: "1");
128 }
129
130 ~QXcbSoftwareOpenGLEnforcer() {
131 // unset LIBGL_ALWAYS_SOFTWARE now so other processes don't inherit it
132 if (forceSoftwareOpenGL)
133 qunsetenv(varName: "LIBGL_ALWAYS_SOFTWARE");
134 }
135
136 static bool checkedForceSoftwareOpenGL;
137 static bool forceSoftwareOpenGL;
138};
139
140bool QXcbSoftwareOpenGLEnforcer::checkedForceSoftwareOpenGL = false;
141bool QXcbSoftwareOpenGLEnforcer::forceSoftwareOpenGL = false;
142
143template <class T>
144struct QXlibScopedPointerDeleter {
145 static inline void cleanup(T *pointer) {
146 if (pointer)
147 XFree(pointer);
148 }
149};
150
151template <class T>
152using QXlibPointer = QScopedPointer<T, QXlibScopedPointerDeleter<T>>;
153
154template <class T>
155using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>;
156}
157
158GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags)
159{
160 QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer;
161
162 GLXFBConfig config = nullptr;
163
164 do {
165 const QList<int> spec = qglx_buildSpec(format, drawableBit, flags);
166
167 int confcount = 0;
168 QXlibArrayPointer<GLXFBConfig> configs(glXChooseFBConfig(dpy: display, screen, attribList: spec.constData(), nitems: &confcount));
169
170 if (!config && confcount > 0) {
171 config = configs[0];
172 if (highestPixelFormat && !format.hasAlpha())
173 break;
174 }
175
176 const int requestedRed = qMax(a: 0, b: format.redBufferSize());
177 const int requestedGreen = qMax(a: 0, b: format.greenBufferSize());
178 const int requestedBlue = qMax(a: 0, b: format.blueBufferSize());
179 const int requestedAlpha = qMax(a: 0, b: format.alphaBufferSize());
180
181 GLXFBConfig compatibleCandidate = nullptr;
182 for (int i = 0; i < confcount; i++) {
183 GLXFBConfig candidate = configs[i];
184
185 if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QColorSpace::SRgb) {
186 int srgbCapable = 0;
187 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
188 if (!srgbCapable)
189 continue;
190 }
191
192 QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(dpy: display, config: candidate));
193 if (!visual)
194 continue;
195 int actualRed;
196 int actualGreen;
197 int actualBlue;
198 int actualAlpha;
199 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_RED_SIZE, value: &actualRed);
200 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_GREEN_SIZE, value: &actualGreen);
201 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_BLUE_SIZE, value: &actualBlue);
202 glXGetFBConfigAttrib(dpy: display, config: candidate, GLX_ALPHA_SIZE, value: &actualAlpha);
203 // Sometimes the visuals don't have a depth that includes the alpha channel.
204 actualAlpha = qMin(a: actualAlpha, b: visual->depth - actualRed - actualGreen - actualBlue);
205
206 if (requestedRed && actualRed < requestedRed)
207 continue;
208 if (requestedGreen && actualGreen < requestedGreen)
209 continue;
210 if (requestedBlue && actualBlue < requestedBlue)
211 continue;
212 if (requestedAlpha && actualAlpha < requestedAlpha)
213 continue;
214 if (!compatibleCandidate) // Only pick up the first compatible one offered by the server
215 compatibleCandidate = candidate;
216
217 if (requestedRed && actualRed != requestedRed)
218 continue;
219 if (requestedGreen && actualGreen != requestedGreen)
220 continue;
221 if (requestedBlue && actualBlue != requestedBlue)
222 continue;
223 if (requestedAlpha && actualAlpha != requestedAlpha)
224 continue;
225
226 return candidate;
227 }
228 if (compatibleCandidate) {
229 qCDebug(lcGlx) << "qglx_findConfig: Found non-matching but compatible FBConfig";
230 return compatibleCandidate;
231 }
232 } while (qglx_reduceFormat(format: &format));
233
234 if (!config)
235 qCWarning(lcGlx) << "qglx_findConfig: Failed to finding matching FBConfig for" << format;
236
237 return config;
238}
239
240XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags)
241{
242 Q_ASSERT(format);
243
244 XVisualInfo *visualInfo = nullptr;
245
246 GLXFBConfig config = qglx_findConfig(display, screen, format: *format, highestPixelFormat: false, drawableBit, flags);
247 if (config)
248 visualInfo = glXGetVisualFromFBConfig(dpy: display, config);
249
250 if (visualInfo) {
251 qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags);
252 return visualInfo;
253 }
254
255 // attempt to fall back to glXChooseVisual
256 do {
257 QList<int> attribs = qglx_buildSpec(format: *format, drawableBit, flags);
258 visualInfo = glXChooseVisual(dpy: display, screen, attribList: attribs.data());
259
260 if (visualInfo) {
261 qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags);
262 return visualInfo;
263 }
264 } while (qglx_reduceFormat(format));
265
266 return visualInfo;
267}
268
269void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags)
270{
271 int redSize = 0;
272 int greenSize = 0;
273 int blueSize = 0;
274 int alphaSize = 0;
275 int depthSize = 0;
276 int stencilSize = 0;
277 int sampleBuffers = 0;
278 int sampleCount = 0;
279 int stereo = 0;
280 int srgbCapable = 0;
281
282 glXGetFBConfigAttrib(dpy: display, config, GLX_RED_SIZE, value: &redSize);
283 glXGetFBConfigAttrib(dpy: display, config, GLX_GREEN_SIZE, value: &greenSize);
284 glXGetFBConfigAttrib(dpy: display, config, GLX_BLUE_SIZE, value: &blueSize);
285 glXGetFBConfigAttrib(dpy: display, config, GLX_ALPHA_SIZE, value: &alphaSize);
286 glXGetFBConfigAttrib(dpy: display, config, GLX_DEPTH_SIZE, value: &depthSize);
287 glXGetFBConfigAttrib(dpy: display, config, GLX_STENCIL_SIZE, value: &stencilSize);
288 glXGetFBConfigAttrib(dpy: display, config, GLX_SAMPLE_BUFFERS_ARB, value: &sampleBuffers);
289 glXGetFBConfigAttrib(dpy: display, config, GLX_STEREO, value: &stereo);
290 if (flags & QGLX_SUPPORTS_SRGB)
291 glXGetFBConfigAttrib(dpy: display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
292
293 format->setRedBufferSize(redSize);
294 format->setGreenBufferSize(greenSize);
295 format->setBlueBufferSize(blueSize);
296 format->setAlphaBufferSize(alphaSize);
297 format->setDepthBufferSize(depthSize);
298 format->setStencilBufferSize(stencilSize);
299 if (sampleBuffers) {
300 glXGetFBConfigAttrib(dpy: display, config, GLX_SAMPLES_ARB, value: &sampleCount);
301 format->setSamples(sampleCount);
302 }
303 if (srgbCapable)
304 format->setColorSpace(QColorSpace::SRgb);
305 else
306 format->setColorSpace(QColorSpace());
307
308 format->setStereo(stereo);
309}
310
311void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags)
312{
313 int redSize = 0;
314 int greenSize = 0;
315 int blueSize = 0;
316 int alphaSize = 0;
317 int depthSize = 0;
318 int stencilSize = 0;
319 int sampleBuffers = 0;
320 int sampleCount = 0;
321 int stereo = 0;
322 int srgbCapable = 0;
323
324 glXGetConfig(dpy: display, visual: visualInfo, GLX_RED_SIZE, value: &redSize);
325 glXGetConfig(dpy: display, visual: visualInfo, GLX_GREEN_SIZE, value: &greenSize);
326 glXGetConfig(dpy: display, visual: visualInfo, GLX_BLUE_SIZE, value: &blueSize);
327 glXGetConfig(dpy: display, visual: visualInfo, GLX_ALPHA_SIZE, value: &alphaSize);
328 glXGetConfig(dpy: display, visual: visualInfo, GLX_DEPTH_SIZE, value: &depthSize);
329 glXGetConfig(dpy: display, visual: visualInfo, GLX_STENCIL_SIZE, value: &stencilSize);
330 glXGetConfig(dpy: display, visual: visualInfo, GLX_SAMPLE_BUFFERS_ARB, value: &sampleBuffers);
331 glXGetConfig(dpy: display, visual: visualInfo, GLX_STEREO, value: &stereo);
332 if (flags & QGLX_SUPPORTS_SRGB)
333 glXGetConfig(dpy: display, visual: visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, value: &srgbCapable);
334
335 format->setRedBufferSize(redSize);
336 format->setGreenBufferSize(greenSize);
337 format->setBlueBufferSize(blueSize);
338 format->setAlphaBufferSize(alphaSize);
339 format->setDepthBufferSize(depthSize);
340 format->setStencilBufferSize(stencilSize);
341 if (sampleBuffers) {
342 glXGetConfig(dpy: display, visual: visualInfo, GLX_SAMPLES_ARB, value: &sampleCount);
343 format->setSamples(sampleCount);
344 }
345 if (srgbCapable)
346 format->setColorSpace(QColorSpace::SRgb);
347 else
348 format->setColorSpace(QColorSpace());
349
350 format->setStereo(stereo);
351}
352
353bool qglx_reduceFormat(QSurfaceFormat *format)
354{
355 Q_ASSERT(format);
356 if (std::max(a: std::max(a: format->redBufferSize(), b: format->greenBufferSize()), b: format->blueBufferSize()) > 8) {
357 if (format->alphaBufferSize() > 2) {
358 // First try to match 10 10 10 2
359 format->setAlphaBufferSize(2);
360 return true;
361 }
362
363 format->setRedBufferSize(std::min(a: format->redBufferSize(), b: 8));
364 format->setGreenBufferSize(std::min(a: format->greenBufferSize(), b: 8));
365 format->setBlueBufferSize(std::min(a: format->blueBufferSize(), b: 8));
366 return true;
367 }
368
369 if (format->redBufferSize() > 1) {
370 format->setRedBufferSize(1);
371 return true;
372 }
373
374 if (format->greenBufferSize() > 1) {
375 format->setGreenBufferSize(1);
376 return true;
377 }
378
379 if (format->blueBufferSize() > 1) {
380 format->setBlueBufferSize(1);
381 return true;
382 }
383
384 if (format->swapBehavior() != QSurfaceFormat::SingleBuffer){
385 format->setSwapBehavior(QSurfaceFormat::SingleBuffer);
386 return true;
387 }
388
389 if (format->samples() > 1) {
390 format->setSamples(qMin(a: 16, b: format->samples() / 2));
391 return true;
392 }
393
394 if (format->depthBufferSize() >= 32) {
395 format->setDepthBufferSize(24);
396 return true;
397 }
398
399 if (format->depthBufferSize() > 1) {
400 format->setDepthBufferSize(1);
401 return true;
402 }
403
404 if (format->depthBufferSize() > 0) {
405 format->setDepthBufferSize(0);
406 return true;
407 }
408
409 if (format->hasAlpha()) {
410 format->setAlphaBufferSize(0);
411 return true;
412 }
413
414 if (format->stencilBufferSize() > 1) {
415 format->setStencilBufferSize(1);
416 return true;
417 }
418
419 if (format->stencilBufferSize() > 0) {
420 format->setStencilBufferSize(0);
421 return true;
422 }
423
424 if (format->stereo()) {
425 format->setStereo(false);
426 return true;
427 }
428
429 if (format->colorSpace() == QColorSpace::SRgb) {
430 format->setColorSpace(QColorSpace());
431 return true;
432 }
433
434 return false;
435}
436
437QT_END_NAMESPACE
438

source code of qtbase/src/gui/opengl/platform/unix/qglxconvenience.cpp