File indexing completed on 2024-04-28 05:30:39

0001 /*
0002     SPDX-FileCopyrightText: 2014 Fredrik Höglund <fredrik@kde.org>
0003 
0004     Explicit command stream synchronization based on the sample implementation by James Jones <jajones@nvidia.com>,
0005     SPDX-FileCopyrightText: 2011 NVIDIA Corporation
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "x11syncmanager.h"
0010 #include "compositor.h"
0011 #include "core/outputbackend.h"
0012 #include "core/renderbackend.h"
0013 #include "main.h"
0014 #include "scene/workspacescene.h"
0015 #include "utils/common.h"
0016 
0017 #include "opengl/glplatform.h"
0018 
0019 namespace KWin
0020 {
0021 
0022 X11SyncObject::X11SyncObject()
0023 {
0024     m_state = Ready;
0025 
0026     xcb_connection_t *const connection = kwinApp()->x11Connection();
0027     m_fence = xcb_generate_id(connection);
0028     xcb_sync_create_fence(connection, kwinApp()->x11RootWindow(), m_fence, false);
0029     xcb_flush(connection);
0030 
0031     m_sync = glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, m_fence, 0);
0032 }
0033 
0034 X11SyncObject::~X11SyncObject()
0035 {
0036     xcb_connection_t *const connection = kwinApp()->x11Connection();
0037     // If glDeleteSync is called before the xcb fence is signalled
0038     // the nvidia driver (the only one to implement GL_SYNC_X11_FENCE_EXT)
0039     // deadlocks waiting for the fence to be signalled.
0040     // To avoid this, make sure the fence is signalled before
0041     // deleting the sync.
0042     if (m_state == Resetting || m_state == Ready) {
0043         trigger();
0044         // The flush is necessary!
0045         // The trigger command needs to be sent to the X server.
0046         xcb_flush(connection);
0047     }
0048     xcb_sync_destroy_fence(connection, m_fence);
0049     glDeleteSync(m_sync);
0050 
0051     if (m_state == Resetting) {
0052         xcb_discard_reply(connection, m_reset_cookie.sequence);
0053     }
0054 }
0055 
0056 void X11SyncObject::trigger()
0057 {
0058     Q_ASSERT(m_state == Ready || m_state == Resetting);
0059 
0060     // Finish resetting the fence if necessary
0061     if (m_state == Resetting) {
0062         finishResetting();
0063     }
0064 
0065     xcb_sync_trigger_fence(kwinApp()->x11Connection(), m_fence);
0066     m_state = TriggerSent;
0067 }
0068 
0069 void X11SyncObject::wait()
0070 {
0071     if (m_state != TriggerSent) {
0072         return;
0073     }
0074 
0075     glWaitSync(m_sync, 0, GL_TIMEOUT_IGNORED);
0076     m_state = Waiting;
0077 }
0078 
0079 bool X11SyncObject::finish()
0080 {
0081     if (m_state == Done) {
0082         return true;
0083     }
0084 
0085     // Note: It is possible that we never inserted a wait for the fence.
0086     //       This can happen if we ended up not rendering the damaged
0087     //       window because it is fully occluded.
0088     Q_ASSERT(m_state == TriggerSent || m_state == Waiting);
0089 
0090     // Check if the fence is signaled
0091     GLint value;
0092     glGetSynciv(m_sync, GL_SYNC_STATUS, 1, nullptr, &value);
0093 
0094     if (value != GL_SIGNALED) {
0095         qCDebug(KWIN_CORE) << "Waiting for X fence to finish";
0096 
0097         // Wait for the fence to become signaled with a one second timeout
0098         const GLenum result = glClientWaitSync(m_sync, 0, 1000000000);
0099 
0100         switch (result) {
0101         case GL_TIMEOUT_EXPIRED:
0102             qCWarning(KWIN_CORE) << "Timeout while waiting for X fence";
0103             return false;
0104 
0105         case GL_WAIT_FAILED:
0106             qCWarning(KWIN_CORE) << "glClientWaitSync() failed";
0107             return false;
0108         }
0109     }
0110 
0111     m_state = Done;
0112     return true;
0113 }
0114 
0115 void X11SyncObject::reset()
0116 {
0117     Q_ASSERT(m_state == Done);
0118 
0119     xcb_connection_t *const connection = kwinApp()->x11Connection();
0120 
0121     // Send the reset request along with a sync request.
0122     // We use the cookie to ensure that the server has processed the reset
0123     // request before we trigger the fence and call glWaitSync().
0124     // Otherwise there is a race condition between the reset finishing and
0125     // the glWaitSync() call.
0126     xcb_sync_reset_fence(connection, m_fence);
0127     m_reset_cookie = xcb_get_input_focus(connection);
0128     xcb_flush(connection);
0129 
0130     m_state = Resetting;
0131 }
0132 
0133 void X11SyncObject::finishResetting()
0134 {
0135     Q_ASSERT(m_state == Resetting);
0136     free(xcb_get_input_focus_reply(kwinApp()->x11Connection(), m_reset_cookie, nullptr));
0137     m_state = Ready;
0138 }
0139 
0140 X11SyncManager *X11SyncManager::create()
0141 {
0142     if (kwinApp()->operationMode() != Application::OperationModeX11) {
0143         return nullptr;
0144     }
0145 
0146     if (Compositor::self()->backend()->compositingType() != OpenGLCompositing) {
0147         return nullptr;
0148     }
0149 
0150     GLPlatform *glPlatform = GLPlatform::instance();
0151     const bool haveSyncObjects = glPlatform->isGLES()
0152         ? hasGLVersion(3, 0)
0153         : hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync");
0154 
0155     if (hasGLExtension("GL_EXT_x11_sync_object") && haveSyncObjects) {
0156         const QString useExplicitSync = qEnvironmentVariable("KWIN_EXPLICIT_SYNC");
0157 
0158         if (useExplicitSync != QLatin1String("0")) {
0159             qCDebug(KWIN_CORE) << "Initializing fences for synchronization with the X command stream";
0160             return new X11SyncManager;
0161         } else {
0162             qCDebug(KWIN_CORE) << "Explicit synchronization with the X command stream disabled by environment variable";
0163         }
0164     }
0165     return nullptr;
0166 }
0167 
0168 X11SyncManager::X11SyncManager()
0169 {
0170     for (int i = 0; i < MaxFences; ++i) {
0171         m_fences.append(new X11SyncObject);
0172     }
0173 }
0174 
0175 X11SyncManager::~X11SyncManager()
0176 {
0177     Compositor::self()->scene()->makeOpenGLContextCurrent();
0178     qDeleteAll(m_fences);
0179 }
0180 
0181 bool X11SyncManager::endFrame()
0182 {
0183     if (!m_currentFence) {
0184         return true;
0185     }
0186 
0187     for (int i = 0; i < std::min<int>(2, m_fences.count() - 1); i++) {
0188         const int index = (m_next + i) % m_fences.count();
0189         X11SyncObject *fence = m_fences[index];
0190 
0191         switch (fence->state()) {
0192         case X11SyncObject::Ready:
0193             break;
0194 
0195         case X11SyncObject::TriggerSent:
0196         case X11SyncObject::Waiting:
0197             if (!fence->finish()) {
0198                 return false;
0199             }
0200             fence->reset();
0201             break;
0202 
0203         // Should not happen in practice since we always reset the fence after finishing it
0204         case X11SyncObject::Done:
0205             fence->reset();
0206             break;
0207 
0208         case X11SyncObject::Resetting:
0209             fence->finishResetting();
0210             break;
0211         }
0212     }
0213 
0214     m_currentFence = nullptr;
0215     return true;
0216 }
0217 
0218 void X11SyncManager::triggerFence()
0219 {
0220     m_currentFence = m_fences[m_next];
0221     m_next = (m_next + 1) % m_fences.count();
0222     m_currentFence->trigger();
0223 }
0224 
0225 void X11SyncManager::insertWait()
0226 {
0227     if (m_currentFence && m_currentFence->state() != X11SyncObject::Waiting) {
0228         m_currentFence->wait();
0229     }
0230 }
0231 
0232 } // namespace KWin