kwin/plugins/platforms/x11/standalone/sgivideosyncvsyncmonitor.cpp

164 lines
4.6 KiB
C++
Raw Normal View History

2020-11-19 08:52:29 +00:00
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "sgivideosyncvsyncmonitor.h"
#include "glxconvenience.h"
2020-11-19 08:52:29 +00:00
#include "logging.h"
#include <QX11Info>
namespace KWin
{
SGIVideoSyncVsyncMonitor *SGIVideoSyncVsyncMonitor::create(QObject *parent)
{
const char *extensions = glXQueryExtensionsString(QX11Info::display(),
QX11Info::appScreen());
if (!strstr(extensions, "GLX_SGI_video_sync")) {
return nullptr; // GLX_SGI_video_sync is unsupported.
}
SGIVideoSyncVsyncMonitor *monitor = new SGIVideoSyncVsyncMonitor(parent);
if (monitor->isValid()) {
return monitor;
}
delete monitor;
return nullptr;
}
SGIVideoSyncVsyncMonitorHelper::SGIVideoSyncVsyncMonitorHelper(QObject *parent)
: QObject(parent)
{
// Establish a new X11 connection to avoid locking up the main X11 connection.
m_display = XOpenDisplay(DisplayString(QX11Info::display()));
if (!m_display) {
qCDebug(KWIN_X11STANDALONE) << "Failed to establish vsync monitor X11 connection";
return;
}
Window rootWindow = DefaultRootWindow(m_display);
2020-11-19 08:52:29 +00:00
const int attribs[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0
};
GLXFBConfig config = chooseGlxFbConfig(m_display, attribs);
if (!config) {
2020-11-19 08:52:29 +00:00
qCDebug(KWIN_X11STANDALONE) << "Couldn't find any suitable FBConfig for vsync monitor";
return;
}
XVisualInfo *visualInfo = glXGetVisualFromFBConfig(m_display, config);
if (!visualInfo) {
return;
}
Visual *visual = visualInfo->visual;
const int depth = visualInfo->depth;
XFree(visualInfo);
Colormap colormap = XCreateColormap(m_display, rootWindow, visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
m_dummyWindow = XCreateWindow(m_display, rootWindow, 0, 0, 1, 1, 0, depth,
InputOutput, visual, CWColormap, &attributes);
XFreeColormap(m_display, colormap);
if (!m_dummyWindow) {
qCDebug(KWIN_X11STANDALONE) << "Failed to create a dummy window for vsync monitor";
return;
}
m_drawable = glXCreateWindow(m_display, config, m_dummyWindow, nullptr);
if (!m_drawable) {
qCDebug(KWIN_X11STANDALONE) << "Failed to create GLXWindow for dummy window";
return;
}
2020-11-19 08:52:29 +00:00
m_localContext = glXCreateNewContext(m_display, config, GLX_RGBA_TYPE, 0, true);
if (!m_localContext) {
qCDebug(KWIN_X11STANDALONE) << "Failed to create opengl context for vsync monitor";
return;
}
}
SGIVideoSyncVsyncMonitorHelper::~SGIVideoSyncVsyncMonitorHelper()
{
if (m_localContext) {
glXDestroyContext(m_display, m_localContext);
}
if (m_drawable) {
glXDestroyWindow(m_display, m_drawable);
}
if (m_dummyWindow) {
XDestroyWindow(m_display, m_dummyWindow);
}
2020-11-19 08:52:29 +00:00
if (m_display) {
XCloseDisplay(m_display);
}
}
bool SGIVideoSyncVsyncMonitorHelper::isValid() const
{
return m_display && m_localContext && m_drawable;
}
void SGIVideoSyncVsyncMonitorHelper::poll()
{
if (!glXMakeCurrent(m_display, m_drawable, m_localContext)) {
qCDebug(KWIN_X11STANDALONE) << "Failed to make vsync monitor OpenGL context current";
emit errorOccurred();
return;
}
uint count;
glXGetVideoSyncSGI(&count);
glXWaitVideoSyncSGI(2, (count + 1) % 2, &count);
// Using monotonic clock is inaccurate, but it's still a pretty good estimate.
emit vblankOccurred(std::chrono::steady_clock::now().time_since_epoch());
}
SGIVideoSyncVsyncMonitor::SGIVideoSyncVsyncMonitor(QObject *parent)
: VsyncMonitor(parent)
, m_thread(new QThread)
, m_helper(new SGIVideoSyncVsyncMonitorHelper)
{
m_helper->moveToThread(m_thread);
connect(m_helper, &SGIVideoSyncVsyncMonitorHelper::errorOccurred,
this, &SGIVideoSyncVsyncMonitor::errorOccurred);
connect(m_helper, &SGIVideoSyncVsyncMonitorHelper::vblankOccurred,
this, &SGIVideoSyncVsyncMonitor::vblankOccurred);
m_thread->setObjectName(QStringLiteral("vsync event monitor"));
m_thread->start();
}
SGIVideoSyncVsyncMonitor::~SGIVideoSyncVsyncMonitor()
{
m_thread->quit();
m_thread->wait();
delete m_helper;
delete m_thread;
}
bool SGIVideoSyncVsyncMonitor::isValid() const
{
return m_helper->isValid();
}
void SGIVideoSyncVsyncMonitor::arm()
{
QMetaObject::invokeMethod(m_helper, &SGIVideoSyncVsyncMonitorHelper::poll);
}
} // namespace KWin