diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/windowing/X11/GLContextGLX.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/xbmc/windowing/X11/GLContextGLX.cpp b/xbmc/windowing/X11/GLContextGLX.cpp new file mode 100644 index 0000000..3c31c22 --- /dev/null +++ b/xbmc/windowing/X11/GLContextGLX.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GLContextGLX.h" + +#include "utils/log.h" + +#include <GL/glx.h> + +#include "system_gl.h" + +using namespace KODI::WINDOWING::X11; + +CGLContextGLX::CGLContextGLX(Display *dpy) : CGLContext(dpy) +{ + m_extPrefix = "GLX_"; + m_vsyncMode = 0; +} + +bool CGLContextGLX::Refresh(bool force, int screen, Window glWindow, bool &newContext) +{ + bool retVal = false; + m_glxWindow = glWindow; + m_nScreen = screen; + + // refresh context + if (m_glxContext && !force) + { + CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshGlxContext: refreshing context"); + glXMakeCurrent(m_dpy, None, NULL); + glXMakeCurrent(m_dpy, glWindow, m_glxContext); + return true; + } + + // create context + + XVisualInfo vMask; + XVisualInfo *visuals; + XVisualInfo *vInfo = NULL; + int availableVisuals = 0; + vMask.screen = screen; + XWindowAttributes winAttr; + + /* Assume a depth of 24 in case the below calls to XGetWindowAttributes() + or XGetVisualInfo() fail. That shouldn't happen unless something is + fatally wrong, but lets prepare for everything. */ + vMask.depth = 24; + + if (XGetWindowAttributes(m_dpy, glWindow, &winAttr)) + { + vMask.visualid = XVisualIDFromVisual(winAttr.visual); + vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals); + if (!vInfo) + CLog::Log(LOGWARNING, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid); + else if(!IsSuitableVisual(vInfo)) + { + CLog::Log(LOGWARNING, + "Visual 0x{:x} of the window is not suitable, looking for another one...", + (unsigned)vInfo->visualid); + vMask.depth = vInfo->depth; + XFree(vInfo); + vInfo = NULL; + } + } + else + CLog::Log(LOGWARNING, "Failed to get window attributes"); + + /* As per glXMakeCurrent documentation, we have to use the same visual as + m_glWindow. Since that was not suitable for use, we try to use another + one with the same depth and hope that the used implementation is less + strict than the documentation. */ + if (!vInfo) + { + visuals = XGetVisualInfo(m_dpy, VisualScreenMask | VisualDepthMask, &vMask, &availableVisuals); + for (int i = 0; i < availableVisuals; i++) + { + if (IsSuitableVisual(&visuals[i])) + { + vMask.visualid = visuals[i].visualid; + vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals); + break; + } + } + XFree(visuals); + } + + if (vInfo) + { + CLog::Log(LOGINFO, "Using visual 0x{:x}", (unsigned)vInfo->visualid); + if (m_glxContext) + { + glXMakeCurrent(m_dpy, None, NULL); + glXDestroyContext(m_dpy, m_glxContext); + XSync(m_dpy, False); + } + + if ((m_glxContext = glXCreateContext(m_dpy, vInfo, NULL, True))) + { + // make this context current + glXMakeCurrent(m_dpy, glWindow, m_glxContext); + retVal = true; + newContext = true; + } + else + CLog::Log(LOGERROR, "GLX Error: Could not create context"); + + XFree(vInfo); + } + else + { + CLog::Log(LOGERROR, "GLX Error: vInfo is NULL!"); + } + + return retVal; +} + +void CGLContextGLX::Destroy() +{ + glXMakeCurrent(m_dpy, None, NULL); + glXDestroyContext(m_dpy, m_glxContext); + m_glxContext = 0; +} + +void CGLContextGLX::Detach() +{ + glXMakeCurrent(m_dpy, None, NULL); +} + +bool CGLContextGLX::IsSuitableVisual(XVisualInfo *vInfo) +{ + int value; + + if (glXGetConfig(m_dpy, vInfo, GLX_RGBA, &value) || !value) + return false; + if (glXGetConfig(m_dpy, vInfo, GLX_DOUBLEBUFFER, &value) || !value) + return false; + if (glXGetConfig(m_dpy, vInfo, GLX_RED_SIZE, &value) || value < 8) + return false; + if (glXGetConfig(m_dpy, vInfo, GLX_GREEN_SIZE, &value) || value < 8) + return false; + if (glXGetConfig(m_dpy, vInfo, GLX_BLUE_SIZE, &value) || value < 8) + return false; + if (glXGetConfig(m_dpy, vInfo, GLX_DEPTH_SIZE, &value) || value < 24) + return false; + + return true; +} + +void CGLContextGLX::SetVSync(bool enable) +{ + // turn of current setting first + if(m_glXSwapIntervalEXT) + m_glXSwapIntervalEXT(m_dpy, m_glxWindow, 0); + else if(m_glXSwapIntervalMESA) + m_glXSwapIntervalMESA(0); + + m_iVSyncErrors = 0; + + if(!enable) + return; + + if (m_glXSwapIntervalEXT) + { + m_glXSwapIntervalEXT(m_dpy, m_glxWindow, 1); + m_vsyncMode = 6; + } + if (m_glXSwapIntervalMESA) + { + if(m_glXSwapIntervalMESA(1) == 0) + m_vsyncMode = 2; + else + CLog::Log(LOGWARNING, "{} - glXSwapIntervalMESA failed", __FUNCTION__); + } + if (m_glXWaitVideoSyncSGI && m_glXGetVideoSyncSGI && !m_vsyncMode) + { + unsigned int count; + if(m_glXGetVideoSyncSGI(&count) == 0) + m_vsyncMode = 3; + else + CLog::Log(LOGWARNING, "{} - glXGetVideoSyncSGI failed, glcontext probably not direct", + __FUNCTION__); + } +} + +void CGLContextGLX::SwapBuffers() +{ + if (m_vsyncMode == 3) + { + glFinish(); + unsigned int before = 0, after = 0; + if (m_glXGetVideoSyncSGI(&before) != 0) + CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count", + __FUNCTION__); + + glXSwapBuffers(m_dpy, m_glxWindow); + glFinish(); + + if(m_glXGetVideoSyncSGI(&after) != 0) + CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count", + __FUNCTION__); + + if (after == before) + m_iVSyncErrors = 1; + else + m_iVSyncErrors--; + + if (m_iVSyncErrors > 0) + { + CLog::Log(LOGINFO, "GL: retrace count didn't change after buffer swap, switching to vsync mode 4"); + m_iVSyncErrors = 0; + m_vsyncMode = 4; + } + + if (m_iVSyncErrors < -200) + { + CLog::Log( + LOGINFO, + "GL: retrace count change for {} consecutive buffer swap, switching to vsync mode 2", + -m_iVSyncErrors); + m_iVSyncErrors = 0; + m_vsyncMode = 2; + } + } + else if (m_vsyncMode == 4) + { + glFinish(); + unsigned int before = 0, swap = 0, after = 0; + if (m_glXGetVideoSyncSGI(&before) != 0) + CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count", + __FUNCTION__); + + if(m_glXWaitVideoSyncSGI(2, (before+1)%2, &swap) != 0) + CLog::Log(LOGERROR, "{} - glXWaitVideoSyncSGI - Returned error", __FUNCTION__); + + glXSwapBuffers(m_dpy, m_glxWindow); + glFinish(); + + if (m_glXGetVideoSyncSGI(&after) != 0) + CLog::Log(LOGERROR, "{} - glXGetVideoSyncSGI - Failed to get current retrace count", + __FUNCTION__); + + if (after == before) + CLog::Log(LOGERROR, "{} - glXWaitVideoSyncSGI - Woke up early", __FUNCTION__); + + if (after > before + 1) + m_iVSyncErrors++; + else + m_iVSyncErrors = 0; + + if (m_iVSyncErrors > 30) + { + CLog::Log(LOGINFO, "GL: retrace count seems to be changing due to the swapbuffers call, switching to vsync mode 3"); + m_vsyncMode = 3; + m_iVSyncErrors = 0; + } + } + else + glXSwapBuffers(m_dpy, m_glxWindow); +} + +void CGLContextGLX::QueryExtensions() +{ + m_extensions = " "; + m_extensions += glXQueryExtensionsString(m_dpy, m_nScreen); + m_extensions += " "; + + CLog::Log(LOGDEBUG, "GLX_EXTENSIONS:{}", m_extensions); + + if (IsExtSupported("GLX_SGI_video_sync")) + m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI"); + else + m_glXWaitVideoSyncSGI = NULL; + + if (IsExtSupported("GLX_SGI_video_sync")) + m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI"); + else + m_glXGetVideoSyncSGI = NULL; + + if (IsExtSupported("GLX_MESA_swap_control")) + m_glXSwapIntervalMESA = (int (*)(int))glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA"); + else + m_glXSwapIntervalMESA = NULL; + + if (IsExtSupported("GLX_EXT_swap_control")) + m_glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT"); + else + m_glXSwapIntervalEXT = NULL; +} |