File indexing completed on 2025-04-27 11:33:01
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_sgivideosyncvsyncmonitor.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<SGIVideoSyncVsyncMonitor> SGIVideoSyncVsyncMonitor::create() 0021 { 0022 const char *extensions = glXQueryExtensionsString(QX11Info::display(), 0023 QX11Info::appScreen()); 0024 if (!strstr(extensions, "GLX_SGI_video_sync")) { 0025 return nullptr; // GLX_SGI_video_sync is unsupported. 0026 } 0027 0028 std::unique_ptr<SGIVideoSyncVsyncMonitor> monitor{new SGIVideoSyncVsyncMonitor()}; 0029 if (monitor->isValid()) { 0030 return monitor; 0031 } else { 0032 return nullptr; 0033 } 0034 } 0035 0036 SGIVideoSyncVsyncMonitorHelper::SGIVideoSyncVsyncMonitorHelper() 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 SGIVideoSyncVsyncMonitorHelper::~SGIVideoSyncVsyncMonitorHelper() 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 SGIVideoSyncVsyncMonitorHelper::isValid() const 0109 { 0110 return m_display && m_localContext && m_drawable; 0111 } 0112 0113 void SGIVideoSyncVsyncMonitorHelper::poll() 0114 { 0115 if (!glXMakeCurrent(m_display, m_drawable, m_localContext)) { 0116 qCDebug(KWIN_X11STANDALONE) << "Failed to make vsync monitor OpenGL context current"; 0117 Q_EMIT errorOccurred(); 0118 return; 0119 } 0120 0121 uint count; 0122 0123 glXGetVideoSyncSGI(&count); 0124 glXWaitVideoSyncSGI(2, (count + 1) % 2, &count); 0125 0126 // Using monotonic clock is inaccurate, but it's still a pretty good estimate. 0127 Q_EMIT vblankOccurred(std::chrono::steady_clock::now().time_since_epoch()); 0128 } 0129 0130 SGIVideoSyncVsyncMonitor::SGIVideoSyncVsyncMonitor() 0131 { 0132 m_helper.moveToThread(&m_thread); 0133 0134 connect(&m_helper, &SGIVideoSyncVsyncMonitorHelper::errorOccurred, 0135 this, &SGIVideoSyncVsyncMonitor::errorOccurred); 0136 connect(&m_helper, &SGIVideoSyncVsyncMonitorHelper::vblankOccurred, 0137 this, &SGIVideoSyncVsyncMonitor::vblankOccurred); 0138 0139 m_thread.setObjectName(QStringLiteral("vsync event monitor")); 0140 m_thread.start(); 0141 } 0142 0143 SGIVideoSyncVsyncMonitor::~SGIVideoSyncVsyncMonitor() 0144 { 0145 m_thread.quit(); 0146 m_thread.wait(); 0147 } 0148 0149 bool SGIVideoSyncVsyncMonitor::isValid() const 0150 { 0151 return m_helper.isValid(); 0152 } 0153 0154 void SGIVideoSyncVsyncMonitor::arm() 0155 { 0156 QMetaObject::invokeMethod(&m_helper, &SGIVideoSyncVsyncMonitorHelper::poll); 0157 } 0158 0159 } // namespace KWin