Support GLX_NV_robustness_video_memory_purge in XCB-GLX

Nvidia doesn't handle suspend very well in Linux and textures get
corrupted. To handle this Nvidia has a bespoke extension to query when
this has happened.

This patch checks if graphics have been reset and invalidates the
surface.

Task-number: QTBUG-56610
Change-Id: I0b97d539ce6cc2b9cfe41c71bf6efd4f68496cd6
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
bb10
David Edmundson 2018-02-15 02:01:39 +00:00 committed by Laszlo Agocs
parent f4435d2d5c
commit ee54df0311
2 changed files with 58 additions and 8 deletions

View File

@ -86,6 +86,22 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC
#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
#endif
#ifndef GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB
#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
#endif
#ifndef GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB
#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#endif
#ifndef GLX_LOSE_CONTEXT_ON_RESET_ARB
#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
#endif
#ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
#endif
static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin)
{
Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone);
@ -179,6 +195,8 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
, m_isPBufferCurrent(false)
, m_swapInterval(-1)
, m_ownsContext(nativeHandle.isNull())
, m_getGraphicsResetStatus(0)
, m_lost(false)
{
if (nativeHandle.isNull())
init(screen, share);
@ -214,6 +232,8 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
const bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile");
const bool supportsRobustness = glxExt.contains("GLX_ARB_create_context_robustness");
const bool supportsVideoMemoryPurge = glxExt.contains("GLX_NV_robustness_video_memory_purge");
// Use glXCreateContextAttribsARB if available
// Also, GL ES context creation requires GLX_EXT_create_context_es2_profile
@ -266,6 +286,9 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
int flags = 0;
if (supportsRobustness)
flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
if (m_format.testOption(QSurfaceFormat::DebugContext))
flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
@ -279,14 +302,33 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
}
contextAttributes << None;
if (supportsRobustness && supportsVideoMemoryPurge) {
QVector<int> contextAttributesWithNvidiaReset = contextAttributes;
m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data());
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributes.data());
if (m_context)
m_shareContext = 0;
contextAttributesWithNvidiaReset << GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB << GLX_LOSE_CONTEXT_ON_RESET_ARB;
contextAttributesWithNvidiaReset << GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV << GL_TRUE;
contextAttributesWithNvidiaReset << None;
m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributesWithNvidiaReset.data());
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributesWithNvidiaReset.data());
if (m_context)
m_shareContext = 0;
}
}
if (m_context) {
m_getGraphicsResetStatus = reinterpret_cast<GLenum (QOPENGLF_APIENTRYP)()>(getProcAddress("glGetGraphicsResetStatusARB"));
} else {
contextAttributes << None;
m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data());
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_context = glXCreateContextAttribsARB(m_display, config, 0, true, contextAttributes.data());
if (m_context)
m_shareContext = 0;
}
}
}
}
@ -494,6 +536,12 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface)
QXcbWindow *window = static_cast<QXcbWindow *>(surface);
glxDrawable = window->xcb_window();
success = glXMakeCurrent(m_display, glxDrawable, m_context);
m_lost = false;
if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) {
m_lost = true;
// Drop the surface. Will recreate on the next makeCurrent.
window->invalidateSurface();
}
} else if (surfaceClass == QSurface::Offscreen) {
m_isPBufferCurrent = true;
QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface);
@ -609,7 +657,7 @@ bool QGLXContext::isSharing() const
bool QGLXContext::isValid() const
{
return m_context != 0;
return m_context != 0 && !m_lost;
}
bool QGLXContext::m_queriedDummyContext = false;

View File

@ -89,6 +89,8 @@ private:
bool m_isPBufferCurrent;
int m_swapInterval;
bool m_ownsContext;
GLenum (APIENTRY * m_getGraphicsResetStatus)();
bool m_lost;
static bool m_queriedDummyContext;
static bool m_supportsThreading;
};