// Copyright (C) 2016 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 #include "qxcbeglintegration.h" #include "qxcbeglcontext.h" #include #include #include #include #include "qxcbeglnativeinterfacehandler.h" QT_BEGIN_NAMESPACE namespace { struct VisualInfo { xcb_visualtype_t visualType; uint8_t depth; }; std::optional getVisualInfo(xcb_screen_t *screen, std::optional requestedVisualId, std::optional requestedDepth = std::nullopt) { xcb_depth_iterator_t depthIterator = xcb_screen_allowed_depths_iterator(screen); while (depthIterator.rem) { xcb_depth_t *depth = depthIterator.data; xcb_visualtype_iterator_t visualTypeIterator = xcb_depth_visuals_iterator(depth); while (visualTypeIterator.rem) { xcb_visualtype_t *visualType = visualTypeIterator.data; if (requestedVisualId && visualType->visual_id != *requestedVisualId) { xcb_visualtype_next(&visualTypeIterator); continue; } if (requestedDepth && depth->depth != *requestedDepth) { xcb_visualtype_next(&visualTypeIterator); continue; } return VisualInfo{ *visualType, depth->depth }; } xcb_depth_next(&depthIterator); } return std::nullopt; } } // namespace QXcbEglIntegration::QXcbEglIntegration() : m_connection(nullptr) , m_egl_display(EGL_NO_DISPLAY) { qCDebug(lcQpaGl) << "Xcb EGL gl-integration created"; } QXcbEglIntegration::~QXcbEglIntegration() { if (m_egl_display != EGL_NO_DISPLAY) eglTerminate(m_egl_display); } bool QXcbEglIntegration::initialize(QXcbConnection *connection) { m_connection = connection; const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) { QEGLStreamConvenience streamFuncs; m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR, xlib_display(), nullptr); } if (!m_egl_display) m_egl_display = eglGetDisplay(reinterpret_cast(xlib_display())); EGLint major, minor; bool success = eglInitialize(m_egl_display, &major, &minor); if (!success) { m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); qCDebug(lcQpaGl) << "Xcb EGL gl-integration retrying with display" << m_egl_display; success = eglInitialize(m_egl_display, &major, &minor); } m_native_interface_handler.reset(new QXcbEglNativeInterfaceHandler(connection->nativeInterface())); if (success) qCDebug(lcQpaGl) << "Xcb EGL gl-integration successfully initialized"; else qCWarning(lcQpaGl) << "Xcb EGL gl-integration initialize failed"; return success; } QXcbWindow *QXcbEglIntegration::createWindow(QWindow *window) const { return new QXcbEglWindow(window, const_cast(this)); } QPlatformOpenGLContext *QXcbEglIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { QXcbScreen *screen = static_cast(context->screen()->handle()); QXcbEglContext *platformContext = new QXcbEglContext(screen->surfaceFormatFor(context->format()), context->shareHandle(), eglDisplay()); return platformContext; } QOpenGLContext *QXcbEglIntegration::createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const { return QEGLPlatformContext::createFrom(context, display, eglDisplay(), shareContext); } QPlatformOffscreenSurface *QXcbEglIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const { QXcbScreen *screen = static_cast(surface->screen()->handle()); return new QEGLPbuffer(eglDisplay(), screen->surfaceFormatFor(surface->requestedFormat()), surface); } void *QXcbEglIntegration::xlib_display() const { #if QT_CONFIG(xcb_xlib) return m_connection->xlib_display(); #else return EGL_DEFAULT_DISPLAY; #endif } xcb_visualid_t QXcbEglIntegration::getCompatibleVisualId(xcb_screen_t *screen, EGLConfig config) const { xcb_visualid_t visualId = 0; EGLint eglValue = 0; EGLint configRedSize = 0; eglGetConfigAttrib(eglDisplay(), config, EGL_RED_SIZE, &configRedSize); EGLint configGreenSize = 0; eglGetConfigAttrib(eglDisplay(), config, EGL_GREEN_SIZE, &configGreenSize); EGLint configBlueSize = 0; eglGetConfigAttrib(eglDisplay(), config, EGL_BLUE_SIZE, &configBlueSize); EGLint configAlphaSize = 0; eglGetConfigAttrib(eglDisplay(), config, EGL_ALPHA_SIZE, &configAlphaSize); eglGetConfigAttrib(eglDisplay(), config, EGL_CONFIG_ID, &eglValue); int configId = eglValue; // See if EGL provided a valid VisualID: eglGetConfigAttrib(eglDisplay(), config, EGL_NATIVE_VISUAL_ID, &eglValue); visualId = eglValue; if (visualId) { // EGL has suggested a visual id, so get the rest of the visual info for that id: std::optional chosenVisualInfo = getVisualInfo(screen, visualId); if (chosenVisualInfo) { // Skip size checks if implementation supports non-matching visual // and config (QTBUG-9444). if (q_hasEglExtension(eglDisplay(), "EGL_NV_post_convert_rounding")) return visualId; // Skip also for i.MX6 where 565 visuals are suggested for the default 444 configs and it works just fine. const char *vendor = eglQueryString(eglDisplay(), EGL_VENDOR); if (vendor && strstr(vendor, "Vivante")) return visualId; int visualRedSize = qPopulationCount(chosenVisualInfo->visualType.red_mask); int visualGreenSize = qPopulationCount(chosenVisualInfo->visualType.green_mask); int visualBlueSize = qPopulationCount(chosenVisualInfo->visualType.blue_mask); int visualAlphaSize = chosenVisualInfo->depth - visualRedSize - visualBlueSize - visualGreenSize; const bool visualMatchesConfig = visualRedSize >= configRedSize && visualGreenSize >= configGreenSize && visualBlueSize >= configBlueSize && visualAlphaSize >= configAlphaSize; // In some cases EGL tends to suggest a 24-bit visual for 8888 // configs. In such a case we have to fall back to getVisualInfo. if (!visualMatchesConfig) { visualId = 0; qCDebug(lcQpaGl, "EGL suggested using X Visual ID %d (%d %d %d %d depth %d) for EGL config %d" "(%d %d %d %d), but this is incompatible", visualId, visualRedSize, visualGreenSize, visualBlueSize, visualAlphaSize, chosenVisualInfo->depth, configId, configRedSize, configGreenSize, configBlueSize, configAlphaSize); } } else { qCDebug(lcQpaGl, "EGL suggested using X Visual ID %d for EGL config %d, but that isn't a valid ID", visualId, configId); visualId = 0; } } else qCDebug(lcQpaGl, "EGL did not suggest a VisualID (EGL_NATIVE_VISUAL_ID was zero) for EGLConfig %d", configId); if (visualId) { qCDebug(lcQpaGl, configAlphaSize > 0 ? "Using ARGB Visual ID %d provided by EGL for config %d" : "Using Opaque Visual ID %d provided by EGL for config %d", visualId, configId); return visualId; } // Finally, try to use getVisualInfo and only use the bit depths to match on: if (!visualId) { uint8_t depth = configRedSize + configGreenSize + configBlueSize + configAlphaSize; std::optional matchingVisual = getVisualInfo(screen, std::nullopt, depth); if (!matchingVisual) { // Try again without taking the alpha channel into account: depth = configRedSize + configGreenSize + configBlueSize; matchingVisual = getVisualInfo(screen, std::nullopt, depth); } if (matchingVisual) visualId = matchingVisual->visualType.visual_id; } if (visualId) { qCDebug(lcQpaGl, "Using Visual ID %d provided by getVisualInfo for EGL config %d", visualId, configId); return visualId; } qWarning("Unable to find an X11 visual which matches EGL config %d", configId); return 0; } QT_END_NAMESPACE