diff --git a/plugins/platforms/x11/standalone/glxbackend.h b/plugins/platforms/x11/standalone/glxbackend.h --- a/plugins/platforms/x11/standalone/glxbackend.h +++ b/plugins/platforms/x11/standalone/glxbackend.h @@ -62,6 +62,30 @@ }; +// ------------------------------------------------------------------ + + +class AsyncSwapBuffersHandler : public QObject +{ + Q_OBJECT + +public: + AsyncSwapBuffersHandler(Display *dpy, GLXFBConfig config, GLXDrawable drawable, GLXContext shareContext = nullptr); + ~AsyncSwapBuffersHandler() override final; + + void makeCurrent(); + void swapBuffers(GLsync fence); + +Q_SIGNALS: + void swapComplete(std::chrono::microseconds timestamp); + +private: + Display *m_dpy = nullptr; + GLXContext m_context = nullptr; + GLXDrawable m_drawable = None; +}; + + /** * @brief OpenGL Backend using GLX over an X overlay window. */ @@ -120,6 +144,8 @@ bool haveWaitSync = false; Display *m_x11Display; SwapProfiler m_swapProfiler; + AsyncSwapBuffersHandler *m_asyncSwapHandler = nullptr; + QThread *m_asyncSwapThread = nullptr; friend class GlxTexture; }; diff --git a/plugins/platforms/x11/standalone/glxbackend.cpp b/plugins/platforms/x11/standalone/glxbackend.cpp --- a/plugins/platforms/x11/standalone/glxbackend.cpp +++ b/plugins/platforms/x11/standalone/glxbackend.cpp @@ -35,6 +35,7 @@ #include "screens.h" #include "xcbutils.h" #include "texture.h" +#include "abstract_output.h" // kwin libs #include #include @@ -103,6 +104,53 @@ +AsyncSwapBuffersHandler::AsyncSwapBuffersHandler(Display *dpy, GLXFBConfig config, GLXDrawable drawable, GLXContext shareContext) + : QObject(), + m_dpy(dpy), + m_drawable(drawable) +{ + // Create the context we will make current to the other thread + const int attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 2, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + 0 + }; + + m_context = glXCreateContextAttribsARB(m_dpy, config, shareContext, True /*direct*/, attribs); +} + +AsyncSwapBuffersHandler::~AsyncSwapBuffersHandler() +{ + glXDestroyContext(m_dpy, m_context); +} + +void AsyncSwapBuffersHandler::makeCurrent() +{ + glXMakeContextCurrent(m_dpy, m_drawable, m_drawable, m_context); +} + +void AsyncSwapBuffersHandler::swapBuffers(GLsync fence) +{ + const GLenum result = glClientWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + if (result != GL_ALREADY_SIGNALED && result != GL_CONDITION_SATISFIED) { + qCWarning(KWIN_X11STANDALONE) << "glClientWaitSync() before buffer swap failed!"; + } + + glXSwapBuffers(m_dpy, m_drawable); + + const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch(); + const std::chrono::microseconds timestamp = std::chrono::duration_cast(now); + + emit swapComplete(timestamp); + + glDeleteSync(fence); +} + + +// ----------------------------------------------------------------------- + + + GlxBackend::GlxBackend(Display *display) : OpenGLBackend() , m_overlayWindow(kwinApp()->platform()->createOverlayWindow()) @@ -130,6 +178,8 @@ GlxBackend::~GlxBackend() { + m_asyncSwapThread->quit(); + if (isFailed()) { m_overlayWindow->destroy(); } @@ -155,6 +205,10 @@ overlayWindow()->destroy(); delete m_overlayWindow; + + m_asyncSwapThread->wait(); + delete m_asyncSwapHandler; + delete m_asyncSwapThread; } typedef void (*glXFuncPtr)(); @@ -273,6 +327,25 @@ glXQueryDrawable = NULL; } + if (blocksForRetrace()) { + m_asyncSwapHandler = new AsyncSwapBuffersHandler(display(), fbconfig, glxWindow, ctx); + QObject::connect(m_asyncSwapHandler, &AsyncSwapBuffersHandler::swapComplete, [](std::chrono::microseconds timestamp) { + // Update the presentation timestamp in the outputs + const QVector outputs = kwinApp()->platform()->outputs(); + for (AbstractOutput *output : outputs) { + output->setPresentationTimestamp(timestamp); + } + + Compositor::self()->bufferSwapComplete(); + }); + + m_asyncSwapThread = new QThread; + m_asyncSwapHandler->moveToThread(m_asyncSwapThread); + m_asyncSwapThread->start(); + + QMetaObject::invokeMethod(m_asyncSwapHandler, &AsyncSwapBuffersHandler::makeCurrent, Qt::QueuedConnection); + } + setIsDirectRendering(bool(glXIsDirect(display(), ctx))); qCDebug(KWIN_X11STANDALONE) << "Direct rendering:" << isDirectRendering(); @@ -708,15 +781,21 @@ const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion); if (fullRepaint) { - if (m_haveINTELSwapEvent) + if (m_haveINTELSwapEvent || m_asyncSwapHandler) Compositor::self()->aboutToSwapBuffers(); if (haveSwapInterval) { if (gs_tripleBufferNeedsDetection) { glXWaitGL(); m_swapProfiler.begin(); } - glXSwapBuffers(display(), glxWindow); + if (m_asyncSwapHandler) { + GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + QMetaObject::invokeMethod(m_asyncSwapHandler, [=]{ m_asyncSwapHandler->swapBuffers(fence); }, Qt::QueuedConnection); + } else { + glXSwapBuffers(display(), glxWindow); + } if (gs_tripleBufferNeedsDetection) { glXWaitGL(); if (char result = m_swapProfiler.end()) {