summaryrefslogtreecommitdiff
path: root/src/graphics/GLXContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/GLXContext.cpp')
-rw-r--r--src/graphics/GLXContext.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/graphics/GLXContext.cpp b/src/graphics/GLXContext.cpp
new file mode 100644
index 0000000..ff5477a
--- /dev/null
+++ b/src/graphics/GLXContext.cpp
@@ -0,0 +1,265 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+
+#include "GLXContext.h"
+#include "GLContextAttribs.h"
+#include "X11Display.h"
+
+#include "../base/Exception.h"
+#include "../base/Logger.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+
+#include <X11/extensions/xf86vmode.h>
+
+#include <iostream>
+#include <stdlib.h>
+#include <limits>
+
+
+namespace avg {
+
+using namespace std;
+using namespace boost;
+
+GLXContext::GLXContext(const GLConfig& glConfig, const IntPoint& windowSize,
+ const SDL_SysWMinfo* pSDLWMInfo)
+ : GLContext(windowSize, pSDLWMInfo)
+{
+ GLConfig config = glConfig;
+ try {
+ createGLXContext(config, windowSize, pSDLWMInfo, true);
+ } catch (const Exception &e) {
+ if (e.getCode() == AVG_ERR_DEBUG_CONTEXT_FAILED) {
+ createGLXContext(config, windowSize, pSDLWMInfo, false);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+ }
+ init(config, true);
+}
+
+static bool s_bX11Error;
+static bool s_bDumpX11ErrorMsg;
+static int (*s_DefaultErrorHandler) (::Display *, XErrorEvent *);
+
+int X11ErrorHandler(::Display * pDisplay, XErrorEvent * pErrEvent)
+{
+ if (s_bDumpX11ErrorMsg) {
+ char errorString[128];
+ XGetErrorText(pDisplay, pErrEvent->error_code, errorString, 128);
+ cerr << "X11 error creating GL context: " << errorString <<
+ "\n\tMajor opcode of failed request: " << (int)(pErrEvent->request_code) <<
+ "\n\tMinor opcode of failed request: " << (int)(pErrEvent->minor_code) <<
+ "\n";
+ }
+ s_bX11Error = true;
+ return 0;
+}
+
+void GLXContext::createGLXContext(GLConfig& glConfig, const IntPoint& windowSize,
+ const SDL_SysWMinfo* pSDLWMInfo, bool bUseDebugBit)
+{
+ Window win = 0;
+ s_bX11Error = false;
+ s_bDumpX11ErrorMsg = true;
+ s_DefaultErrorHandler = XSetErrorHandler(X11ErrorHandler);
+
+ m_pDisplay = getX11Display(pSDLWMInfo);
+
+ GLContextAttribs attrs;
+ attrs.append(GLX_X_RENDERABLE, 1);
+ attrs.append(GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT);
+ attrs.append(GLX_RENDER_TYPE, GLX_RGBA_BIT);
+ attrs.append(GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR);
+ attrs.append(GLX_DEPTH_SIZE, 0);
+ attrs.append(GLX_STENCIL_SIZE, 8);
+ attrs.append(GLX_DOUBLEBUFFER, 1);
+ attrs.append(GLX_RED_SIZE, 8);
+ attrs.append(GLX_GREEN_SIZE, 8);
+ attrs.append(GLX_BLUE_SIZE, 8);
+ attrs.append(GLX_ALPHA_SIZE, 0);
+
+ int fbCount;
+ GLXFBConfig* pFBConfig = glXChooseFBConfig(m_pDisplay, DefaultScreen(m_pDisplay),
+ attrs.get(), &fbCount);
+ if (!pFBConfig) {
+ throw Exception(AVG_ERR_UNSUPPORTED, "Creating OpenGL context failed.");
+ }
+
+ // Find the config with the appropriate number of multisample samples.
+ int bestConfig = -1;
+ int bestSamples = -1;
+ int bestCaveat = std::numeric_limits<int>::max();
+ for (int i=0; i<fbCount; ++i) {
+ XVisualInfo* pVisualInfo = glXGetVisualFromFBConfig(m_pDisplay, pFBConfig[i]);
+ if (pVisualInfo && pVisualInfo->depth == 24) {
+ int numBuffers;
+ int numSamples;
+ int caveat;
+ glXGetFBConfigAttrib(m_pDisplay, pFBConfig[i], GLX_SAMPLE_BUFFERS,
+ &numBuffers);
+ glXGetFBConfigAttrib(m_pDisplay, pFBConfig[i], GLX_SAMPLES, &numSamples);
+ glXGetFBConfigAttrib(m_pDisplay, pFBConfig[i], GLX_CONFIG_CAVEAT, &caveat);
+ if (numSamples == 0) {
+ // Configs without multisampling have numBuffers == 0 and numSamples == 0,
+ // but the corresponding libavg config is multisamplesamples == 1.
+ numSamples = 1;
+ }
+ if ((numSamples > bestSamples && numSamples <= glConfig.m_MultiSampleSamples)
+ || (numSamples == bestSamples && caveat < bestCaveat))
+ {
+ // A config is better than the last one in two cases:
+ // 1) it has more samples per pixel (but not more than requested) or
+ // 2) it has the same number of samples per pixel but a better caveat
+ // value.
+ bestCaveat = caveat;
+ bestConfig = i;
+ bestSamples = numSamples;
+ }
+ XFree(pVisualInfo);
+ }
+ }
+
+ GLXFBConfig fbConfig = pFBConfig[bestConfig];
+ XFree(pFBConfig);
+ XVisualInfo* pVisualInfo = glXGetVisualFromFBConfig(m_pDisplay, fbConfig);
+
+ if (pSDLWMInfo) {
+ win = createChildWindow(pSDLWMInfo, pVisualInfo, windowSize, m_Colormap);
+ }
+
+ if (haveARBCreateContext()) {
+ GLContextAttribs attrs;
+ if (glConfig.m_bGLES) {
+ attrs.append(GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT);
+ attrs.append(GLX_CONTEXT_MAJOR_VERSION_ARB, 2);
+ attrs.append(GLX_CONTEXT_MINOR_VERSION_ARB, 0);
+ }
+ if (glConfig.m_bUseDebugContext && bUseDebugBit) {
+ attrs.append(GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB);
+ }
+ PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB =
+ (PFNGLXCREATECONTEXTATTRIBSARBPROC)
+ getglXProcAddress("glXCreateContextAttribsARB");
+
+ s_bDumpX11ErrorMsg = false;
+ m_Context = CreateContextAttribsARB(m_pDisplay, fbConfig, 0, 1, attrs.get());
+ s_bDumpX11ErrorMsg = true;
+ throwOnXError(AVG_ERR_DEBUG_CONTEXT_FAILED);
+ } else {
+ m_Context = glXCreateContext(m_pDisplay, pVisualInfo, 0, GL_TRUE);
+ }
+ AVG_ASSERT(m_Context);
+ if (pSDLWMInfo) {
+ setCurrent();
+ glXMakeCurrent(m_pDisplay, win, m_Context);
+ } else {
+ Pixmap pmp = XCreatePixmap(m_pDisplay,
+ RootWindow(m_pDisplay, pVisualInfo->screen), 8, 8, pVisualInfo->depth);
+ GLXPixmap pixmap = glXCreateGLXPixmap(m_pDisplay, pVisualInfo, pmp);
+
+ glXMakeCurrent(m_pDisplay, pixmap, m_Context);
+ }
+ XSetErrorHandler(s_DefaultErrorHandler);
+
+ throwOnXError();
+ glConfig.m_MultiSampleSamples = bestSamples;
+ m_Drawable = glXGetCurrentDrawable();
+}
+
+GLXContext::~GLXContext()
+{
+ deleteObjects();
+ if (m_Context && ownsContext()) {
+ glXMakeCurrent(m_pDisplay, 0, 0);
+ glXDestroyContext(m_pDisplay, m_Context);
+ m_Context = 0;
+ XDestroyWindow(m_pDisplay, m_Drawable);
+ XFreeColormap(m_pDisplay, m_Colormap);
+ }
+}
+
+void GLXContext::throwOnXError( int code)
+{
+ if (s_bX11Error) {
+ throw Exception(code, "X error creating OpenGL context.");
+ }
+}
+
+void GLXContext::activate()
+{
+ glXMakeCurrent(m_pDisplay, m_Drawable, m_Context);
+ setCurrent();
+}
+
+bool GLXContext::initVBlank(int rate)
+{
+ static bool s_bVBlankActive = false;
+ if (rate > 0) {
+ if (getenv("__GL_SYNC_TO_VBLANK") != 0) {
+ AVG_LOG_WARNING("__GL_SYNC_TO_VBLANK set. This interferes with libavg vblank handling.");
+ s_bVBlankActive = false;
+ return false;
+ }
+ if (!queryGLXExtension("GLX_EXT_swap_control")) {
+ AVG_LOG_WARNING("Linux VBlank setup failed: OpenGL Extension not supported.");
+ s_bVBlankActive = false;
+ return false;
+ }
+
+ glproc::SwapIntervalEXT(m_pDisplay, m_Drawable, rate);
+ s_bVBlankActive = true;
+ return true;
+
+ } else {
+ if (s_bVBlankActive) {
+ glproc::SwapIntervalEXT(m_pDisplay, m_Drawable, 0);
+ s_bVBlankActive = false;
+ }
+ return false;
+ }
+}
+
+bool GLXContext::useDepthBuffer() const
+{
+ // NVidia GLX GLES doesn't allow framebuffer stencil without depth.
+ return true;
+}
+
+void GLXContext::swapBuffers()
+{
+ glXSwapBuffers(m_pDisplay, m_Drawable);
+}
+
+bool GLXContext::haveARBCreateContext()
+{
+ static bool s_bExtensionChecked = false;
+ static bool s_bHaveExtension = false;
+ if (!s_bExtensionChecked) {
+ s_bExtensionChecked = true;
+ s_bHaveExtension = (queryGLXExtension("GLX_ARB_create_context"));
+ }
+ return s_bHaveExtension;
+}
+
+}