File indexing completed on 2024-11-10 04:58:04
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