Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Fredrik Höglund <fredrik@kde.org>
|
||||
|
||||
Explicit command stream synchronization based on the sample implementation by James Jones <jajones@nvidia.com>,
|
||||
SPDX-FileCopyrightText: 2011 NVIDIA Corporation
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "x11syncmanager.h"
|
||||
#include "compositor.h"
|
||||
#include "core/outputbackend.h"
|
||||
#include "core/renderbackend.h"
|
||||
#include "main.h"
|
||||
#include "scene/workspacescene.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include "opengl/glplatform.h"
|
||||
#include "platformsupport/scenes/opengl/openglbackend.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
X11SyncObject::X11SyncObject()
|
||||
{
|
||||
m_state = Ready;
|
||||
|
||||
xcb_connection_t *const connection = kwinApp()->x11Connection();
|
||||
m_fence = xcb_generate_id(connection);
|
||||
xcb_sync_create_fence(connection, kwinApp()->x11RootWindow(), m_fence, false);
|
||||
xcb_flush(connection);
|
||||
|
||||
m_sync = glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, m_fence, 0);
|
||||
}
|
||||
|
||||
X11SyncObject::~X11SyncObject()
|
||||
{
|
||||
xcb_connection_t *const connection = kwinApp()->x11Connection();
|
||||
// If glDeleteSync is called before the xcb fence is signalled
|
||||
// the nvidia driver (the only one to implement GL_SYNC_X11_FENCE_EXT)
|
||||
// deadlocks waiting for the fence to be signalled.
|
||||
// To avoid this, make sure the fence is signalled before
|
||||
// deleting the sync.
|
||||
if (m_state == Resetting || m_state == Ready) {
|
||||
trigger();
|
||||
// The flush is necessary!
|
||||
// The trigger command needs to be sent to the X server.
|
||||
xcb_flush(connection);
|
||||
}
|
||||
xcb_sync_destroy_fence(connection, m_fence);
|
||||
glDeleteSync(m_sync);
|
||||
|
||||
if (m_state == Resetting) {
|
||||
xcb_discard_reply(connection, m_reset_cookie.sequence);
|
||||
}
|
||||
}
|
||||
|
||||
void X11SyncObject::trigger()
|
||||
{
|
||||
Q_ASSERT(m_state == Ready || m_state == Resetting);
|
||||
|
||||
// Finish resetting the fence if necessary
|
||||
if (m_state == Resetting) {
|
||||
finishResetting();
|
||||
}
|
||||
|
||||
xcb_sync_trigger_fence(kwinApp()->x11Connection(), m_fence);
|
||||
m_state = TriggerSent;
|
||||
}
|
||||
|
||||
void X11SyncObject::wait()
|
||||
{
|
||||
if (m_state != TriggerSent) {
|
||||
return;
|
||||
}
|
||||
|
||||
glWaitSync(m_sync, 0, GL_TIMEOUT_IGNORED);
|
||||
m_state = Waiting;
|
||||
}
|
||||
|
||||
bool X11SyncObject::finish()
|
||||
{
|
||||
if (m_state == Done) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: It is possible that we never inserted a wait for the fence.
|
||||
// This can happen if we ended up not rendering the damaged
|
||||
// window because it is fully occluded.
|
||||
Q_ASSERT(m_state == TriggerSent || m_state == Waiting);
|
||||
|
||||
// Check if the fence is signaled
|
||||
GLint value;
|
||||
glGetSynciv(m_sync, GL_SYNC_STATUS, 1, nullptr, &value);
|
||||
|
||||
if (value != GL_SIGNALED) {
|
||||
qCDebug(KWIN_CORE) << "Waiting for X fence to finish";
|
||||
|
||||
// Wait for the fence to become signaled with a one second timeout
|
||||
const GLenum result = glClientWaitSync(m_sync, 0, 1000000000);
|
||||
|
||||
switch (result) {
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
qCWarning(KWIN_CORE) << "Timeout while waiting for X fence";
|
||||
return false;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
qCWarning(KWIN_CORE) << "glClientWaitSync() failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_state = Done;
|
||||
return true;
|
||||
}
|
||||
|
||||
void X11SyncObject::reset()
|
||||
{
|
||||
Q_ASSERT(m_state == Done);
|
||||
|
||||
xcb_connection_t *const connection = kwinApp()->x11Connection();
|
||||
|
||||
// Send the reset request along with a sync request.
|
||||
// We use the cookie to ensure that the server has processed the reset
|
||||
// request before we trigger the fence and call glWaitSync().
|
||||
// Otherwise there is a race condition between the reset finishing and
|
||||
// the glWaitSync() call.
|
||||
xcb_sync_reset_fence(connection, m_fence);
|
||||
m_reset_cookie = xcb_get_input_focus(connection);
|
||||
xcb_flush(connection);
|
||||
|
||||
m_state = Resetting;
|
||||
}
|
||||
|
||||
void X11SyncObject::finishResetting()
|
||||
{
|
||||
Q_ASSERT(m_state == Resetting);
|
||||
free(xcb_get_input_focus_reply(kwinApp()->x11Connection(), m_reset_cookie, nullptr));
|
||||
m_state = Ready;
|
||||
}
|
||||
|
||||
X11SyncManager *X11SyncManager::create(RenderBackend *backend)
|
||||
{
|
||||
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Compositor::self()->backend()->compositingType() != OpenGLCompositing) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto context = static_cast<OpenGLBackend *>(backend)->openglContext();
|
||||
if (context->hasOpenglExtension("GL_EXT_x11_sync_object") && context->haveSyncFences()) {
|
||||
const QString useExplicitSync = qEnvironmentVariable("KWIN_EXPLICIT_SYNC");
|
||||
|
||||
if (useExplicitSync != QLatin1String("0")) {
|
||||
qCDebug(KWIN_CORE) << "Initializing fences for synchronization with the X command stream";
|
||||
return new X11SyncManager;
|
||||
} else {
|
||||
qCDebug(KWIN_CORE) << "Explicit synchronization with the X command stream disabled by environment variable";
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
X11SyncManager::X11SyncManager()
|
||||
{
|
||||
for (int i = 0; i < MaxFences; ++i) {
|
||||
m_fences.append(new X11SyncObject);
|
||||
}
|
||||
}
|
||||
|
||||
X11SyncManager::~X11SyncManager()
|
||||
{
|
||||
Compositor::self()->scene()->makeOpenGLContextCurrent();
|
||||
qDeleteAll(m_fences);
|
||||
}
|
||||
|
||||
bool X11SyncManager::endFrame()
|
||||
{
|
||||
if (!m_currentFence) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < std::min<int>(2, m_fences.count() - 1); i++) {
|
||||
const int index = (m_next + i) % m_fences.count();
|
||||
X11SyncObject *fence = m_fences[index];
|
||||
|
||||
switch (fence->state()) {
|
||||
case X11SyncObject::Ready:
|
||||
break;
|
||||
|
||||
case X11SyncObject::TriggerSent:
|
||||
case X11SyncObject::Waiting:
|
||||
if (!fence->finish()) {
|
||||
return false;
|
||||
}
|
||||
fence->reset();
|
||||
break;
|
||||
|
||||
// Should not happen in practice since we always reset the fence after finishing it
|
||||
case X11SyncObject::Done:
|
||||
fence->reset();
|
||||
break;
|
||||
|
||||
case X11SyncObject::Resetting:
|
||||
fence->finishResetting();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_currentFence = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void X11SyncManager::triggerFence()
|
||||
{
|
||||
m_currentFence = m_fences[m_next];
|
||||
m_next = (m_next + 1) % m_fences.count();
|
||||
m_currentFence->trigger();
|
||||
}
|
||||
|
||||
void X11SyncManager::insertWait()
|
||||
{
|
||||
if (m_currentFence && m_currentFence->state() != X11SyncObject::Waiting) {
|
||||
m_currentFence->wait();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
Reference in New Issue
Block a user