Revert "Remove vsync detection and configurability"

This reverts commit b3a19f9e5b.

See: https://mail.kde.org/pipermail/kwin/2020-January/002999.html
This commit is contained in:
Roman Gilg 2020-01-09 18:18:22 +01:00
parent ac05dd01c8
commit bcf64af49b
17 changed files with 116 additions and 40 deletions

View file

@ -334,12 +334,14 @@ void Compositor::startupWithWorkspace()
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);
if (m_scene->syncsToVBlank()) {
// 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);
} else {
// No vsync - DO NOT set "0", would cause div-by-zero segfaults.
vBlankInterval = milliToNano(1);
}
// Sets also the 'effects' pointer.
kwinApp()->platform()->createEffectsHandler(this, m_scene);
@ -717,7 +719,7 @@ void Compositor::performCompositing()
// 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.
if (m_bufferSwapPending) {
if (m_bufferSwapPending && m_scene->syncsToVBlank()) {
m_composeAtSwapCompletion = true;
} else {
scheduleRepaint();
@ -816,7 +818,7 @@ void Compositor::setCompositeTimer()
waitTime = 1;
}
}
/* else if (m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) {
/* 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

View file

@ -81,14 +81,16 @@ void Compositing::reset()
auto swapStrategy = [&kwinConfig]() {
const QString glSwapStrategyValue = kwinConfig.readEntry("GLPreferBufferSwap", "a");
if (glSwapStrategyValue == "a") {
if (glSwapStrategyValue == "n") {
return 0;
} else if (glSwapStrategyValue == "e") {
} else if (glSwapStrategyValue == "a") {
return 1;
} else if (glSwapStrategyValue == "p") {
} else if (glSwapStrategyValue == "e") {
return 2;
} else if (glSwapStrategyValue == "c") {
} else if (glSwapStrategyValue == "p") {
return 3;
} else if (glSwapStrategyValue == "c") {
return 4;
}
return 0;
};
@ -284,13 +286,15 @@ void Compositing::save()
}
auto swapStrategy = [this] {
switch (glSwapStrategy()) {
case 1:
return QStringLiteral("e");
case 2:
return QStringLiteral("p");
case 3:
return QStringLiteral("c");
case 0:
return QStringLiteral("n");
case 2:
return QStringLiteral("e");
case 3:
return QStringLiteral("p");
case 4:
return QStringLiteral("c");
case 1:
default:
return QStringLiteral("a");
}

View file

@ -165,6 +165,11 @@ Alternatively, you might want to use the XRender backend instead.</string>
</item>
<item row="10" column="1">
<widget class="QComboBox" name="tearingPrevention">
<item>
<property name="text">
<string>Never</string>
</property>
</item>
<item>
<property name="text">
<string>Automatic</string>

View file

@ -918,12 +918,12 @@ void Options::reloadCompositingSettings(bool force)
}
setGLCoreProfile(config.readEntry("GLCore", Options::defaultGLCoreProfile()));
char c = 'a';
char c = 0;
const QString s = config.readEntry("GLPreferBufferSwap", QString(Options::defaultGlPreferBufferSwap()));
if (!s.isEmpty())
c = s.at(0).toLatin1();
if (c != 'a' && c != 'c' && c != 'p' && c != 'e')
c = 'a';
c = 0;
setGlPreferBufferSwap(c);
m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false);

View file

@ -570,7 +570,7 @@ public:
return m_glPlatformInterface;
}
enum GlSwapStrategy { CopyFrontBuffer = 'c', PaintFullScreen = 'p', ExtendDamage = 'e', AutoSwapStrategy = 'a' };
enum GlSwapStrategy { NoSwapEncourage = 0, CopyFrontBuffer = 'c', PaintFullScreen = 'p', ExtendDamage = 'e', AutoSwapStrategy = 'a' };
GlSwapStrategy glPreferBufferSwap() const {
return m_glPreferBufferSwap;
}

View file

@ -30,7 +30,8 @@ namespace KWin
{
OpenGLBackend::OpenGLBackend()
: m_blocksForRetrace(false)
: m_syncsToVBlank(false)
, m_blocksForRetrace(false)
, m_directRendering(false)
, m_haveBufferAge(false)
, m_failed(false)

View file

@ -124,6 +124,16 @@ public:
bool isFailed() const {
return m_failed;
}
/**
* @brief Whether the Backend provides VSync.
*
* Currently only the GLX backend can provide VSync.
*
* @return bool @c true if VSync support is available, @c false otherwise
*/
bool syncsToVBlank() const {
return m_syncsToVBlank;
}
/**
* @brief Whether VSync blocks execution until the screen is in the retrace
*
@ -202,6 +212,16 @@ protected:
* @param reason The reason why the initialization failed.
*/
void setFailed(const QString &reason);
/**
* @brief Sets whether the backend provides VSync.
*
* Should be called by the concrete subclass once it is determined whether VSync is supported.
* If the subclass does not call this method, the backend defaults to @c false.
* @param enabled @c true if VSync support available, @c false otherwise.
*/
void setSyncsToVBlank(bool enabled) {
m_syncsToVBlank = enabled;
}
/**
* @brief Sets whether the VSync iplementation blocks
*
@ -264,6 +284,10 @@ protected:
}
private:
/**
* @brief Whether VSync is available and used, defaults to @c false.
*/
bool m_syncsToVBlank;
/**
* @brief Whether present() will block execution until the next vertical retrace @c false.
*/

View file

@ -42,7 +42,7 @@ EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend)
{
// Egl is always direct rendering.
setIsDirectRendering(true);
setSyncsToVBlank(true);
connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::createOutput);
connect(m_backend, &DrmBackend::outputRemoved, this, &EglGbmBackend::removeOutput);
}

View file

@ -86,6 +86,7 @@ EglStreamBackend::EglStreamBackend(DrmBackend *b)
: AbstractEglBackend(), m_backend(b)
{
setIsDirectRendering(true);
setSyncsToVBlank(true);
connect(m_backend, &DrmBackend::outputAdded, this, &EglStreamBackend::createOutput);
connect(m_backend, &DrmBackend::outputRemoved, this,
[this] (DrmOutput *output) {

View file

@ -30,6 +30,7 @@ EglHwcomposerBackend::EglHwcomposerBackend(HwcomposerBackend *backend)
{
// EGL is always direct rendering
setIsDirectRendering(true);
setSyncsToVBlank(true);
}
EglHwcomposerBackend::~EglHwcomposerBackend()

View file

@ -121,21 +121,27 @@ void EglOnXBackend::init()
}
}
setSyncsToVBlank(false);
setBlocksForRetrace(true);
if (surfaceHasSubPost) {
qCDebug(KWIN_CORE) << "EGL implementation and surface support eglPostSubBufferNV, let's use it";
// check if swap interval 1 is supported
EGLint val;
eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val);
if (val >= 1) {
if (eglSwapInterval(eglDisplay(), 1)) {
qCDebug(KWIN_CORE) << "Enabled v-sync";
if (options->glPreferBufferSwap() != Options::NoSwapEncourage) {
// check if swap interval 1 is supported
EGLint val;
eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val);
if (val >= 1) {
if (eglSwapInterval(eglDisplay(), 1)) {
qCDebug(KWIN_CORE) << "Enabled v-sync";
setSyncsToVBlank(true);
}
} else {
qCWarning(KWIN_CORE) << "Cannot enable v-sync as max. swap interval is" << val;
}
} else {
qCWarning(KWIN_CORE) << "Cannot enable v-sync as max. swap interval is" << val;
// disable v-sync
eglSwapInterval(eglDisplay(), 0);
}
} else {
/* In the GLX backend, we fall back to using glCopyPixels if we have no extension providing support for partial screen updates.
* However, that does not work in EGL - glCopyPixels with glDrawBuffer(GL_FRONT); does nothing.

View file

@ -113,6 +113,7 @@ GlxBackend::GlxBackend(Display *display)
, glxWindow(None)
, ctx(nullptr)
, m_bufferAge(0)
, haveSwapInterval(false)
, m_x11Display(display)
{
// Ensures calls to glXSwapBuffers will always block until the next
@ -220,6 +221,8 @@ void GlxBackend::init()
glXSelectEvent(display(), glxWindow, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
}
haveSwapInterval = m_haveMESASwapControl || m_haveEXTSwapControl;
setSupportsBufferAge(false);
if (hasExtension(QByteArrayLiteral("GLX_EXT_buffer_age"))) {
@ -229,16 +232,20 @@ void GlxBackend::init()
setSupportsBufferAge(true);
}
setSyncsToVBlank(false);
setBlocksForRetrace(true);
if (m_haveEXTSwapControl) {
glXSwapIntervalEXT(display(), glxWindow, 1);
} else if (m_haveMESASwapControl) {
glXSwapIntervalMESA(1);
const bool wantSync = options->glPreferBufferSwap() != Options::NoSwapEncourage;
if (wantSync && glXIsDirect(display(), ctx)) {
if (haveSwapInterval) { // glXSwapInterval is preferred being more reliable
setSwapInterval(1);
setSyncsToVBlank(true);
} else {
qCWarning(KWIN_X11STANDALONE) << "NO VSYNC! glSwapInterval is not supported";
}
} else {
qCWarning(KWIN_X11STANDALONE) << "NO VSYNC! glSwapInterval is not supported";
// disable v-sync (if possible)
setSwapInterval(0);
}
if (glPlatform->isVirtualBox()) {
// VirtualBox does not support glxQueryDrawable
// this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
@ -665,6 +672,14 @@ FBConfigInfo *GlxBackend::infoForVisual(xcb_visualid_t visual)
return info;
}
void GlxBackend::setSwapInterval(int interval)
{
if (m_haveEXTSwapControl)
glXSwapIntervalEXT(display(), glxWindow, interval);
else if (m_haveMESASwapControl)
glXSwapIntervalMESA(interval);
}
void GlxBackend::present()
{
if (lastDamage().isEmpty())
@ -678,8 +693,11 @@ void GlxBackend::present()
if (m_haveINTELSwapEvent)
Compositor::self()->aboutToSwapBuffers();
glXSwapBuffers(display(), glxWindow);
if (haveSwapInterval) {
glXSwapBuffers(display(), glxWindow);
} else {
glXSwapBuffers(display(), glxWindow);
}
if (supportsBufferAge()) {
glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *) &m_bufferAge);
}

View file

@ -89,6 +89,7 @@ private:
bool initRenderingContext();
bool initFbConfig();
void initVisualDepthHashTable();
void setSwapInterval(int interval);
Display *display() const {
return m_x11Display;
}
@ -112,6 +113,7 @@ private:
bool m_haveMESASwapControl = false;
bool m_haveEXTSwapControl = false;
bool m_haveINTELSwapEvent = false;
bool haveSwapInterval = false;
Display *m_x11Display;
friend class GlxTexture;
};

View file

@ -492,6 +492,11 @@ OverlayWindow *SceneOpenGL::overlayWindow() const
return m_backend->overlayWindow();
}
bool SceneOpenGL::syncsToVBlank() const
{
return m_backend->syncsToVBlank();
}
bool SceneOpenGL::blocksForRetrace() const
{
return m_backend->blocksForRetrace();

View file

@ -53,6 +53,7 @@ public:
OverlayWindow *overlayWindow() const override;
bool usesOverlayWindow() const override;
bool blocksForRetrace() const override;
bool syncsToVBlank() const override;
bool makeOpenGLContextCurrent() override;
void doneOpenGLContextCurrent() override;
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;

View file

@ -626,6 +626,11 @@ bool Scene::blocksForRetrace() const
return false;
}
bool Scene::syncsToVBlank() const
{
return false;
}
void Scene::screenGeometryChanged(const QSize &size)
{
if (!overlayWindow()) {

View file

@ -146,6 +146,7 @@ public:
// there's nothing to paint (adjust time_diff later)
virtual void idle();
virtual bool blocksForRetrace() const;
virtual bool syncsToVBlank() const;
virtual OverlayWindow* overlayWindow() const = 0;
virtual bool makeOpenGLContextCurrent();