Commit 1e3128b0 authored by Roman Gilg's avatar Roman Gilg
Browse files

Flexible composite swap and timer events

Summary:
The GLX backend might need a combination of swap and composite timer events for
continous painting.

The reason for that is that if the buffer age extension is not available we
fall back to copies in case not the whole screen is repainted.

The timer logic is adapted to make this possible in a lean way what cleans up
the Compositor class in several ways.

Test Plan: Tested on X11 (with/without swap events, buffer age enabled)  and Wayland.

Reviewers: #kwin

Subscribers: hurikhan77, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D26216
parent 2bb4acf7
......@@ -123,12 +123,9 @@ Compositor::Compositor(QObject* workspace)
: QObject(workspace)
, m_state(State::Off)
, m_selectionOwner(nullptr)
, vBlankInterval(0)
, fpsInterval(0)
, m_timeSinceLastVBlank(0)
, m_scene(nullptr)
, m_timerOffset(0)
, m_bufferSwapPending(false)
, m_composeAtSwapCompletion(false)
, m_scene(nullptr)
{
connect(options, &Options::configChanged, this, &Compositor::configChanged);
connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
......@@ -332,14 +329,6 @@ void Compositor::startupWithWorkspace()
connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); });
setupX11Support();
fpsInterval = options->maxFpsInterval();
const auto rate = currentRefreshRate();
Q_ASSERT(rate != 0); // There is a fallback in options.cpp, so why check at all?
// If we do vsync, set the fps to the next multiple of the vblank rate.
vBlankInterval = milliToNano(1000) / currentRefreshRate();
fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval);
// Sets also the 'effects' pointer.
kwinApp()->platform()->createEffectsHandler(this, m_scene);
......@@ -399,15 +388,8 @@ void Compositor::scheduleRepaint()
// But on the other side Present extension does not allow to sync with another screen
// anyway.
if (m_scene->hasSwapEvent()) {
// TODO: If we don't call it back from the event loop we often crash on Wayland
// in AnimationEffect::postPaintScreen. Why?
// Theory is that effects call addRepaintFull in there and then performCompositing
// is called again while still in the first paint. So queing it here makes sense!
compositeTimer.start(0, this);
} else {
setCompositeTimer();
}
setCompositeTimer();
}
void Compositor::stop()
......@@ -472,7 +454,6 @@ void Compositor::stop()
delete m_scene;
m_scene = nullptr;
m_bufferSwapPending = false;
m_composeAtSwapCompletion = false;
compositeTimer.stop();
repaints_region = QRegion();
......@@ -610,29 +591,23 @@ void Compositor::bufferSwapComplete()
{
Q_ASSERT(m_bufferSwapPending);
m_bufferSwapPending = false;
emit bufferSwapCompleted();
if (m_composeAtSwapCompletion) {
m_composeAtSwapCompletion = false;
performCompositing();
}
performCompositing();
}
void Compositor::performCompositing()
{
compositeTimer.stop();
// If a buffer swap is still pending, we return to the event loop and
// continue processing events until the swap has completed.
if (m_bufferSwapPending) {
m_composeAtSwapCompletion = true;
compositeTimer.stop();
return;
}
// If outputs are disabled, we return to the event loop and
// continue processing events until the outputs are enabled again
if (!kwinApp()->platform()->areOutputsEnabled()) {
compositeTimer.stop();
return;
}
......@@ -678,12 +653,9 @@ void Compositor::performCompositing()
if (repaints_region.isEmpty() && !windowRepaintsPending()) {
m_scene->idle();
m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now"
// Note: It would seem here we should undo suspended unredirect, but when scenes need
// it for some reason, e.g. transformations or translucency, the next pass that does not
// need this anymore and paints normally will also reset the suspended unredirect.
// Otherwise the window would not be painted normally anyway.
compositeTimer.stop();
// This means the next time we composite it is done without timer delay.
m_timerOffset = 1000 / refreshRate();
return;
}
......@@ -715,11 +687,11 @@ void Compositor::performCompositing()
Q_ASSERT(!m_bufferSwapPending);
// Start the actual painting process.
m_timeSinceLastVBlank = m_scene->paint(repaints, windows);
m_timerOffset = m_scene->paint(repaints, windows) / 1000 / 1000;
// TODO: In case we have swap events the buffer swap should now be pending, but this is not
// always the case on X11 standalone platform. Look into that.
// Q_ASSERT(m_scene->hasSwapEvent() ^ !m_bufferSwapPending);
// Either the backend will provide a swap event and a buffer swap is pending now or there is no
// pending buffer swap and by that no swap event received later on for the current paint call.
Q_ASSERT(m_scene->hasSwapEvent() ^ !m_bufferSwapPending);
if (m_framesToTestForSafety > 0) {
if (m_scene->compositingType() & OpenGLCompositing) {
......@@ -741,15 +713,12 @@ void Compositor::performCompositing()
}
}
// Stop here to ensure *we* cause the next repaint schedule - not some effect
// through m_scene->paint().
compositeTimer.stop();
// Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
// is called the next time. If there would be nothing pending, it will not restart the timer and
// scheduleRepaint() would restart it again somewhen later, called from functions that
// would again add something pending.
scheduleRepaint();
if (m_scene->hasSwapEvent()) {
m_timerOffset = 1000 / refreshRate();
} else {
setCompositeTimer();
}
}
template <class T>
......@@ -798,7 +767,7 @@ void Compositor::setCompositeTimer()
return;
}
uint waitTime = 1000 / refreshRate();
uint waitTime = 1000 / refreshRate() - m_timerOffset;
// Force 4fps minimum:
compositeTimer.start(qMin(waitTime, 250u), this);
}
......
......@@ -158,16 +158,14 @@ private:
QTimer m_releaseSelectionTimer;
QList<xcb_atom_t> m_unusedSupportProperties;
QTimer m_unusedSupportPropertyTimer;
qint64 vBlankInterval, fpsInterval;
QRegion repaints_region;
qint64 m_timeSinceLastVBlank;
// Compositing pause decrease through paint duration (in ms).
qint64 m_timerOffset;
bool m_bufferSwapPending;
Scene *m_scene;
bool m_bufferSwapPending;
bool m_composeAtSwapCompletion;
int m_framesToTestForSafety = 3;
QElapsedTimer m_monotonicClock;
};
......
......@@ -41,7 +41,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwineffectquickview.h>
#include <kwinxrenderutils.h>
// Qt
#include <QDebug>
#include <QOpenGLContext>
#include <QX11Info>
#include <QtPlatformHeaders/QGLXNativeContext>
......@@ -665,10 +664,12 @@ void GlxBackend::present()
const QSize &screenSize = screens()->size();
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion);
const bool canSwapBuffers = supportsBufferAge() || (lastDamage() == displayRegion);
m_needsCompositeTimerStart = true;
if (fullRepaint) {
if (hasSwapEvent()) {
if (canSwapBuffers) {
if (supportsSwapEvents()) {
m_needsCompositeTimerStart = false;
Compositor::self()->aboutToSwapBuffers();
}
......@@ -683,7 +684,8 @@ void GlxBackend::present()
int y = screenSize.height() - r.y() - r.height();
glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
}
} else { // Copy Pixels (horribly slow on Mesa)
} else {
// Copy Pixels (horribly slow on Mesa).
glDrawBuffer(GL_FRONT);
copyPixels(lastDamage());
glDrawBuffer(GL_BACK);
......@@ -783,11 +785,16 @@ bool GlxBackend::usesOverlayWindow() const
return true;
}
bool GlxBackend::hasSwapEvent() const
bool GlxBackend::supportsSwapEvents() const
{
return m_swapEventFilter != nullptr;
}
bool GlxBackend::hasSwapEvent() const
{
return !m_needsCompositeTimerStart;
}
/********************************************************
* GlxTexture
*******************************************************/
......
......@@ -93,6 +93,7 @@ private:
Display *display() const {
return m_x11Display;
}
bool supportsSwapEvents() const;
int visualDepth(xcb_visualid_t visual) const;
FBConfigInfo *infoForVisual(xcb_visualid_t visual);
......@@ -112,6 +113,7 @@ private:
bool m_haveMESACopySubBuffer = false;
bool m_haveMESASwapControl = false;
bool m_haveEXTSwapControl = false;
bool m_needsCompositeTimerStart = false;
Display *m_x11Display;
friend class GlxTexture;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment