From e63e5f5712328b3b4e4f81a7e94c222b4501f9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20L=C3=BCbking?= Date: Fri, 30 Aug 2013 01:30:13 +0200 Subject: [PATCH] fix scheduling the repaints repaints caused by effects so far polluted the timing calculations since they started the timer on the old vsync offset This (together with undercut timing) lead to multiple frames in the buffer queue, and ultimately to a blocking swap For unsynced painting, it simply caused wrong timings - leading to "well, kinda around 60Hz - could be 75 as just well". REVIEW: 112368 CCBUG: 322060 that part is fixed in 4.11.2 --- composite.cpp | 33 +++++++++++++++++++++++++++------ eglonxbackend.cpp | 10 ++++++++++ glxbackend.cpp | 10 ++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/composite.cpp b/composite.cpp index de674b6c91..4eb8e46044 100644 --- a/composite.cpp +++ b/composite.cpp @@ -270,7 +270,6 @@ void Compositor::slotCompositingOptionsInitialized() } // render at least once - compositeTimer.stop(); performCompositing(); } @@ -548,7 +547,6 @@ void Compositor::addRepaintFull() void Compositor::timerEvent(QTimerEvent *te) { if (te->timerId() == compositeTimer.timerId()) { - compositeTimer.stop(); performCompositing(); } else QObject::timerEvent(te); @@ -596,10 +594,12 @@ 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(); return; } @@ -617,6 +617,8 @@ void Compositor::performCompositing() m_timeSinceLastVBlank = m_scene->paint(repaints, windows); + compositeTimer.stop(); // stop here to ensure *we* cause the next repaint schedule - not some effect through m_scene->paint() + // 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 @@ -676,10 +678,29 @@ void Compositor::setCompositeTimer() waitTime = nanoToMilli(padding - options->vBlankTime()); } } - else // w/o vsync we just jump to the next demanded tick - // the "1" will ensure we don't block out the eventloop - the system's just not faster - // "0" would be sufficient, but the compositor isn't the WMs only task - waitTime = (m_timeSinceLastVBlank > fpsInterval) ? 1 : nanoToMilli(fpsInterval - m_timeSinceLastVBlank); + else { // w/o blocking vsync we just jump to the next demanded tick + if (fpsInterval > m_timeSinceLastVBlank) { + waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); + if (!waitTime) { + waitTime = 1; // will ensure we don't block out the eventloop - the system's just not faster ... + } + }/* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) { + // NOTICE - "for later" ------------------------------------------------------------------ + // It can happen that we push two frames within one refresh cycle. + // Swapping will then block even with triple buffering when the GPU does not discard but + // queues frames + // now here's the mean part: if we take that as "OMG, we're late - next frame ASAP", + // there'll immediately be 2 frames in the pipe, swapping will block, we think we're + // late ... ewww + // so instead we pad to the clock again and add 2ms safety to ensure the pipe is really + // free + // NOTICE: obviously m_timeSinceLastVBlank can be too big because we're too slow as well + // So if this code was enabled, we'd needlessly half the framerate once more (15 instead of 30) + waitTime = nanoToMilli(vBlankInterval - (m_timeSinceLastVBlank - fpsInterval)%vBlankInterval) + 2; + }*/ else { + waitTime = 1; // ... "0" would be sufficient, but the compositor isn't the WMs only task + } + } compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum } diff --git a/eglonxbackend.cpp b/eglonxbackend.cpp index ab692d7cb5..39dd82419b 100644 --- a/eglonxbackend.cpp +++ b/eglonxbackend.cpp @@ -26,6 +26,8 @@ along with this program. If not, see . #include // KDE #include +// system +#include namespace KWin { @@ -320,6 +322,14 @@ SceneOpenGL::TexturePrivate *EglOnXBackend::createBackendTexture(SceneOpenGL::Te void EglOnXBackend::prepareRenderingFrame() { + if (gs_tripleBufferNeedsDetection) { + // the composite timer floors the repaint frequency. This can pollute our triple buffering + // detection because the glXSwapBuffers call for the new frame has to wait until the pending + // one scanned out. + // So we compensate for that by waiting an extra milisecond to give the driver the chance to + // fllush the buffer queue + usleep(1000); + } present(); startRenderTimer(); eglWaitNative(EGL_CORE_NATIVE_ENGINE); diff --git a/glxbackend.cpp b/glxbackend.cpp index 9e3064c092..f1b9a6a598 100644 --- a/glxbackend.cpp +++ b/glxbackend.cpp @@ -35,6 +35,8 @@ along with this program. If not, see . // KDE #include #include +// system +#include namespace KWin { @@ -493,6 +495,14 @@ SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Textu void GlxBackend::prepareRenderingFrame() { + if (gs_tripleBufferNeedsDetection) { + // the composite timer floors the repaint frequency. This can pollute our triple buffering + // detection because the glXSwapBuffers call for the new frame has to wait until the pending + // one scanned out. + // So we compensate for that by waiting an extra milisecond to give the driver the chance to + // fllush the buffer queue + usleep(1000); + } present(); startRenderTimer(); glXWaitX();