scenes/opengl: Move out X11 explicit sync
This moves X11 specific code to a better place, which allows us make item rendering code less platform specific and easier to change.
This commit is contained in:
parent
047c1a91f2
commit
1c94e1bfc4
11 changed files with 346 additions and 286 deletions
|
@ -142,6 +142,7 @@ set(kwin_SRCS
|
|||
workspace.cpp
|
||||
x11client.cpp
|
||||
x11eventfilter.cpp
|
||||
x11syncmanager.cpp
|
||||
xcbutils.cpp
|
||||
xcursortheme.cpp
|
||||
xdgactivationv1.cpp
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "utils.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "x11syncmanager.h"
|
||||
#include "xcbutils.h"
|
||||
|
||||
#include <kwingltexture.h>
|
||||
|
@ -694,6 +695,11 @@ X11Compositor::~X11Compositor()
|
|||
stop(); // this can't be called in the destructor of Compositor
|
||||
}
|
||||
|
||||
X11SyncManager *X11Compositor::syncManager() const
|
||||
{
|
||||
return m_syncManager.data();
|
||||
}
|
||||
|
||||
void X11Compositor::toggleCompositing()
|
||||
{
|
||||
if (m_suspended) {
|
||||
|
@ -773,6 +779,13 @@ void X11Compositor::start()
|
|||
return;
|
||||
}
|
||||
startupWithWorkspace();
|
||||
m_syncManager.reset(X11SyncManager::create());
|
||||
}
|
||||
|
||||
void X11Compositor::stop()
|
||||
{
|
||||
m_syncManager.reset();
|
||||
Compositor::stop();
|
||||
}
|
||||
|
||||
void X11Compositor::composite(RenderLoop *renderLoop)
|
||||
|
@ -795,7 +808,9 @@ void X11Compositor::composite(RenderLoop *renderLoop)
|
|||
}
|
||||
|
||||
if (dirtyItems.count() > 0) {
|
||||
scene()->triggerFence();
|
||||
if (m_syncManager) {
|
||||
m_syncManager->triggerFence();
|
||||
}
|
||||
xcb_flush(kwinApp()->x11Connection());
|
||||
}
|
||||
|
||||
|
@ -810,6 +825,14 @@ void X11Compositor::composite(RenderLoop *renderLoop)
|
|||
|
||||
Compositor::composite(renderLoop);
|
||||
|
||||
if (m_syncManager) {
|
||||
if (!m_syncManager->endFrame()) {
|
||||
qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
|
||||
qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
|
||||
m_syncManager.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_framesToTestForSafety > 0) {
|
||||
if (scene()->compositingType() & OpenGLCompositing) {
|
||||
kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame);
|
||||
|
|
|
@ -23,6 +23,7 @@ class CompositorSelectionOwner;
|
|||
class RenderLoop;
|
||||
class Scene;
|
||||
class X11Client;
|
||||
class X11SyncManager;
|
||||
|
||||
class KWIN_EXPORT Compositor : public QObject
|
||||
{
|
||||
|
@ -96,7 +97,7 @@ protected:
|
|||
explicit Compositor(QObject *parent = nullptr);
|
||||
|
||||
virtual void start() = 0;
|
||||
void stop();
|
||||
virtual void stop();
|
||||
|
||||
/**
|
||||
* @brief Prepares start.
|
||||
|
@ -177,6 +178,8 @@ public:
|
|||
static X11Compositor *create(QObject *parent = nullptr);
|
||||
~X11Compositor() override;
|
||||
|
||||
X11SyncManager *syncManager() const;
|
||||
|
||||
/**
|
||||
* @brief Suspends the Compositor if it is currently active.
|
||||
*
|
||||
|
@ -229,10 +232,12 @@ public:
|
|||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void composite(RenderLoop *renderLoop) override;
|
||||
|
||||
private:
|
||||
explicit X11Compositor(QObject *parent);
|
||||
QScopedPointer<X11SyncManager> m_syncManager;
|
||||
/**
|
||||
* Whether the Compositor is currently suspended, 8 bits encoding the reason
|
||||
*/
|
||||
|
|
|
@ -9,11 +9,6 @@
|
|||
Based on glcompmgr code by Felix Bellaby.
|
||||
Using code from Compiz and Beryl.
|
||||
|
||||
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 "scene_opengl.h"
|
||||
|
@ -73,224 +68,6 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* SyncObject represents a fence used to synchronize operations in
|
||||
* the kwin command stream with operations in the X command stream.
|
||||
*/
|
||||
class SyncObject
|
||||
{
|
||||
public:
|
||||
enum State { Ready, TriggerSent, Waiting, Done, Resetting };
|
||||
|
||||
SyncObject();
|
||||
~SyncObject();
|
||||
|
||||
State state() const { return m_state; }
|
||||
|
||||
void trigger();
|
||||
void wait();
|
||||
bool finish();
|
||||
void reset();
|
||||
void finishResetting();
|
||||
|
||||
private:
|
||||
State m_state;
|
||||
GLsync m_sync;
|
||||
xcb_sync_fence_t m_fence;
|
||||
xcb_get_input_focus_cookie_t m_reset_cookie;
|
||||
};
|
||||
|
||||
SyncObject::SyncObject()
|
||||
{
|
||||
m_state = Ready;
|
||||
|
||||
xcb_connection_t * const c = connection();
|
||||
|
||||
m_fence = xcb_generate_id(c);
|
||||
xcb_sync_create_fence(c, rootWindow(), m_fence, false);
|
||||
xcb_flush(c);
|
||||
|
||||
m_sync = glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, m_fence, 0);
|
||||
}
|
||||
|
||||
SyncObject::~SyncObject()
|
||||
{
|
||||
// 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 SyncObject::trigger()
|
||||
{
|
||||
Q_ASSERT(m_state == Ready || m_state == Resetting);
|
||||
|
||||
// Finish resetting the fence if necessary
|
||||
if (m_state == Resetting)
|
||||
finishResetting();
|
||||
|
||||
xcb_sync_trigger_fence(connection(), m_fence);
|
||||
m_state = TriggerSent;
|
||||
}
|
||||
|
||||
void SyncObject::wait()
|
||||
{
|
||||
if (m_state != TriggerSent)
|
||||
return;
|
||||
|
||||
glWaitSync(m_sync, 0, GL_TIMEOUT_IGNORED);
|
||||
m_state = Waiting;
|
||||
}
|
||||
|
||||
bool SyncObject::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_OPENGL) << "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_OPENGL) << "Timeout while waiting for X fence";
|
||||
return false;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
qCWarning(KWIN_OPENGL) << "glClientWaitSync() failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_state = Done;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyncObject::reset()
|
||||
{
|
||||
Q_ASSERT(m_state == Done);
|
||||
|
||||
xcb_connection_t * const c = connection();
|
||||
|
||||
// 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(c, m_fence);
|
||||
m_reset_cookie = xcb_get_input_focus(c);
|
||||
xcb_flush(c);
|
||||
|
||||
m_state = Resetting;
|
||||
}
|
||||
|
||||
void SyncObject::finishResetting()
|
||||
{
|
||||
Q_ASSERT(m_state == Resetting);
|
||||
free(xcb_get_input_focus_reply(connection(), m_reset_cookie, nullptr));
|
||||
m_state = Ready;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SyncManager manages a set of fences used for explicit synchronization
|
||||
* with the X command stream.
|
||||
*/
|
||||
class SyncManager
|
||||
{
|
||||
public:
|
||||
enum { MaxFences = 4 };
|
||||
|
||||
SyncManager();
|
||||
~SyncManager();
|
||||
|
||||
SyncObject *nextFence();
|
||||
bool updateFences();
|
||||
|
||||
private:
|
||||
std::array<SyncObject, MaxFences> m_fences;
|
||||
int m_next;
|
||||
};
|
||||
|
||||
SyncManager::SyncManager()
|
||||
: m_next(0)
|
||||
{
|
||||
}
|
||||
|
||||
SyncManager::~SyncManager()
|
||||
{
|
||||
}
|
||||
|
||||
SyncObject *SyncManager::nextFence()
|
||||
{
|
||||
SyncObject *fence = &m_fences[m_next];
|
||||
m_next = (m_next + 1) % MaxFences;
|
||||
return fence;
|
||||
}
|
||||
|
||||
bool SyncManager::updateFences()
|
||||
{
|
||||
for (int i = 0; i < qMin(2, MaxFences - 1); i++) {
|
||||
const int index = (m_next + i) % MaxFences;
|
||||
SyncObject &fence = m_fences[index];
|
||||
|
||||
switch (fence.state()) {
|
||||
case SyncObject::Ready:
|
||||
break;
|
||||
|
||||
case SyncObject::TriggerSent:
|
||||
case SyncObject::Waiting:
|
||||
if (!fence.finish())
|
||||
return false;
|
||||
fence.reset();
|
||||
break;
|
||||
|
||||
// Should not happen in practice since we always reset the fence
|
||||
// after finishing it
|
||||
case SyncObject::Done:
|
||||
fence.reset();
|
||||
break;
|
||||
|
||||
case SyncObject::Resetting:
|
||||
fence.finishResetting();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/************************************************
|
||||
* SceneOpenGL
|
||||
***********************************************/
|
||||
|
@ -299,8 +76,6 @@ SceneOpenGL::SceneOpenGL(OpenGLBackend *backend, QObject *parent)
|
|||
: Scene(parent)
|
||||
, init_ok(true)
|
||||
, m_backend(backend)
|
||||
, m_syncManager(nullptr)
|
||||
, m_currentFence(nullptr)
|
||||
{
|
||||
if (m_backend->isFailed()) {
|
||||
init_ok = false;
|
||||
|
@ -330,21 +105,6 @@ SceneOpenGL::SceneOpenGL(OpenGLBackend *backend, QObject *parent)
|
|||
if (options->isGlStrictBindingFollowsDriver()) {
|
||||
options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
|
||||
}
|
||||
|
||||
bool haveSyncObjects = glPlatform->isGLES()
|
||||
? hasGLVersion(3, 0)
|
||||
: hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync");
|
||||
|
||||
if (hasGLExtension("GL_EXT_x11_sync_object") && haveSyncObjects && kwinApp()->operationMode() == Application::OperationModeX11) {
|
||||
const QByteArray useExplicitSync = qgetenv("KWIN_EXPLICIT_SYNC");
|
||||
|
||||
if (useExplicitSync != "0") {
|
||||
qCDebug(KWIN_OPENGL) << "Initializing fences for synchronization with the X command stream";
|
||||
m_syncManager = new SyncManager;
|
||||
} else {
|
||||
qCDebug(KWIN_OPENGL) << "Explicit synchronization with the X command stream disabled by environment variable";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneOpenGL::~SceneOpenGL()
|
||||
|
@ -354,8 +114,6 @@ SceneOpenGL::~SceneOpenGL()
|
|||
}
|
||||
SceneOpenGL::EffectFrame::cleanup();
|
||||
|
||||
delete m_syncManager;
|
||||
|
||||
// backend might be still needed for a different scene
|
||||
delete m_backend;
|
||||
}
|
||||
|
@ -514,22 +272,6 @@ void SceneOpenGL::handleGraphicsReset(GLenum status)
|
|||
m_resetOccurred = true;
|
||||
}
|
||||
|
||||
|
||||
void SceneOpenGL::triggerFence()
|
||||
{
|
||||
if (m_syncManager) {
|
||||
m_currentFence = m_syncManager->nextFence();
|
||||
m_currentFence->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneOpenGL::insertWait()
|
||||
{
|
||||
if (m_currentFence && m_currentFence->state() != SyncObject::Waiting) {
|
||||
m_currentFence->wait();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render cursor texture in case hardware cursor is disabled.
|
||||
* Useful for screen recording apps or backends that can't do planes.
|
||||
|
@ -711,16 +453,6 @@ void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Topleve
|
|||
GLVertexBuffer::streamingBuffer()->endOfFrame();
|
||||
m_backend->endFrame(screenId, valid, update);
|
||||
GLVertexBuffer::streamingBuffer()->framePosted();
|
||||
|
||||
if (m_currentFence) {
|
||||
if (!m_syncManager->updateFences()) {
|
||||
qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream.";
|
||||
qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized.";
|
||||
delete m_syncManager;
|
||||
m_syncManager = nullptr;
|
||||
}
|
||||
m_currentFence = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1241,9 +973,6 @@ void OpenGLWindow::createRenderNode(Item *item, RenderContext *context)
|
|||
} else if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) {
|
||||
WindowQuadList quads = clipQuads(item, context);
|
||||
if (!quads.isEmpty()) {
|
||||
if (!surfaceItem->damage().isEmpty()) { // only relevant on X11
|
||||
m_scene->insertWait();
|
||||
}
|
||||
SurfacePixmap *pixmap = surfaceItem->pixmap();
|
||||
if (pixmap) {
|
||||
context->renderNodes.append(RenderNode{
|
||||
|
|
|
@ -23,8 +23,6 @@ namespace KWin
|
|||
{
|
||||
class LanczosFilter;
|
||||
class OpenGLBackend;
|
||||
class SyncManager;
|
||||
class SyncObject;
|
||||
|
||||
class KWIN_EXPORT SceneOpenGL
|
||||
: public Scene
|
||||
|
@ -45,15 +43,12 @@ public:
|
|||
bool supportsSurfacelessContext() const override;
|
||||
bool supportsNativeFence() const override;
|
||||
DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
|
||||
void triggerFence() override;
|
||||
virtual QMatrix4x4 projectionMatrix() const = 0;
|
||||
bool animationsSupported() const override;
|
||||
PlatformSurfaceTexture *createPlatformSurfaceTextureInternal(SurfacePixmapInternal *pixmap) override;
|
||||
PlatformSurfaceTexture *createPlatformSurfaceTextureX11(SurfacePixmapX11 *pixmap) override;
|
||||
PlatformSurfaceTexture *createPlatformSurfaceTextureWayland(SurfacePixmapWayland *pixmap) override;
|
||||
|
||||
void insertWait();
|
||||
|
||||
bool debug() const { return m_debug; }
|
||||
void initDebugOutput();
|
||||
|
||||
|
@ -89,8 +84,6 @@ private:
|
|||
bool m_resetOccurred = false;
|
||||
bool m_debug;
|
||||
OpenGLBackend *m_backend;
|
||||
SyncManager *m_syncManager;
|
||||
SyncObject *m_currentFence;
|
||||
};
|
||||
|
||||
class SceneOpenGL2 : public SceneOpenGL
|
||||
|
|
|
@ -681,10 +681,6 @@ bool Scene::supportsNativeFence() const
|
|||
return false;
|
||||
}
|
||||
|
||||
void Scene::triggerFence()
|
||||
{
|
||||
}
|
||||
|
||||
QMatrix4x4 Scene::screenProjectionMatrix() const
|
||||
{
|
||||
return QMatrix4x4();
|
||||
|
|
|
@ -153,8 +153,6 @@ public:
|
|||
|
||||
virtual QMatrix4x4 screenProjectionMatrix() const;
|
||||
|
||||
virtual void triggerFence();
|
||||
|
||||
virtual DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *) = 0;
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "surfaceitem_x11.h"
|
||||
#include "composite.h"
|
||||
#include "scene.h"
|
||||
#include "x11syncmanager.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -34,6 +35,17 @@ SurfaceItemX11::~SurfaceItemX11()
|
|||
{
|
||||
}
|
||||
|
||||
void SurfaceItemX11::preprocess()
|
||||
{
|
||||
if (!damage().isEmpty()) {
|
||||
X11Compositor *compositor = X11Compositor::self();
|
||||
if (X11SyncManager *syncManager = compositor->syncManager()) {
|
||||
syncManager->insertWait();
|
||||
}
|
||||
}
|
||||
SurfaceItem::preprocess();
|
||||
}
|
||||
|
||||
void SurfaceItemX11::processDamage()
|
||||
{
|
||||
m_isDamaged = true;
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
explicit SurfaceItemX11(Scene::Window *window, Item *parent = nullptr);
|
||||
~SurfaceItemX11() override;
|
||||
|
||||
void preprocess() override;
|
||||
|
||||
void processDamage();
|
||||
bool fetchDamage();
|
||||
void waitForDamage();
|
||||
|
|
232
src/x11syncmanager.cpp
Normal file
232
src/x11syncmanager.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
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 "composite.h"
|
||||
#include "main.h"
|
||||
#include "platform.h"
|
||||
#include "scene.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "kwinglplatform.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()
|
||||
{
|
||||
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene *scene = Compositor::self()->scene();
|
||||
if (scene->compositingType() != OpenGLCompositing) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLPlatform *glPlatform = GLPlatform::instance();
|
||||
const bool haveSyncObjects = glPlatform->isGLES()
|
||||
? hasGLVersion(3, 0)
|
||||
: hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync");
|
||||
|
||||
if (hasGLExtension("GL_EXT_x11_sync_object") && haveSyncObjects) {
|
||||
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(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
|
69
src/x11syncmanager.h
Normal file
69
src/x11syncmanager.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2014 Fredrik Höglund <fredrik@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwinglutils.h"
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/sync.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* SyncObject represents a fence used to synchronize operations in the kwin command stream
|
||||
* with operations in the X command stream.
|
||||
*/
|
||||
class X11SyncObject
|
||||
{
|
||||
public:
|
||||
enum State { Ready, TriggerSent, Waiting, Done, Resetting, };
|
||||
|
||||
X11SyncObject();
|
||||
~X11SyncObject();
|
||||
|
||||
State state() const { return m_state; }
|
||||
|
||||
void trigger();
|
||||
void wait();
|
||||
bool finish();
|
||||
void reset();
|
||||
void finishResetting();
|
||||
|
||||
private:
|
||||
State m_state;
|
||||
GLsync m_sync;
|
||||
xcb_sync_fence_t m_fence;
|
||||
xcb_get_input_focus_cookie_t m_reset_cookie;
|
||||
};
|
||||
|
||||
/**
|
||||
* SyncManager manages a set of fences used for explicit synchronization with the X command
|
||||
* stream.
|
||||
*/
|
||||
class X11SyncManager
|
||||
{
|
||||
public:
|
||||
enum { MaxFences = 4 };
|
||||
|
||||
static X11SyncManager *create();
|
||||
~X11SyncManager();
|
||||
|
||||
bool endFrame();
|
||||
|
||||
void triggerFence();
|
||||
void insertWait();
|
||||
|
||||
private:
|
||||
X11SyncManager();
|
||||
|
||||
X11SyncObject *m_currentFence = nullptr;
|
||||
QVector<X11SyncObject *> m_fences;
|
||||
int m_next = 0;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
Loading…
Reference in a new issue