File indexing completed on 2025-04-27 11:33:00

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "x11_standalone_omlsynccontrolvsyncmonitor.h"
0008 #include "x11_standalone_glxconvenience.h"
0009 #include "x11_standalone_logging.h"
0010 
0011 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0012 #include <private/qtx11extras_p.h>
0013 #else
0014 #include <QX11Info>
0015 #endif
0016 
0017 namespace KWin
0018 {
0019 
0020 std::unique_ptr<OMLSyncControlVsyncMonitor> OMLSyncControlVsyncMonitor::create()
0021 {
0022     const char *extensions = glXQueryExtensionsString(QX11Info::display(),
0023                                                       QX11Info::appScreen());
0024     if (!strstr(extensions, "GLX_OML_sync_control")) {
0025         return nullptr; // GLX_OML_sync_control is unsupported.
0026     }
0027 
0028     std::unique_ptr<OMLSyncControlVsyncMonitor> monitor{new OMLSyncControlVsyncMonitor()};
0029     if (monitor->isValid()) {
0030         return monitor;
0031     } else {
0032         return nullptr;
0033     }
0034 }
0035 
0036 OMLSyncControlVsyncMonitorHelper::OMLSyncControlVsyncMonitorHelper()
0037 {
0038     // Establish a new X11 connection to avoid locking up the main X11 connection.
0039     m_display = XOpenDisplay(DisplayString(QX11Info::display()));
0040     if (!m_display) {
0041         qCDebug(KWIN_X11STANDALONE) << "Failed to establish vsync monitor X11 connection";
0042         return;
0043     }
0044 
0045     ::Window rootWindow = DefaultRootWindow(m_display);
0046 
0047     const int attribs[] = {
0048         GLX_RENDER_TYPE, GLX_RGBA_BIT,
0049         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0050         0};
0051 
0052     GLXFBConfig config = chooseGlxFbConfig(m_display, attribs);
0053     if (!config) {
0054         qCDebug(KWIN_X11STANDALONE) << "Couldn't find any suitable FBConfig for vsync monitor";
0055         return;
0056     }
0057 
0058     XVisualInfo *visualInfo = glXGetVisualFromFBConfig(m_display, config);
0059     if (!visualInfo) {
0060         return;
0061     }
0062 
0063     Visual *visual = visualInfo->visual;
0064     const int depth = visualInfo->depth;
0065     XFree(visualInfo);
0066 
0067     Colormap colormap = XCreateColormap(m_display, rootWindow, visual, AllocNone);
0068     XSetWindowAttributes attributes;
0069     attributes.colormap = colormap;
0070 
0071     m_dummyWindow = XCreateWindow(m_display, rootWindow, 0, 0, 1, 1, 0, depth,
0072                                   InputOutput, visual, CWColormap, &attributes);
0073     XFreeColormap(m_display, colormap);
0074     if (!m_dummyWindow) {
0075         qCDebug(KWIN_X11STANDALONE) << "Failed to create a dummy window for vsync monitor";
0076         return;
0077     }
0078 
0079     m_drawable = glXCreateWindow(m_display, config, m_dummyWindow, nullptr);
0080     if (!m_drawable) {
0081         qCDebug(KWIN_X11STANDALONE) << "Failed to create GLXWindow for dummy window";
0082         return;
0083     }
0084 
0085     m_localContext = glXCreateNewContext(m_display, config, GLX_RGBA_TYPE, 0, true);
0086     if (!m_localContext) {
0087         qCDebug(KWIN_X11STANDALONE) << "Failed to create opengl context for vsync monitor";
0088         return;
0089     }
0090 }
0091 
0092 OMLSyncControlVsyncMonitorHelper::~OMLSyncControlVsyncMonitorHelper()
0093 {
0094     if (m_localContext) {
0095         glXDestroyContext(m_display, m_localContext);
0096     }
0097     if (m_drawable) {
0098         glXDestroyWindow(m_display, m_drawable);
0099     }
0100     if (m_dummyWindow) {
0101         XDestroyWindow(m_display, m_dummyWindow);
0102     }
0103     if (m_display) {
0104         XCloseDisplay(m_display);
0105     }
0106 }
0107 
0108 bool OMLSyncControlVsyncMonitorHelper::isValid() const
0109 {
0110     return m_display && m_localContext && m_drawable;
0111 }
0112 
0113 void OMLSyncControlVsyncMonitorHelper::poll()
0114 {
0115     if (!glXMakeCurrent(m_display, m_drawable, m_localContext)) {
0116         qCDebug(KWIN_X11STANDALONE) << "Failed to make vsync monitor OpenGL context current";
0117         return;
0118     }
0119 
0120     int64_t ust, msc, sbc;
0121 
0122     glXGetSyncValuesOML(m_display, m_drawable, &ust, &msc, &sbc);
0123     glXWaitForMscOML(m_display, m_drawable, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
0124 
0125     Q_EMIT vblankOccurred(std::chrono::microseconds(ust));
0126 }
0127 
0128 OMLSyncControlVsyncMonitor::OMLSyncControlVsyncMonitor()
0129 {
0130     m_helper.moveToThread(&m_thread);
0131 
0132     connect(&m_helper, &OMLSyncControlVsyncMonitorHelper::errorOccurred,
0133             this, &OMLSyncControlVsyncMonitor::errorOccurred);
0134     connect(&m_helper, &OMLSyncControlVsyncMonitorHelper::vblankOccurred,
0135             this, &OMLSyncControlVsyncMonitor::vblankOccurred);
0136 
0137     m_thread.setObjectName(QStringLiteral("vsync event monitor"));
0138     m_thread.start();
0139 }
0140 
0141 OMLSyncControlVsyncMonitor::~OMLSyncControlVsyncMonitor()
0142 {
0143     m_thread.quit();
0144     m_thread.wait();
0145 }
0146 
0147 bool OMLSyncControlVsyncMonitor::isValid() const
0148 {
0149     return m_helper.isValid();
0150 }
0151 
0152 void OMLSyncControlVsyncMonitor::arm()
0153 {
0154     QMetaObject::invokeMethod(&m_helper, &OMLSyncControlVsyncMonitorHelper::poll);
0155 }
0156 
0157 } // namespace KWin