| // | |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // FunctionsGLX.cpp: Implements the FunctionsGLX class. | |
| #define ANGLE_SKIP_GLX_DEFINES 1 | |
| #include "libANGLE/renderer/gl/glx/FunctionsGLX.h" | |
| #undef ANGLE_SKIP_GLX_DEFINES | |
| // We can only include glx.h in files which do not include ANGLE's GLES | |
| // headers, to avoid doubly-defined GLenum macros, typedefs, etc. | |
| #include <GL/glx.h> | |
| #include <dlfcn.h> | |
| #include <algorithm> | |
| #include "common/string_utils.h" | |
| #include "libANGLE/renderer/gl/glx/functionsglx_typedefs.h" | |
| namespace rx | |
| { | |
| void *FunctionsGLX::sLibHandle = nullptr; | |
| template <typename T> | |
| static bool GetProc(PFNGETPROCPROC getProc, T *member, const char *name) | |
| { | |
| *member = reinterpret_cast<T>(getProc(name)); | |
| return *member != nullptr; | |
| } | |
| struct FunctionsGLX::GLXFunctionTable | |
| { | |
| GLXFunctionTable() | |
| : createContextPtr(nullptr), | |
| destroyContextPtr(nullptr), | |
| makeCurrentPtr(nullptr), | |
| swapBuffersPtr(nullptr), | |
| queryExtensionPtr(nullptr), | |
| queryVersionPtr(nullptr), | |
| getCurrentContextPtr(nullptr), | |
| getCurrentDrawablePtr(nullptr), | |
| waitXPtr(nullptr), | |
| waitGLPtr(nullptr), | |
| getClientStringPtr(nullptr), | |
| queryExtensionsStringPtr(nullptr), | |
| getFBConfigsPtr(nullptr), | |
| chooseFBConfigPtr(nullptr), | |
| getFBConfigAttribPtr(nullptr), | |
| getVisualFromFBConfigPtr(nullptr), | |
| createWindowPtr(nullptr), | |
| destroyWindowPtr(nullptr), | |
| createPbufferPtr(nullptr), | |
| destroyPbufferPtr(nullptr), | |
| queryDrawablePtr(nullptr), | |
| createContextAttribsARBPtr(nullptr), | |
| swapIntervalEXTPtr(nullptr), | |
| swapIntervalMESAPtr(nullptr), | |
| swapIntervalSGIPtr(nullptr), | |
| getSyncValuesOMLPtr(nullptr) | |
| {} | |
| // GLX 1.0 | |
| PFNGLXCREATECONTEXTPROC createContextPtr; | |
| PFNGLXDESTROYCONTEXTPROC destroyContextPtr; | |
| PFNGLXMAKECURRENTPROC makeCurrentPtr; | |
| PFNGLXSWAPBUFFERSPROC swapBuffersPtr; | |
| PFNGLXQUERYEXTENSIONPROC queryExtensionPtr; | |
| PFNGLXQUERYVERSIONPROC queryVersionPtr; | |
| PFNGLXGETCURRENTCONTEXTPROC getCurrentContextPtr; | |
| PFNGLXGETCURRENTDRAWABLEPROC getCurrentDrawablePtr; | |
| PFNGLXWAITXPROC waitXPtr; | |
| PFNGLXWAITGLPROC waitGLPtr; | |
| // GLX 1.1 | |
| PFNGLXGETCLIENTSTRINGPROC getClientStringPtr; | |
| PFNGLXQUERYEXTENSIONSSTRINGPROC queryExtensionsStringPtr; | |
| // GLX 1.3 | |
| PFNGLXGETFBCONFIGSPROC getFBConfigsPtr; | |
| PFNGLXCHOOSEFBCONFIGPROC chooseFBConfigPtr; | |
| PFNGLXGETFBCONFIGATTRIBPROC getFBConfigAttribPtr; | |
| PFNGLXGETVISUALFROMFBCONFIGPROC getVisualFromFBConfigPtr; | |
| PFNGLXCREATEWINDOWPROC createWindowPtr; | |
| PFNGLXDESTROYWINDOWPROC destroyWindowPtr; | |
| PFNGLXCREATEPBUFFERPROC createPbufferPtr; | |
| PFNGLXDESTROYPBUFFERPROC destroyPbufferPtr; | |
| PFNGLXQUERYDRAWABLEPROC queryDrawablePtr; | |
| // GLX_ARB_create_context | |
| PFNGLXCREATECONTEXTATTRIBSARBPROC createContextAttribsARBPtr; | |
| // GLX_EXT_swap_control | |
| PFNGLXSWAPINTERVALEXTPROC swapIntervalEXTPtr; | |
| // GLX_MESA_swap_control | |
| PFNGLXSWAPINTERVALMESAPROC swapIntervalMESAPtr; | |
| // GLX_SGI_swap_control | |
| PFNGLXSWAPINTERVALSGIPROC swapIntervalSGIPtr; | |
| // GLX_OML_sync_control | |
| PFNGLXGETSYNCVALUESOMLPROC getSyncValuesOMLPtr; | |
| }; | |
| FunctionsGLX::FunctionsGLX() | |
| : majorVersion(0), | |
| minorVersion(0), | |
| mXDisplay(nullptr), | |
| mXScreen(-1), | |
| mFnPtrs(new GLXFunctionTable()) | |
| {} | |
| FunctionsGLX::~FunctionsGLX() | |
| { | |
| delete mFnPtrs; | |
| terminate(); | |
| } | |
| bool FunctionsGLX::initialize(Display *xDisplay, int screen, std::string *errorString) | |
| { | |
| terminate(); | |
| mXDisplay = xDisplay; | |
| mXScreen = screen; | |
| #if !defined(ANGLE_LINK_GLX) | |
| // Some OpenGL implementations can't handle having this library | |
| // handle closed while there's any X window still open against | |
| // which a GLXWindow was ever created. | |
| if (!sLibHandle) | |
| { | |
| sLibHandle = dlopen("libGL.so.1", RTLD_NOW); | |
| if (!sLibHandle) | |
| { | |
| *errorString = std::string("Could not dlopen libGL.so.1: ") + dlerror(); | |
| return false; | |
| } | |
| } | |
| getProc = reinterpret_cast<PFNGETPROCPROC>(dlsym(sLibHandle, "glXGetProcAddress")); | |
| if (!getProc) | |
| { | |
| getProc = reinterpret_cast<PFNGETPROCPROC>(dlsym(sLibHandle, "glXGetProcAddressARB")); | |
| } | |
| if (!getProc) | |
| { | |
| *errorString = "Could not retrieve glXGetProcAddress"; | |
| return false; | |
| } | |
| #else | |
| getProc = reinterpret_cast<PFNGETPROCPROC>(glXGetProcAddress); | |
| #endif | |
| #define GET_PROC_OR_ERROR(MEMBER, NAME) \ | |
| do \ | |
| { \ | |
| if (!GetProc(getProc, MEMBER, #NAME)) \ | |
| { \ | |
| *errorString = "Could not load GLX entry point " #NAME; \ | |
| return false; \ | |
| } \ | |
| } while (0) | |
| #if !defined(ANGLE_LINK_GLX) | |
| # define GET_FNPTR_OR_ERROR(MEMBER, NAME) GET_PROC_OR_ERROR(MEMBER, NAME) | |
| #else | |
| # define GET_FNPTR_OR_ERROR(MEMBER, NAME) *MEMBER = NAME | |
| #endif | |
| // GLX 1.0 | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->createContextPtr, glXCreateContext); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->destroyContextPtr, glXDestroyContext); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->makeCurrentPtr, glXMakeCurrent); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->swapBuffersPtr, glXSwapBuffers); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->queryExtensionPtr, glXQueryExtension); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->queryVersionPtr, glXQueryVersion); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getCurrentContextPtr, glXGetCurrentContext); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getCurrentDrawablePtr, glXGetCurrentDrawable); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->waitXPtr, glXWaitX); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->waitGLPtr, glXWaitGL); | |
| // GLX 1.1 | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getClientStringPtr, glXGetClientString); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->queryExtensionsStringPtr, glXQueryExtensionsString); | |
| // Check we have a working GLX | |
| { | |
| int errorBase; | |
| int eventBase; | |
| if (!queryExtension(&errorBase, &eventBase)) | |
| { | |
| *errorString = "GLX is not present."; | |
| return false; | |
| } | |
| } | |
| // Check we have a supported version of GLX | |
| if (!queryVersion(&majorVersion, &minorVersion)) | |
| { | |
| *errorString = "Could not query the GLX version."; | |
| return false; | |
| } | |
| if (majorVersion != 1 || minorVersion < 3) | |
| { | |
| *errorString = "Unsupported GLX version (requires at least 1.3)."; | |
| return false; | |
| } | |
| const char *extensions = queryExtensionsString(); | |
| if (!extensions) | |
| { | |
| *errorString = "glXQueryExtensionsString returned NULL"; | |
| return false; | |
| } | |
| angle::SplitStringAlongWhitespace(extensions, &mExtensions); | |
| // GLX 1.3 | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getFBConfigsPtr, glXGetFBConfigs); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->chooseFBConfigPtr, glXChooseFBConfig); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getFBConfigAttribPtr, glXGetFBConfigAttrib); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->getVisualFromFBConfigPtr, glXGetVisualFromFBConfig); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->createWindowPtr, glXCreateWindow); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->destroyWindowPtr, glXDestroyWindow); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->createPbufferPtr, glXCreatePbuffer); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->destroyPbufferPtr, glXDestroyPbuffer); | |
| GET_FNPTR_OR_ERROR(&mFnPtrs->queryDrawablePtr, glXQueryDrawable); | |
| // Extensions | |
| if (hasExtension("GLX_ARB_create_context")) | |
| { | |
| GET_PROC_OR_ERROR(&mFnPtrs->createContextAttribsARBPtr, glXCreateContextAttribsARB); | |
| } | |
| if (hasExtension("GLX_EXT_swap_control")) | |
| { | |
| GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalEXTPtr, glXSwapIntervalEXT); | |
| } | |
| if (hasExtension("GLX_MESA_swap_control")) | |
| { | |
| GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalMESAPtr, glXSwapIntervalMESA); | |
| } | |
| if (hasExtension("GLX_SGI_swap_control")) | |
| { | |
| GET_PROC_OR_ERROR(&mFnPtrs->swapIntervalSGIPtr, glXSwapIntervalSGI); | |
| } | |
| if (hasExtension("GLX_OML_sync_control")) | |
| { | |
| GET_PROC_OR_ERROR(&mFnPtrs->getSyncValuesOMLPtr, glXGetSyncValuesOML); | |
| } | |
| #undef GET_FNPTR_OR_ERROR | |
| #undef GET_PROC_OR_ERROR | |
| *errorString = ""; | |
| return true; | |
| } | |
| void FunctionsGLX::terminate() {} | |
| bool FunctionsGLX::hasExtension(const char *extension) const | |
| { | |
| return std::find(mExtensions.begin(), mExtensions.end(), extension) != mExtensions.end(); | |
| } | |
| Display *FunctionsGLX::getDisplay() const | |
| { | |
| return mXDisplay; | |
| } | |
| int FunctionsGLX::getScreen() const | |
| { | |
| return mXScreen; | |
| } | |
| // GLX functions | |
| // GLX 1.0 | |
| glx::Context FunctionsGLX::createContext(XVisualInfo *visual, glx::Context share, bool direct) const | |
| { | |
| GLXContext shareCtx = reinterpret_cast<GLXContext>(share); | |
| GLXContext context = mFnPtrs->createContextPtr(mXDisplay, visual, shareCtx, direct); | |
| return reinterpret_cast<glx::Context>(context); | |
| } | |
| void FunctionsGLX::destroyContext(glx::Context context) const | |
| { | |
| GLXContext ctx = reinterpret_cast<GLXContext>(context); | |
| mFnPtrs->destroyContextPtr(mXDisplay, ctx); | |
| } | |
| Bool FunctionsGLX::makeCurrent(glx::Drawable drawable, glx::Context context) const | |
| { | |
| GLXContext ctx = reinterpret_cast<GLXContext>(context); | |
| return mFnPtrs->makeCurrentPtr(mXDisplay, drawable, ctx); | |
| } | |
| void FunctionsGLX::swapBuffers(glx::Drawable drawable) const | |
| { | |
| mFnPtrs->swapBuffersPtr(mXDisplay, drawable); | |
| } | |
| Bool FunctionsGLX::queryExtension(int *errorBase, int *event) const | |
| { | |
| return mFnPtrs->queryExtensionPtr(mXDisplay, errorBase, event); | |
| } | |
| Bool FunctionsGLX::queryVersion(int *major, int *minor) const | |
| { | |
| return mFnPtrs->queryVersionPtr(mXDisplay, major, minor); | |
| } | |
| glx::Context FunctionsGLX::getCurrentContext() const | |
| { | |
| GLXContext context = mFnPtrs->getCurrentContextPtr(); | |
| return reinterpret_cast<glx::Context>(context); | |
| } | |
| glx::Drawable FunctionsGLX::getCurrentDrawable() const | |
| { | |
| GLXDrawable drawable = mFnPtrs->getCurrentDrawablePtr(); | |
| return reinterpret_cast<glx::Drawable>(drawable); | |
| } | |
| void FunctionsGLX::waitX() const | |
| { | |
| mFnPtrs->waitXPtr(); | |
| } | |
| void FunctionsGLX::waitGL() const | |
| { | |
| mFnPtrs->waitGLPtr(); | |
| } | |
| // GLX 1.1 | |
| const char *FunctionsGLX::getClientString(int name) const | |
| { | |
| return mFnPtrs->getClientStringPtr(mXDisplay, name); | |
| } | |
| const char *FunctionsGLX::queryExtensionsString() const | |
| { | |
| return mFnPtrs->queryExtensionsStringPtr(mXDisplay, mXScreen); | |
| } | |
| // GLX 1.4 | |
| glx::FBConfig *FunctionsGLX::getFBConfigs(int *nElements) const | |
| { | |
| GLXFBConfig *configs = mFnPtrs->getFBConfigsPtr(mXDisplay, mXScreen, nElements); | |
| return reinterpret_cast<glx::FBConfig *>(configs); | |
| } | |
| glx::FBConfig *FunctionsGLX::chooseFBConfig(const int *attribList, int *nElements) const | |
| { | |
| GLXFBConfig *configs = mFnPtrs->chooseFBConfigPtr(mXDisplay, mXScreen, attribList, nElements); | |
| return reinterpret_cast<glx::FBConfig *>(configs); | |
| } | |
| int FunctionsGLX::getFBConfigAttrib(glx::FBConfig config, int attribute, int *value) const | |
| { | |
| GLXFBConfig cfg = reinterpret_cast<GLXFBConfig>(config); | |
| return mFnPtrs->getFBConfigAttribPtr(mXDisplay, cfg, attribute, value); | |
| } | |
| XVisualInfo *FunctionsGLX::getVisualFromFBConfig(glx::FBConfig config) const | |
| { | |
| GLXFBConfig cfg = reinterpret_cast<GLXFBConfig>(config); | |
| return mFnPtrs->getVisualFromFBConfigPtr(mXDisplay, cfg); | |
| } | |
| GLXWindow FunctionsGLX::createWindow(glx::FBConfig config, | |
| Window window, | |
| const int *attribList) const | |
| { | |
| GLXFBConfig cfg = reinterpret_cast<GLXFBConfig>(config); | |
| return mFnPtrs->createWindowPtr(mXDisplay, cfg, window, attribList); | |
| } | |
| void FunctionsGLX::destroyWindow(glx::Window window) const | |
| { | |
| mFnPtrs->destroyWindowPtr(mXDisplay, window); | |
| } | |
| glx::Pbuffer FunctionsGLX::createPbuffer(glx::FBConfig config, const int *attribList) const | |
| { | |
| GLXFBConfig cfg = reinterpret_cast<GLXFBConfig>(config); | |
| return mFnPtrs->createPbufferPtr(mXDisplay, cfg, attribList); | |
| } | |
| void FunctionsGLX::destroyPbuffer(glx::Pbuffer pbuffer) const | |
| { | |
| mFnPtrs->destroyPbufferPtr(mXDisplay, pbuffer); | |
| } | |
| void FunctionsGLX::queryDrawable(glx::Drawable drawable, int attribute, unsigned int *value) const | |
| { | |
| mFnPtrs->queryDrawablePtr(mXDisplay, drawable, attribute, value); | |
| } | |
| // GLX_ARB_create_context | |
| glx::Context FunctionsGLX::createContextAttribsARB(glx::FBConfig config, | |
| glx::Context shareContext, | |
| Bool direct, | |
| const int *attribList) const | |
| { | |
| GLXContext shareCtx = reinterpret_cast<GLXContext>(shareContext); | |
| GLXFBConfig cfg = reinterpret_cast<GLXFBConfig>(config); | |
| GLXContext ctx = | |
| mFnPtrs->createContextAttribsARBPtr(mXDisplay, cfg, shareCtx, direct, attribList); | |
| return reinterpret_cast<glx::Context>(ctx); | |
| } | |
| void FunctionsGLX::swapIntervalEXT(glx::Drawable drawable, int intervals) const | |
| { | |
| mFnPtrs->swapIntervalEXTPtr(mXDisplay, drawable, intervals); | |
| } | |
| int FunctionsGLX::swapIntervalMESA(int intervals) const | |
| { | |
| return mFnPtrs->swapIntervalMESAPtr(intervals); | |
| } | |
| int FunctionsGLX::swapIntervalSGI(int intervals) const | |
| { | |
| return mFnPtrs->swapIntervalSGIPtr(intervals); | |
| } | |
| bool FunctionsGLX::getSyncValuesOML(glx::Drawable drawable, | |
| int64_t *ust, | |
| int64_t *msc, | |
| int64_t *sbc) const | |
| { | |
| return mFnPtrs->getSyncValuesOMLPtr(mXDisplay, drawable, ust, msc, sbc); | |
| } | |
| } // namespace rx |