Improve the implementation of the _NET_WM_SYNC_REQUEST protocol

This change improves the synced resizes of xcb windows and adds support
for synced resizes of glx windows.

The QXcbWindow keeps a better track on whether the window manager
expects a sync and can be in one of three states:
* no sync required
* sync required, but configure notify event not yet received
* sync required and configured

By tracking this in the QXcbWindow itself the backing store can make
use of this information and doesn't need an own heuristic to decide
whether a sync is needed.

Also this allows to add support for synced resizes of windows with an
OpenGLSurface. This is accomplished by checking the sync state after
swapping buffers. As the OpenGL context may be bound to a background
thread the sync is done using a QueuedConnection to ensure that the
sync happens in the thread which created the xcb window.
So far this is only added for GLX.

This significantly improves the resize experience of QQuickWindow and
also the initial mapping with a composited window manager in case the
compositor uses the sync protocol to determine whether the window is
ready to get painted on screen.

Change-Id: Ied0261873043d785dec652d2821fc3638292fa36
Reviewed-by: Uli Schlachter <psychon@znc.in>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
This commit is contained in:
Martin Gräßlin 2014-05-20 13:01:50 +02:00 committed by The Qt Project
parent d153e4628e
commit 8746a6a3e2
8 changed files with 59 additions and 16 deletions

View File

@ -390,6 +390,15 @@ void QGLXContext::swapBuffers(QPlatformSurface *surface)
else
glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window();
glXSwapBuffers(DISPLAY_FROM_XCB(m_screen), glxDrawable);
if (surface->surface()->surfaceClass() == QSurface::Window) {
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface);
// OpenGL context might be bound to a non-gui thread
// use QueuedConnection to sync the window from the platformWindow's thread
// as QXcbWindow is no QObject, a wrapper slot in QXcbConnection is used.
if (platformWindow->needsSync())
QMetaObject::invokeMethod(m_screen->connection(), "syncWindow", Qt::QueuedConnection, Q_ARG(QXcbWindow*, platformWindow));
}
}
void (*QGLXContext::getProcAddress(const QByteArray &procName)) ()

View File

@ -261,7 +261,6 @@ void QXcbShmImage::preparePaint(const QRegion &region)
QXcbBackingStore::QXcbBackingStore(QWindow *window)
: QPlatformBackingStore(window)
, m_image(0)
, m_syncingResize(false)
{
QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle());
setConnection(screen->connection());
@ -330,13 +329,10 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
Q_XCB_NOOP(connection());
if (m_syncingResize) {
connection()->sync();
m_syncingResize = false;
if (platformWindow->needsSync())
platformWindow->updateSyncRequestCounter();
} else {
else
xcb_flush(xcb_connection());
}
}
#ifndef QT_NO_OPENGL
@ -347,10 +343,8 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, c
Q_XCB_NOOP(connection());
if (m_syncingResize) {
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
connection()->sync();
m_syncingResize = false;
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (platformWindow->needsSync()) {
platformWindow->updateSyncRequestCounter();
} else {
xcb_flush(xcb_connection());
@ -376,8 +370,6 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
delete m_image;
m_image = new QXcbShmImage(screen, size, win->depth(), win->imageFormat());
Q_XCB_NOOP(connection());
m_syncingResize = true;
}
extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);

View File

@ -72,7 +72,6 @@ public:
private:
QXcbShmImage *m_image;
bool m_syncingResize;
};
QT_END_NAMESPACE

View File

@ -1833,6 +1833,11 @@ QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker()
return m_systemTrayTracker;
}
void QXcbConnection::syncWindow(QXcbWindow *window)
{
window->updateSyncRequestCounter();
}
QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection)
:m_connection(connection)
{

View File

@ -464,6 +464,9 @@ public:
void handleEnterEvent(const xcb_enter_notify_event_t *);
#endif
public slots:
void syncWindow(QXcbWindow *window);
private slots:
void processXcbEvents();

View File

@ -127,6 +127,7 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
: m_services(new QGenericUnixServices)
, m_instanceName(0)
{
qRegisterMetaType<QXcbWindow*>();
#ifdef XCB_USE_XLIB
XInitThreads();
#endif

View File

@ -200,6 +200,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
, m_eglSurface(0)
#endif
, m_lastWindowStateEvent(-1)
, m_syncState(NoSyncNeeded)
{
m_screen = static_cast<QXcbScreen *>(window->screen()->handle());
@ -367,7 +368,12 @@ void QXcbWindow::create()
properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS);
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING);
m_usingSyncProtocol = m_screen->syncRequestSupported() && window()->surfaceType() != QSurface::OpenGLSurface;
m_usingSyncProtocol = m_screen->syncRequestSupported();
#if !defined(XCB_USE_GLX)
// synced resize only implemented on GLX
if (window()->supportsOpenGL())
m_usingSyncProtocol = false;
#endif
if (m_usingSyncProtocol)
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST);
@ -1596,6 +1602,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
connection()->setTime(event->data.data32[1]);
m_syncValue.lo = event->data.data32[2];
m_syncValue.hi = event->data.data32[3];
if (m_usingSyncProtocol)
m_syncState = SyncReceived;
#ifndef QT_NO_WHATSTHIS
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
QWindowSystemInterface::handleEnterWhatsThisEvent();
@ -1669,6 +1677,9 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
}
if (m_usingSyncProtocol && m_syncState == SyncReceived)
m_syncState = SyncAndConfigureReceived;
m_dirtyFrameMargins = true;
}
@ -1945,12 +1956,17 @@ void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *)
void QXcbWindow::updateSyncRequestCounter()
{
if (m_syncState != SyncAndConfigureReceived) {
// window manager does not expect a sync event yet.
return;
}
if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
Q_XCB_CALL(xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue));
connection()->sync();
xcb_flush(xcb_connection());
m_syncValue.lo = 0;
m_syncValue.hi = 0;
m_syncState = NoSyncNeeded;
}
}
@ -2173,4 +2189,9 @@ void QXcbWindow::setAlertState(bool enabled)
}
}
bool QXcbWindow::needsSync() const
{
return m_syncState == SyncAndConfigureReceived;
}
QT_END_NAMESPACE

View File

@ -144,13 +144,17 @@ public:
void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers);
void updateSyncRequestCounter();
void updateNetWmUserTime(xcb_timestamp_t timestamp);
#if defined(XCB_USE_EGL)
QXcbEGLSurface *eglSurface() const;
#endif
bool needsSync() const;
public Q_SLOTS:
void updateSyncRequestCounter();
private:
void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0);
NetWmStates netWmStates();
@ -217,8 +221,17 @@ private:
xcb_visualid_t m_visualId;
int m_lastWindowStateEvent;
enum SyncState {
NoSyncNeeded,
SyncReceived,
SyncAndConfigureReceived
};
SyncState m_syncState;
};
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QXcbWindow*)
#endif