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