diff --git a/composite.h b/composite.h --- a/composite.h +++ b/composite.h @@ -158,16 +158,14 @@ QTimer m_releaseSelectionTimer; QList 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; }; diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -123,12 +123,9 @@ : 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 @@ 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 @@ // 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 @@ delete m_scene; m_scene = nullptr; m_bufferSwapPending = false; - m_composeAtSwapCompletion = false; compositeTimer.stop(); repaints_region = QRegion(); @@ -610,29 +591,23 @@ { 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 @@ 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 @@ 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 @@ } } - // 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 @@ -798,7 +767,7 @@ return; } - uint waitTime = 1000 / refreshRate(); + uint waitTime = 1000 / refreshRate() - m_timerOffset; // Force 4fps minimum: compositeTimer.start(qMin(waitTime, 250u), this); } diff --git a/plugins/platforms/x11/standalone/glxbackend.h b/plugins/platforms/x11/standalone/glxbackend.h --- a/plugins/platforms/x11/standalone/glxbackend.h +++ b/plugins/platforms/x11/standalone/glxbackend.h @@ -93,6 +93,7 @@ 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 @@ bool m_haveMESACopySubBuffer = false; bool m_haveMESASwapControl = false; bool m_haveEXTSwapControl = false; + bool m_needsCompositeTimerStart = false; Display *m_x11Display; friend class GlxTexture; }; diff --git a/plugins/platforms/x11/standalone/glxbackend.cpp b/plugins/platforms/x11/standalone/glxbackend.cpp --- a/plugins/platforms/x11/standalone/glxbackend.cpp +++ b/plugins/platforms/x11/standalone/glxbackend.cpp @@ -41,7 +41,6 @@ #include #include // Qt -#include #include #include #include @@ -665,10 +664,12 @@ 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 @@ 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 @@ return true; } -bool GlxBackend::hasSwapEvent() const +bool GlxBackend::supportsSwapEvents() const { return m_swapEventFilter != nullptr; } +bool GlxBackend::hasSwapEvent() const +{ + return !m_needsCompositeTimerStart; +} + /******************************************************** * GlxTexture *******************************************************/