File indexing completed on 2024-11-10 04:56:36

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