Revert "Flexible composite swap and timer events"

This reverts commit 1e3128b0db.

See: https://mail.kde.org/pipermail/kwin/2020-January/002999.html
This commit is contained in:
Roman Gilg 2020-01-09 18:05:36 +01:00
parent 396ef7100e
commit a5c2f23e92
4 changed files with 62 additions and 38 deletions

View file

@ -123,9 +123,12 @@ Compositor::Compositor(QObject* workspace)
: QObject(workspace) : QObject(workspace)
, m_state(State::Off) , m_state(State::Off)
, m_selectionOwner(nullptr) , m_selectionOwner(nullptr)
, m_timerOffset(0) , vBlankInterval(0)
, m_bufferSwapPending(false) , fpsInterval(0)
, m_timeSinceLastVBlank(0)
, m_scene(nullptr) , m_scene(nullptr)
, m_bufferSwapPending(false)
, m_composeAtSwapCompletion(false)
{ {
connect(options, &Options::configChanged, this, &Compositor::configChanged); connect(options, &Options::configChanged, this, &Compositor::configChanged);
connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged); connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
@ -329,6 +332,14 @@ void Compositor::startupWithWorkspace()
connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); }); connect(workspace(), &Workspace::destroyed, this, [this] { compositeTimer.stop(); });
setupX11Support(); 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. // Sets also the 'effects' pointer.
kwinApp()->platform()->createEffectsHandler(this, m_scene); kwinApp()->platform()->createEffectsHandler(this, m_scene);
@ -388,8 +399,15 @@ void Compositor::scheduleRepaint()
// But on the other side Present extension does not allow to sync with another screen // But on the other side Present extension does not allow to sync with another screen
// anyway. // anyway.
setCompositeTimer(); 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();
}
} }
void Compositor::stop() void Compositor::stop()
@ -454,6 +472,7 @@ void Compositor::stop()
delete m_scene; delete m_scene;
m_scene = nullptr; m_scene = nullptr;
m_bufferSwapPending = false; m_bufferSwapPending = false;
m_composeAtSwapCompletion = false;
compositeTimer.stop(); compositeTimer.stop();
repaints_region = QRegion(); repaints_region = QRegion();
@ -591,23 +610,29 @@ void Compositor::bufferSwapComplete()
{ {
Q_ASSERT(m_bufferSwapPending); Q_ASSERT(m_bufferSwapPending);
m_bufferSwapPending = false; m_bufferSwapPending = false;
emit bufferSwapCompleted(); emit bufferSwapCompleted();
performCompositing();
if (m_composeAtSwapCompletion) {
m_composeAtSwapCompletion = false;
performCompositing();
}
} }
void Compositor::performCompositing() void Compositor::performCompositing()
{ {
compositeTimer.stop();
// If a buffer swap is still pending, we return to the event loop and // If a buffer swap is still pending, we return to the event loop and
// continue processing events until the swap has completed. // continue processing events until the swap has completed.
if (m_bufferSwapPending) { if (m_bufferSwapPending) {
m_composeAtSwapCompletion = true;
compositeTimer.stop();
return; return;
} }
// If outputs are disabled, we return to the event loop and // If outputs are disabled, we return to the event loop and
// continue processing events until the outputs are enabled again // continue processing events until the outputs are enabled again
if (!kwinApp()->platform()->areOutputsEnabled()) { if (!kwinApp()->platform()->areOutputsEnabled()) {
compositeTimer.stop();
return; return;
} }
@ -653,9 +678,12 @@ void Compositor::performCompositing()
if (repaints_region.isEmpty() && !windowRepaintsPending()) { if (repaints_region.isEmpty() && !windowRepaintsPending()) {
m_scene->idle(); m_scene->idle();
m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now"
// This means the next time we composite it is done without timer delay. // Note: It would seem here we should undo suspended unredirect, but when scenes need
m_timerOffset = 1000 / refreshRate(); // 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();
return; return;
} }
@ -687,11 +715,11 @@ void Compositor::performCompositing()
Q_ASSERT(!m_bufferSwapPending); Q_ASSERT(!m_bufferSwapPending);
// Start the actual painting process. // Start the actual painting process.
m_timerOffset = m_scene->paint(repaints, windows) / 1000 / 1000; m_timeSinceLastVBlank = m_scene->paint(repaints, windows);
// Either the backend will provide a swap event and a buffer swap is pending now or there is no // TODO: In case we have swap events the buffer swap should now be pending, but this is not
// pending buffer swap and by that no swap event received later on for the current paint call. // always the case on X11 standalone platform. Look into that.
Q_ASSERT(m_scene->hasSwapEvent() ^ !m_bufferSwapPending); // Q_ASSERT(m_scene->hasSwapEvent() ^ !m_bufferSwapPending);
if (m_framesToTestForSafety > 0) { if (m_framesToTestForSafety > 0) {
if (m_scene->compositingType() & OpenGLCompositing) { if (m_scene->compositingType() & OpenGLCompositing) {
@ -713,12 +741,15 @@ void Compositor::performCompositing()
} }
} }
// Stop here to ensure *we* cause the next repaint schedule - not some effect
// through m_scene->paint().
compositeTimer.stop(); compositeTimer.stop();
if (m_scene->hasSwapEvent()) {
m_timerOffset = 1000 / refreshRate(); // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle()
} else { // is called the next time. If there would be nothing pending, it will not restart the timer and
setCompositeTimer(); // scheduleRepaint() would restart it again somewhen later, called from functions that
} // would again add something pending.
scheduleRepaint();
} }
template <class T> template <class T>
@ -767,7 +798,7 @@ void Compositor::setCompositeTimer()
return; return;
} }
uint waitTime = 1000 / refreshRate() - m_timerOffset; uint waitTime = 1000 / refreshRate();
// Force 4fps minimum: // Force 4fps minimum:
compositeTimer.start(qMin(waitTime, 250u), this); compositeTimer.start(qMin(waitTime, 250u), this);
} }

View file

@ -158,14 +158,16 @@ private:
QTimer m_releaseSelectionTimer; QTimer m_releaseSelectionTimer;
QList<xcb_atom_t> m_unusedSupportProperties; QList<xcb_atom_t> m_unusedSupportProperties;
QTimer m_unusedSupportPropertyTimer; QTimer m_unusedSupportPropertyTimer;
qint64 vBlankInterval, fpsInterval;
QRegion repaints_region; QRegion repaints_region;
// Compositing pause decrease through paint duration (in ms). qint64 m_timeSinceLastVBlank;
qint64 m_timerOffset;
bool m_bufferSwapPending;
Scene *m_scene; Scene *m_scene;
bool m_bufferSwapPending;
bool m_composeAtSwapCompletion;
int m_framesToTestForSafety = 3; int m_framesToTestForSafety = 3;
QElapsedTimer m_monotonicClock; QElapsedTimer m_monotonicClock;
}; };

View file

@ -41,6 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwineffectquickview.h> #include <kwineffectquickview.h>
#include <kwinxrenderutils.h> #include <kwinxrenderutils.h>
// Qt // Qt
#include <QDebug>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QX11Info> #include <QX11Info>
#include <QtPlatformHeaders/QGLXNativeContext> #include <QtPlatformHeaders/QGLXNativeContext>
@ -664,12 +665,10 @@ void GlxBackend::present()
const QSize &screenSize = screens()->size(); const QSize &screenSize = screens()->size();
const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height()); const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
const bool canSwapBuffers = supportsBufferAge() || (lastDamage() == displayRegion); const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion);
m_needsCompositeTimerStart = true;
if (canSwapBuffers) { if (fullRepaint) {
if (supportsSwapEvents()) { if (hasSwapEvent()) {
m_needsCompositeTimerStart = false;
Compositor::self()->aboutToSwapBuffers(); Compositor::self()->aboutToSwapBuffers();
} }
@ -684,8 +683,7 @@ void GlxBackend::present()
int y = screenSize.height() - r.y() - r.height(); int y = screenSize.height() - r.y() - r.height();
glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height()); glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
} }
} else { } else { // Copy Pixels (horribly slow on Mesa)
// Copy Pixels (horribly slow on Mesa).
glDrawBuffer(GL_FRONT); glDrawBuffer(GL_FRONT);
copyPixels(lastDamage()); copyPixels(lastDamage());
glDrawBuffer(GL_BACK); glDrawBuffer(GL_BACK);
@ -785,14 +783,9 @@ bool GlxBackend::usesOverlayWindow() const
return true; return true;
} }
bool GlxBackend::supportsSwapEvents() const
{
return m_swapEventFilter != nullptr;
}
bool GlxBackend::hasSwapEvent() const bool GlxBackend::hasSwapEvent() const
{ {
return !m_needsCompositeTimerStart; return m_swapEventFilter != nullptr;
} }
/******************************************************** /********************************************************

View file

@ -93,7 +93,6 @@ private:
Display *display() const { Display *display() const {
return m_x11Display; return m_x11Display;
} }
bool supportsSwapEvents() const;
int visualDepth(xcb_visualid_t visual) const; int visualDepth(xcb_visualid_t visual) const;
FBConfigInfo *infoForVisual(xcb_visualid_t visual); FBConfigInfo *infoForVisual(xcb_visualid_t visual);
@ -113,7 +112,6 @@ private:
bool m_haveMESACopySubBuffer = false; bool m_haveMESACopySubBuffer = false;
bool m_haveMESASwapControl = false; bool m_haveMESASwapControl = false;
bool m_haveEXTSwapControl = false; bool m_haveEXTSwapControl = false;
bool m_needsCompositeTimerStart = false;
Display *m_x11Display; Display *m_x11Display;
friend class GlxTexture; friend class GlxTexture;
}; };