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"