1// Copyright (C) 2016 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#include "qxcbeglintegration.h"
5
6#include "qxcbeglcontext.h"
7
8#include <QtGui/QOffscreenSurface>
9#include <QtGui/private/qeglconvenience_p.h>
10#include <QtGui/private/qeglstreamconvenience_p.h>
11#include <optional>
12
13#include "qxcbeglnativeinterfacehandler.h"
14
15QT_BEGIN_NAMESPACE
16
17namespace {
18
19struct VisualInfo
20{
21 xcb_visualtype_t visualType;
22 uint8_t depth;
23};
24
25std::optional<VisualInfo> getVisualInfo(xcb_screen_t *screen,
26 std::optional<xcb_visualid_t> requestedVisualId,
27 std::optional<uint8_t> requestedDepth = std::nullopt)
28{
29 xcb_depth_iterator_t depthIterator = xcb_screen_allowed_depths_iterator(R: screen);
30
31 while (depthIterator.rem) {
32 xcb_depth_t *depth = depthIterator.data;
33 xcb_visualtype_iterator_t visualTypeIterator = xcb_depth_visuals_iterator(R: depth);
34
35 while (visualTypeIterator.rem) {
36 xcb_visualtype_t *visualType = visualTypeIterator.data;
37 if (requestedVisualId && visualType->visual_id != *requestedVisualId) {
38 xcb_visualtype_next(i: &visualTypeIterator);
39 continue;
40 }
41
42 if (requestedDepth && depth->depth != *requestedDepth) {
43 xcb_visualtype_next(i: &visualTypeIterator);
44 continue;
45 }
46
47 return VisualInfo{ .visualType: *visualType, .depth: depth->depth };
48 }
49
50 xcb_depth_next(i: &depthIterator);
51 }
52
53 return std::nullopt;
54}
55
56} // namespace
57
58QXcbEglIntegration::QXcbEglIntegration()
59 : m_connection(nullptr)
60 , m_egl_display(EGL_NO_DISPLAY)
61{
62 qCDebug(lcQpaGl) << "Xcb EGL gl-integration created";
63}
64
65QXcbEglIntegration::~QXcbEglIntegration()
66{
67 if (m_egl_display != EGL_NO_DISPLAY)
68 eglTerminate(dpy: m_egl_display);
69}
70
71bool QXcbEglIntegration::initialize(QXcbConnection *connection)
72{
73 m_connection = connection;
74
75 const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
76
77#if QT_CONFIG(xcb_xlib)
78 if (extensions && strstr(haystack: extensions, needle: "EGL_EXT_platform_x11")) {
79 QEGLStreamConvenience streamFuncs;
80 m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR,
81 m_connection->xlib_display(),
82 nullptr);
83 }
84
85#if QT_CONFIG(egl_x11)
86 if (!m_egl_display)
87 m_egl_display = eglGetDisplay(display_id: reinterpret_cast<EGLNativeDisplayType>(m_connection->xlib_display()));
88#endif
89#else
90 if (extensions && (strstr(extensions, "EGL_EXT_platform_xcb") || strstr(extensions, "EGL_MESA_platform_xcb"))) {
91 QEGLStreamConvenience streamFuncs;
92 m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_XCB_KHR,
93 reinterpret_cast<void *>(connection->xcb_connection()),
94 nullptr);
95 }
96#endif
97
98 EGLint major, minor;
99 bool success = eglInitialize(dpy: m_egl_display, major: &major, minor: &minor);
100#if QT_CONFIG(egl_x11)
101 if (!success) {
102 m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
103 qCDebug(lcQpaGl) << "Xcb EGL gl-integration retrying with display" << m_egl_display;
104 success = eglInitialize(dpy: m_egl_display, major: &major, minor: &minor);
105 }
106#endif
107
108 m_native_interface_handler.reset(other: new QXcbEglNativeInterfaceHandler(connection->nativeInterface()));
109
110 if (success)
111 qCDebug(lcQpaGl) << "Xcb EGL gl-integration successfully initialized";
112 else
113 qCWarning(lcQpaGl) << "Xcb EGL gl-integration initialize failed";
114
115 return success;
116}
117
118QXcbWindow *QXcbEglIntegration::createWindow(QWindow *window) const
119{
120 return new QXcbEglWindow(window, const_cast<QXcbEglIntegration *>(this));
121}
122
123QPlatformOpenGLContext *QXcbEglIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
124{
125 QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle());
126 QXcbEglContext *platformContext = new QXcbEglContext(screen->surfaceFormatFor(format: context->format()),
127 context->shareHandle(),
128 eglDisplay());
129 return platformContext;
130}
131
132QOpenGLContext *QXcbEglIntegration::createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const
133{
134 return QEGLPlatformContext::createFrom<QXcbEglContext>(context, contextDisplay: display, platformDisplay: eglDisplay(), shareContext);
135}
136
137QPlatformOffscreenSurface *QXcbEglIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
138{
139 QXcbScreen *screen = static_cast<QXcbScreen *>(surface->screen()->handle());
140 return new QEGLPbuffer(eglDisplay(), screen->surfaceFormatFor(format: surface->requestedFormat()), surface);
141}
142
143xcb_visualid_t QXcbEglIntegration::getCompatibleVisualId(xcb_screen_t *screen, EGLConfig config) const
144{
145 xcb_visualid_t visualId = 0;
146 EGLint eglValue = 0;
147
148 EGLint configRedSize = 0;
149 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_RED_SIZE, value: &configRedSize);
150
151 EGLint configGreenSize = 0;
152 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_GREEN_SIZE, value: &configGreenSize);
153
154 EGLint configBlueSize = 0;
155 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_BLUE_SIZE, value: &configBlueSize);
156
157 EGLint configAlphaSize = 0;
158 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_ALPHA_SIZE, value: &configAlphaSize);
159
160 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_CONFIG_ID, value: &eglValue);
161 int configId = eglValue;
162
163 // See if EGL provided a valid VisualID:
164 eglGetConfigAttrib(dpy: eglDisplay(), config, EGL_NATIVE_VISUAL_ID, value: &eglValue);
165 visualId = eglValue;
166 if (visualId) {
167 // EGL has suggested a visual id, so get the rest of the visual info for that id:
168 std::optional<VisualInfo> chosenVisualInfo = getVisualInfo(screen, requestedVisualId: visualId);
169 if (chosenVisualInfo) {
170 // Skip size checks if implementation supports non-matching visual
171 // and config (QTBUG-9444).
172 if (q_hasEglExtension(display: eglDisplay(), extensionName: "EGL_NV_post_convert_rounding"))
173 return visualId;
174 // Skip also for i.MX6 where 565 visuals are suggested for the default 444 configs and it works just fine.
175 const char *vendor = eglQueryString(dpy: eglDisplay(), EGL_VENDOR);
176 if (vendor && strstr(haystack: vendor, needle: "Vivante"))
177 return visualId;
178
179 int visualRedSize = qPopulationCount(v: chosenVisualInfo->visualType.red_mask);
180 int visualGreenSize = qPopulationCount(v: chosenVisualInfo->visualType.green_mask);
181 int visualBlueSize = qPopulationCount(v: chosenVisualInfo->visualType.blue_mask);
182 int visualAlphaSize = chosenVisualInfo->depth - visualRedSize - visualBlueSize - visualGreenSize;
183
184 const bool visualMatchesConfig = visualRedSize >= configRedSize
185 && visualGreenSize >= configGreenSize
186 && visualBlueSize >= configBlueSize
187 && visualAlphaSize >= configAlphaSize;
188
189 // In some cases EGL tends to suggest a 24-bit visual for 8888
190 // configs. In such a case we have to fall back to getVisualInfo.
191 if (!visualMatchesConfig) {
192 visualId = 0;
193 qCDebug(lcQpaGl,
194 "EGL suggested using X Visual ID %d (%d %d %d %d depth %d) for EGL config %d"
195 "(%d %d %d %d), but this is incompatible",
196 visualId, visualRedSize, visualGreenSize, visualBlueSize, visualAlphaSize, chosenVisualInfo->depth,
197 configId, configRedSize, configGreenSize, configBlueSize, configAlphaSize);
198 }
199 } else {
200 qCDebug(lcQpaGl, "EGL suggested using X Visual ID %d for EGL config %d, but that isn't a valid ID",
201 visualId, configId);
202 visualId = 0;
203 }
204 }
205 else
206 qCDebug(lcQpaGl, "EGL did not suggest a VisualID (EGL_NATIVE_VISUAL_ID was zero) for EGLConfig %d", configId);
207
208 if (visualId) {
209 qCDebug(lcQpaGl, configAlphaSize > 0
210 ? "Using ARGB Visual ID %d provided by EGL for config %d"
211 : "Using Opaque Visual ID %d provided by EGL for config %d", visualId, configId);
212 return visualId;
213 }
214
215 // Finally, try to use getVisualInfo and only use the bit depths to match on:
216 if (!visualId) {
217 uint8_t depth = configRedSize + configGreenSize + configBlueSize + configAlphaSize;
218 std::optional<VisualInfo> matchingVisual = getVisualInfo(screen, requestedVisualId: std::nullopt, requestedDepth: depth);
219 if (!matchingVisual) {
220 // Try again without taking the alpha channel into account:
221 depth = configRedSize + configGreenSize + configBlueSize;
222 matchingVisual = getVisualInfo(screen, requestedVisualId: std::nullopt, requestedDepth: depth);
223 }
224
225 if (matchingVisual)
226 visualId = matchingVisual->visualType.visual_id;
227 }
228
229 if (visualId) {
230 qCDebug(lcQpaGl, "Using Visual ID %d provided by getVisualInfo for EGL config %d", visualId, configId);
231 return visualId;
232 }
233
234 qWarning(msg: "Unable to find an X11 visual which matches EGL config %d", configId);
235 return 0;
236}
237
238QT_END_NAMESPACE
239

source code of qtbase/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp