Revert "[platforms/x11] Remove triple buffering detection"

This reverts commit ad892ce3a6.

See: https://mail.kde.org/pipermail/kwin/2020-January/002999.html
master
Roman Gilg 2020-01-09 18:18:27 +01:00
parent a15624dcc5
commit b8b9f78b37
8 changed files with 198 additions and 3 deletions

View File

@ -2,6 +2,7 @@ set(SCENE_OPENGL_BACKEND_SRCS
abstract_egl_backend.cpp
backend.cpp
egl_dmabuf.cpp
swap_profiler.cpp
texture.cpp
)

View File

@ -137,8 +137,7 @@ public:
/**
* @brief Whether VSync blocks execution until the screen is in the retrace
*
* Case for waitVideoSync and non triple buffering buffer swaps (triple buffering support
* has been removed).
* Case for waitVideoSync and non triple buffering buffer swaps
*
*/
bool blocksForRetrace() const {

View File

@ -0,0 +1,57 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "swap_profiler.h"
#include <logging.h>
namespace KWin
{
SwapProfiler::SwapProfiler()
{
init();
}
void SwapProfiler::init()
{
m_time = 2 * 1000*1000; // we start with a long time mean of 2ms ...
m_counter = 0;
}
void SwapProfiler::begin()
{
m_timer.start();
}
char SwapProfiler::end()
{
// .. and blend in actual values.
// this way we prevent extremes from killing our long time mean
m_time = (10*m_time + m_timer.nsecsElapsed())/11;
if (++m_counter > 500) {
const bool blocks = m_time > 1000 * 1000; // 1ms, i get ~250µs and ~7ms w/o triple buffering...
qCDebug(KWIN_OPENGL) << "Triple buffering detection:" << QString(blocks ? QStringLiteral("NOT available") : QStringLiteral("Available")) <<
" - Mean block time:" << m_time/(1000.0*1000.0) << "ms";
return blocks ? 'd' : 't';
}
return 0;
}
}

View File

@ -0,0 +1,53 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_SCENE_OPENGL_SWAP_PROFILER_H
#define KWIN_SCENE_OPENGL_SWAP_PROFILER_H
#include <QElapsedTimer>
#include <kwin_export.h>
namespace KWin
{
/**
* @short Profiler to detect whether we have triple buffering
* The strategy is to start setBlocksForRetrace(false) but assume blocking and have the system prove that assumption wrong
*/
class KWIN_EXPORT SwapProfiler
{
public:
SwapProfiler();
void init();
void begin();
/**
* @return char being 'd' for double, 't' for triple (or more - but non-blocking) buffering and
* 0 (NOT '0') otherwise, so you can act on "if (char result = SwapProfiler::end()) { fooBar(); }
*/
char end();
private:
QElapsedTimer m_timer;
qint64 m_time;
int m_counter;
};
}
#endif

View File

@ -73,6 +73,9 @@ EglOnXBackend::EglOnXBackend(xcb_connection_t *connection, Display *display, xcb
setIsDirectRendering(true);
}
static bool gs_tripleBufferUndetected = true;
static bool gs_tripleBufferNeedsDetection = false;
EglOnXBackend::~EglOnXBackend()
{
if (isFailed() && m_overlayWindow) {
@ -80,6 +83,9 @@ EglOnXBackend::~EglOnXBackend()
}
cleanup();
gs_tripleBufferUndetected = true;
gs_tripleBufferNeedsDetection = false;
if (m_overlayWindow) {
if (overlayWindow()->window()) {
overlayWindow()->destroy();
@ -122,7 +128,9 @@ void EglOnXBackend::init()
}
setSyncsToVBlank(false);
setBlocksForRetrace(true);
setBlocksForRetrace(false);
gs_tripleBufferNeedsDetection = false;
m_swapProfiler.init();
if (surfaceHasSubPost) {
qCDebug(KWIN_CORE) << "EGL implementation and surface support eglPostSubBufferNV, let's use it";
@ -134,6 +142,12 @@ void EglOnXBackend::init()
if (eglSwapInterval(eglDisplay(), 1)) {
qCDebug(KWIN_CORE) << "Enabled v-sync";
setSyncsToVBlank(true);
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
if (!tripleBuffer.isEmpty()) {
setBlocksForRetrace(qstrcmp(tripleBuffer, "0") == 0);
gs_tripleBufferUndetected = false;
}
gs_tripleBufferNeedsDetection = gs_tripleBufferUndetected;
}
} else {
qCWarning(KWIN_CORE) << "Cannot enable v-sync as max. swap interval is" << val;
@ -329,8 +343,32 @@ void EglOnXBackend::presentSurface(EGLSurface surface, const QRegion &damage, co
const bool fullRepaint = supportsBufferAge() || (damage == screenGeometry);
if (fullRepaint || !surfaceHasSubPost) {
if (gs_tripleBufferNeedsDetection) {
eglWaitGL();
m_swapProfiler.begin();
}
// the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation)
eglSwapBuffers(eglDisplay(), surface);
if (gs_tripleBufferNeedsDetection) {
eglWaitGL();
if (char result = m_swapProfiler.end()) {
gs_tripleBufferUndetected = gs_tripleBufferNeedsDetection = false;
if (result == 'd' && GLPlatform::instance()->driver() == Driver_NVidia) {
// TODO this is a workaround, we should get __GL_YIELD set before libGL checks it
if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) {
options->setGlPreferBufferSwap(0);
eglSwapInterval(eglDisplay(), 0);
result = 0; // hint proper behavior
qCWarning(KWIN_CORE) << "\nIt seems you are using the nvidia driver without triple buffering\n"
"You must export __GL_YIELD=\"USLEEP\" to prevent large CPU overhead on synced swaps\n"
"Preferably, enable the TripleBuffer Option in the xorg.conf Device\n"
"For this reason, the tearing prevention has been disabled.\n"
"See https://bugs.kde.org/show_bug.cgi?id=322060\n";
}
}
setBlocksForRetrace(result == 'd');
}
}
if (supportsBufferAge()) {
eglQuerySurface(eglDisplay(), surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
}
@ -361,6 +399,15 @@ QRegion EglOnXBackend::prepareRenderingFrame()
{
QRegion repaint;
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();
if (supportsBufferAge())

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_EGL_ON_X_BACKEND_H
#define KWIN_EGL_ON_X_BACKEND_H
#include "abstract_egl_backend.h"
#include "swap_profiler.h"
#include <xcb/xcb.h>
@ -81,6 +82,7 @@ private:
xcb_window_t m_renderingWindow = XCB_WINDOW_NONE;
bool m_havePlatformBase = false;
bool m_x11TextureFromPixmapSupported = true;
SwapProfiler m_swapProfiler;
friend class EglTexture;
};

View File

@ -127,6 +127,9 @@ GlxBackend::GlxBackend(Display *display)
QOpenGLContext::supportsThreadedOpenGL();
}
static bool gs_tripleBufferUndetected = true;
static bool gs_tripleBufferNeedsDetection = false;
GlxBackend::~GlxBackend()
{
if (isFailed()) {
@ -138,6 +141,9 @@ GlxBackend::~GlxBackend()
doneCurrent();
EffectQuickView::setShareContext(nullptr);
gs_tripleBufferUndetected = true;
gs_tripleBufferNeedsDetection = false;
if (ctx)
glXDestroyContext(display(), ctx);
@ -236,11 +242,19 @@ void GlxBackend::init()
setSyncsToVBlank(false);
setBlocksForRetrace(false);
haveWaitSync = false;
gs_tripleBufferNeedsDetection = false;
m_swapProfiler.init();
const bool wantSync = options->glPreferBufferSwap() != Options::NoSwapEncourage;
if (wantSync && glXIsDirect(display(), ctx)) {
if (haveSwapInterval) { // glXSwapInterval is preferred being more reliable
setSwapInterval(1);
setSyncsToVBlank(true);
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
if (!tripleBuffer.isEmpty()) {
setBlocksForRetrace(qstrcmp(tripleBuffer, "0") == 0);
gs_tripleBufferUndetected = false;
}
gs_tripleBufferNeedsDetection = gs_tripleBufferUndetected;
} else if (hasExtension(QByteArrayLiteral("GLX_SGI_video_sync"))) {
unsigned int sync;
if (glXGetVideoSyncSGI(&sync) == 0 && glXWaitVideoSyncSGI(1, 0, &sync) == 0) {
@ -722,7 +736,18 @@ void GlxBackend::present()
Compositor::self()->aboutToSwapBuffers();
if (haveSwapInterval) {
if (gs_tripleBufferNeedsDetection) {
glXWaitGL();
m_swapProfiler.begin();
}
glXSwapBuffers(display(), glxWindow);
if (gs_tripleBufferNeedsDetection) {
glXWaitGL();
if (char result = m_swapProfiler.end()) {
gs_tripleBufferUndetected = gs_tripleBufferNeedsDetection = false;
setBlocksForRetrace(result == 'd');
}
}
} else {
waitSync();
glXSwapBuffers(display(), glxWindow);
@ -773,6 +798,15 @@ QRegion GlxBackend::prepareRenderingFrame()
{
QRegion repaint;
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();
if (supportsBufferAge())

View File

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KWIN_GLX_BACKEND_H
#include "backend.h"
#include "texture.h"
#include "swap_profiler.h"
#include "x11eventfilter.h"
#include <xcb/glx.h>
@ -118,6 +119,7 @@ private:
bool haveSwapInterval = false;
bool haveWaitSync = false;
Display *m_x11Display;
SwapProfiler m_swapProfiler;
friend class GlxTexture;
};