Revert "[platforms/x11] Remove triple buffering detection"
This reverts commit ad892ce3a6
.
See: https://mail.kde.org/pipermail/kwin/2020-January/002999.html
This commit is contained in:
parent
a15624dcc5
commit
b8b9f78b37
8 changed files with 198 additions and 3 deletions
|
@ -2,6 +2,7 @@ set(SCENE_OPENGL_BACKEND_SRCS
|
|||
abstract_egl_backend.cpp
|
||||
backend.cpp
|
||||
egl_dmabuf.cpp
|
||||
swap_profiler.cpp
|
||||
texture.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
57
platformsupport/scenes/opengl/swap_profiler.cpp
Normal file
57
platformsupport/scenes/opengl/swap_profiler.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
53
platformsupport/scenes/opengl/swap_profiler.h
Normal file
53
platformsupport/scenes/opengl/swap_profiler.h
Normal 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
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue