[platforms/x11] Force glXSwapBuffers to block with NVIDIA driver
Summary:
The NVIDIA implementation of glXSwapBuffers will, by default, queue up
to two frames for presentation before blocking. KWin's compositor,
however, assumes that calls to glXSwapBuffers will always block until
the next vblank when rendering double buffered. This assumption isn't
valid, as glXSwapBuffers is specified as being an implicit glFlush,
not an implicit glFinish, and so it isn't required to block. When this
assumption is violated, KWin's frame timing logic will
break. Specifically, there will be extraneous calls to
setCompositeTimer with a waitTime of 0 after the non-blocking buffer
swaps, dramatically reducing desktop responsiveness. To remedy this,
a call to glXWaitGL was added by Thomas Luebking after glXSwapBuffers
in 2015 (see bug 346275, commit
8bea96d701
). That glXWaitGL call is
equivalent to a glFinish call in direct rendering, so it was a good
way to make glXSwapBuffers behave as though it implied a glFinish
call.
However, the NVIDIA driver will by default do a busy wait in glFinish,
for reduced latency. Therefore that change dramatically increased CPU
usage. GL_YIELD can be set to USLEEP (case insensitive) to change
the behavior and use usleep instead. When using the NVIDIA driver,
KWin will disable vsync entirely if GL_YIELD isn't set to USLEEP
(case sensitive, a bug in KWin).
However, the NVIDIA driver supports another environment variable,
__GL_MaxFramesAllowed, which can be used to control how many frames
may be queued by glXSwapBuffers. If this is set to 1 the function
will always block until retrace, in line with KWin's expectations.
This allows the now-unnecessary call to glXWaitGL to be removed along
with the logic to conditionally disable vsync, providing a better
experience on NVIDIA hardware.
Reviewers: #kwin, davidedmundson, zzag
Reviewed By: #kwin, davidedmundson, zzag
Subscribers: kwin, davidedmundson, zzag
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19867
This commit is contained in:
parent
d2820bf05e
commit
22a441e071
1 changed files with 5 additions and 17 deletions
|
@ -115,6 +115,11 @@ GlxBackend::GlxBackend(Display *display)
|
|||
, haveSwapInterval(false)
|
||||
, m_x11Display(display)
|
||||
{
|
||||
// Ensures calls to glXSwapBuffers will always block until the next
|
||||
// retrace when using the proprietary NVIDIA driver. This must be
|
||||
// set before libGL.so is loaded.
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
|
||||
// Force initialization of GLX integration in the Qt's xcb backend
|
||||
// to make it call XESetWireToEvent callbacks, which is required
|
||||
// by Mesa when using DRI2.
|
||||
|
@ -696,25 +701,8 @@ void GlxBackend::present()
|
|||
glXWaitGL();
|
||||
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);
|
||||
setSwapInterval(0);
|
||||
result = 0; // hint proper behavior
|
||||
qCWarning(KWIN_X11STANDALONE) << "\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');
|
||||
}
|
||||
} else if (blocksForRetrace()) {
|
||||
// at least the nvidia blob manages to swap async, ie. return immediately on double
|
||||
// buffering - what messes our timing calculation and leads to laggy behavior #346275
|
||||
glXWaitGL();
|
||||
}
|
||||
} else {
|
||||
waitSync();
|
||||
|
|
Loading…
Reference in a new issue