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