diff options
Diffstat (limited to 'gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp')
-rwxr-xr-x | gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp new file mode 100755 index 000000000..e98401d0a --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp @@ -0,0 +1,981 @@ +// +// Copyright (c) 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. +// + +// DisplayGLX.cpp: GLX implementation of egl::Display + +#include "libANGLE/renderer/gl/glx/DisplayGLX.h" + +#include <EGL/eglext.h> +#include <algorithm> +#include <cstring> +#include <fstream> + +#include "common/debug.h" +#include "libANGLE/Config.h" +#include "libANGLE/Display.h" +#include "libANGLE/Surface.h" +#include "libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h" +#include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h" +#include "libANGLE/renderer/gl/RendererGL.h" +#include "libANGLE/renderer/gl/renderergl_utils.h" +#include "third_party/libXNVCtrl/NVCtrl.h" +#include "third_party/libXNVCtrl/NVCtrlLib.h" + +namespace +{ + +// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion". +// Return empty string on failing. +egl::Error GetAMDDriverVersion(std::string *version) +{ + *version = ""; + + const char kAMDDriverInfoFileName[] = "/etc/ati/amdpcsdb.default"; + std::ifstream file(kAMDDriverInfoFileName); + + if (!file) + { + return egl::Error(EGL_SUCCESS); + } + + std::string line; + while (std::getline(file, line)) + { + static const char kReleaseVersion[] = "ReleaseVersion="; + if (line.compare(0, std::strlen(kReleaseVersion), kReleaseVersion) != 0) + { + continue; + } + + const size_t begin = line.find_first_of("0123456789"); + if (begin == std::string::npos) + { + continue; + } + + const size_t end = line.find_first_not_of("0123456789.", begin); + if (end == std::string::npos) + { + *version = line.substr(begin); + } + else + { + *version = line.substr(begin, end - begin); + } + return egl::Error(EGL_SUCCESS); + } + return egl::Error(EGL_SUCCESS); +} + +} // anonymous namespace + +namespace rx +{ + +static int IgnoreX11Errors(Display *, XErrorEvent *) +{ + return 0; +} + +SwapControlData::SwapControlData() + : targetSwapInterval(0), + maxSwapInterval(-1), + currentSwapInterval(-1) +{ +} + +class FunctionsGLGLX : public FunctionsGL +{ + public: + FunctionsGLGLX(PFNGETPROCPROC getProc) + : mGetProc(getProc) + { + } + + ~FunctionsGLGLX() override {} + + private: + void *loadProcAddress(const std::string &function) override + { + return reinterpret_cast<void*>(mGetProc(function.c_str())); + } + + PFNGETPROCPROC mGetProc; +}; + +DisplayGLX::DisplayGLX() + : DisplayGL(), + mFunctionsGL(nullptr), + mRequestedVisual(-1), + mContextConfig(nullptr), + mContext(nullptr), + mDummyPbuffer(0), + mUsesNewXDisplay(false), + mIsMesa(false), + mHasMultisample(false), + mHasARBCreateContext(false), + mHasARBCreateContextProfile(false), + mHasARBCreateContextRobustness(false), + mHasEXTCreateContextES2Profile(false), + mSwapControl(SwapControl::Absent), + mMinSwapInterval(0), + mMaxSwapInterval(0), + mCurrentSwapInterval(-1), + mXDisplay(nullptr), + mEGLDisplay(nullptr) +{ +} + +DisplayGLX::~DisplayGLX() +{ +} + +egl::Error DisplayGLX::initialize(egl::Display *display) +{ + mEGLDisplay = display; + mXDisplay = display->getNativeDisplayId(); + const auto &attribMap = display->getAttributeMap(); + + // ANGLE_platform_angle allows the creation of a default display + // using EGL_DEFAULT_DISPLAY (= nullptr). In this case just open + // the display specified by the DISPLAY environment variable. + if (mXDisplay == EGL_DEFAULT_DISPLAY) + { + mUsesNewXDisplay = true; + mXDisplay = XOpenDisplay(NULL); + if (!mXDisplay) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not open the default X display."); + } + } + + std::string glxInitError; + if (!mGLX.initialize(mXDisplay, DefaultScreen(mXDisplay), &glxInitError)) + { + return egl::Error(EGL_NOT_INITIALIZED, glxInitError.c_str()); + } + + mHasMultisample = mGLX.minorVersion > 3 || mGLX.hasExtension("GLX_ARB_multisample"); + mHasARBCreateContext = mGLX.hasExtension("GLX_ARB_create_context"); + mHasARBCreateContextProfile = mGLX.hasExtension("GLX_ARB_create_context_profile"); + mHasARBCreateContextRobustness = mGLX.hasExtension("GLX_ARB_create_context_robustness"); + mHasEXTCreateContextES2Profile = mGLX.hasExtension("GLX_EXT_create_context_es2_profile"); + + std::string clientVendor = mGLX.getClientString(GLX_VENDOR); + mIsMesa = clientVendor.find("Mesa") != std::string::npos; + + // Choose the swap_control extension to use, if any. + // The EXT version is better as it allows glXSwapInterval to be called per + // window, while we'll potentially need to change the swap interval on each + // swap buffers when using the SGI or MESA versions. + if (mGLX.hasExtension("GLX_EXT_swap_control")) + { + mSwapControl = SwapControl::EXT; + + // In GLX_EXT_swap_control querying these is done on a GLXWindow so we just + // set default values. + mMinSwapInterval = 0; + mMaxSwapInterval = 4; + } + else if (mGLX.hasExtension("GLX_MESA_swap_control")) + { + // If we have the Mesa or SGI extension, assume that you can at least set + // a swap interval of 0 or 1. + mSwapControl = SwapControl::Mesa; + mMinSwapInterval = 0; + mMinSwapInterval = 1; + } + else if (mGLX.hasExtension("GLX_SGI_swap_control")) + { + mSwapControl = SwapControl::SGI; + mMinSwapInterval = 0; + mMinSwapInterval = 1; + } + else + { + mSwapControl = SwapControl::Absent; + mMinSwapInterval = 1; + mMinSwapInterval = 1; + } + + if (attribMap.contains(EGL_X11_VISUAL_ID_ANGLE)) + { + mRequestedVisual = static_cast<EGLint>(attribMap.get(EGL_X11_VISUAL_ID_ANGLE, -1)); + + // There is no direct way to get the GLXFBConfig matching an X11 visual ID + // so we have to iterate over all the GLXFBConfigs to find the right one. + int nConfigs; + int attribList[] = { + None, + }; + glx::FBConfig *allConfigs = mGLX.chooseFBConfig(attribList, &nConfigs); + + for (int i = 0; i < nConfigs; ++i) + { + if (getGLXFBConfigAttrib(allConfigs[i], GLX_VISUAL_ID) == mRequestedVisual) + { + mContextConfig = allConfigs[i]; + break; + } + } + XFree(allConfigs); + + if (mContextConfig == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, "Invalid visual ID requested."); + } + } + else + { + // When glXMakeCurrent is called, the context and the surface must be + // compatible which in glX-speak means that their config have the same + // color buffer type, are both RGBA or ColorIndex, and their buffers have + // the same depth, if they exist. + // Since our whole EGL implementation is backed by only one GL context, this + // context must be compatible with all the GLXFBConfig corresponding to the + // EGLconfigs that we will be exposing. + int nConfigs; + int attribList[] = + { + // We want RGBA8 and DEPTH24_STENCIL8 + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + // We want RGBA rendering (vs COLOR_INDEX) and doublebuffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + // Double buffer is not strictly required as a non-doublebuffer + // context can work with a doublebuffered surface, but it still + // flickers and all applications want doublebuffer anyway. + GLX_DOUBLEBUFFER, True, + // All of these must be supported for full EGL support + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT, + // This makes sure the config have an associated visual Id + GLX_X_RENDERABLE, True, + GLX_CONFIG_CAVEAT, GLX_NONE, + None + }; + glx::FBConfig *candidates = mGLX.chooseFBConfig(attribList, &nConfigs); + if (nConfigs == 0) + { + XFree(candidates); + return egl::Error(EGL_NOT_INITIALIZED, "Could not find a decent GLX FBConfig to create the context."); + } + mContextConfig = candidates[0]; + XFree(candidates); + } + + const auto &eglAttributes = display->getAttributeMap(); + if (mHasARBCreateContext) + { + egl::Error error = initializeContext(mContextConfig, eglAttributes, &mContext); + if (error.isError()) + { + return error; + } + } + else + { + if (eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) == + EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Cannot create an OpenGL ES platform on GLX without the " + "GLX_ARB_create_context extension."); + } + + XVisualInfo visualTemplate; + visualTemplate.visualid = getGLXFBConfigAttrib(mContextConfig, GLX_VISUAL_ID); + + int numVisuals = 0; + XVisualInfo *visuals = + XGetVisualInfo(mXDisplay, VisualIDMask, &visualTemplate, &numVisuals); + if (numVisuals <= 0) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Could not get the visual info from the fb config"); + } + ASSERT(numVisuals == 1); + + mContext = mGLX.createContext(&visuals[0], nullptr, true); + XFree(visuals); + + if (!mContext) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); + } + } + ASSERT(mContext); + + // FunctionsGL and DisplayGL need to make a few GL calls, for example to + // query the version of the context so we need to make the context current. + // glXMakeCurrent requires a GLXDrawable so we create a temporary Pbuffer + // (of size 1, 1) for the duration of these calls. + // Ideally we would want to unset the current context and destroy the pbuffer + // before going back to the application but this is TODO + // We could use a pbuffer of size (0, 0) but it fails on the Intel Mesa driver + // as commented on https://bugs.freedesktop.org/show_bug.cgi?id=38869 so we + // use (1, 1) instead. + + int dummyPbufferAttribs[] = + { + GLX_PBUFFER_WIDTH, 1, + GLX_PBUFFER_HEIGHT, 1, + None, + }; + mDummyPbuffer = mGLX.createPbuffer(mContextConfig, dummyPbufferAttribs); + if (!mDummyPbuffer) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create the dummy pbuffer."); + } + + if (!mGLX.makeCurrent(mDummyPbuffer, mContext)) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not make the dummy pbuffer current."); + } + + mFunctionsGL = new FunctionsGLGLX(mGLX.getProc); + mFunctionsGL->initialize(); + + // TODO(cwallez, angleproject:1303) Disable the OpenGL ES backend on Linux NVIDIA and Intel as + // it has problems on our automated testing. An OpenGL ES backend might not trigger this test if + // there is no Desktop OpenGL support, but that's not the case in our automated testing. + VendorID vendor = GetVendorID(mFunctionsGL); + bool isOpenGLES = + eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE) == + EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; + if (isOpenGLES && (IsIntel(vendor) || IsNvidia(vendor))) + { + return egl::Error(EGL_NOT_INITIALIZED, "Intel or NVIDIA OpenGL ES drivers are not supported."); + } + + syncXCommands(); + + return DisplayGL::initialize(display); +} + +void DisplayGLX::terminate() +{ + DisplayGL::terminate(); + + if (mDummyPbuffer) + { + mGLX.destroyPbuffer(mDummyPbuffer); + mDummyPbuffer = 0; + } + + if (mContext) + { + mGLX.destroyContext(mContext); + mContext = nullptr; + } + + mGLX.terminate(); + + SafeDelete(mFunctionsGL); +} + +SurfaceImpl *DisplayGLX::createWindowSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLNativeWindowType window, + const egl::AttributeMap &attribs) +{ + ASSERT(configIdToGLXConfig.count(configuration->configID) > 0); + glx::FBConfig fbConfig = configIdToGLXConfig[configuration->configID]; + + return new WindowSurfaceGLX(state, mGLX, this, getRenderer(), window, mGLX.getDisplay(), + mContext, fbConfig); +} + +SurfaceImpl *DisplayGLX::createPbufferSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + const egl::AttributeMap &attribs) +{ + ASSERT(configIdToGLXConfig.count(configuration->configID) > 0); + glx::FBConfig fbConfig = configIdToGLXConfig[configuration->configID]; + + EGLint width = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0)); + EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0)); + bool largest = (attribs.get(EGL_LARGEST_PBUFFER, EGL_FALSE) == EGL_TRUE); + + return new PbufferSurfaceGLX(state, getRenderer(), width, height, largest, mGLX, mContext, + fbConfig); +} + +SurfaceImpl *DisplayGLX::createPbufferFromClientBuffer(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLenum buftype, + EGLClientBuffer clientBuffer, + const egl::AttributeMap &attribs) +{ + UNIMPLEMENTED(); + return nullptr; +} + +SurfaceImpl *DisplayGLX::createPixmapSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + NativePixmapType nativePixmap, + const egl::AttributeMap &attribs) +{ + UNIMPLEMENTED(); + return nullptr; +} + +egl::Error DisplayGLX::getDevice(DeviceImpl **device) +{ + UNIMPLEMENTED(); + return egl::Error(EGL_BAD_DISPLAY); +} + +egl::Error DisplayGLX::initializeContext(glx::FBConfig config, + const egl::AttributeMap &eglAttributes, + glx::Context *context) +{ + int profileMask = 0; + + EGLint requestedDisplayType = static_cast<EGLint>( + eglAttributes.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)); + if (requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE) + { + if (!mHasEXTCreateContextES2Profile) + { + return egl::Error(EGL_NOT_INITIALIZED, + "Cannot create an OpenGL ES platform on GLX without the " + "GLX_EXT_create_context_es_profile extension."); + } + + ASSERT(mHasARBCreateContextProfile); + profileMask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + } + + // Create a context of the requested version, if any. + gl::Version requestedVersion(static_cast<EGLint>(eglAttributes.get( + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE)), + static_cast<EGLint>(eglAttributes.get( + EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE))); + if (static_cast<EGLint>(requestedVersion.major) != EGL_DONT_CARE && + static_cast<EGLint>(requestedVersion.minor) != EGL_DONT_CARE) + { + if (!(profileMask & GLX_CONTEXT_ES2_PROFILE_BIT_EXT) && + requestedVersion >= gl::Version(3, 2)) + { + profileMask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + } + return createContextAttribs(config, requestedVersion, profileMask, context); + } + + // It is commonly assumed that glXCreateContextAttrib will create a context + // of the highest version possible but it is not specified in the spec and + // is not true on the Mesa drivers. On Mesa, Instead we try to create a + // context per GL version until we succeed, starting from newer version. + // On both Mesa and other drivers we try to create a desktop context and fall + // back to ES context. + // The code could be simpler if the Mesa code path was used for all drivers, + // however the cost of failing a context creation can be high (3 milliseconds + // for the NVIDIA driver). The good thing is that failed context creation only + // takes 0.1 milliseconds on Mesa. + + struct ContextCreationInfo + { + EGLint displayType; + int profileFlag; + Optional<gl::Version> version; + }; + + // clang-format off + // For regular drivers we try to create a core, compatibility, then ES context. + // Without requiring any specific version (the Optional version is undefined). + const ContextCreationInfo contextsToTry[] = { + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, {} }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, {} }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, {} }, + }; + + // On Mesa we try to create a core context, except for versions below 3.2 + // where it is not applicable. (and fallback to ES as well) + const ContextCreationInfo mesaContextsToTry[] = { + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 5) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 4) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 3) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 2) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 1) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(4, 0) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(3, 3) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, { gl::Version(3, 2) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(3, 1) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(3, 0) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(2, 0) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 5) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 4) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 3) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 2) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 1) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, 0, { gl::Version(1, 0) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 2) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 1) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(3, 0) } }, + { EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, { gl::Version(2, 0) } }, + }; + // clang-format on + + const ContextCreationInfo *toTry = contextsToTry; + size_t toTryLength = ArraySize(contextsToTry); + if (mIsMesa) + { + toTry = mesaContextsToTry; + toTryLength = ArraySize(mesaContextsToTry); + } + + // NOTE: below we return as soon as we're able to create a context so the + // "error" variable is EGL_SUCCESS when returned contrary to the common idiom + // of returning "error" when there is an actual error. + for (size_t i = 0; i < toTryLength; ++i) + { + const ContextCreationInfo &info = toTry[i]; + if (requestedDisplayType != EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE && + requestedDisplayType != info.displayType) + { + continue; + } + + egl::Error error = createContextAttribs(config, info.version, info.profileFlag, context); + if (!error.isError()) + { + return error; + } + } + + return egl::Error(EGL_NOT_INITIALIZED, "Could not create a backing OpenGL context."); +} + +egl::ConfigSet DisplayGLX::generateConfigs() +{ + egl::ConfigSet configs; + configIdToGLXConfig.clear(); + + const gl::Version &maxVersion = getMaxSupportedESVersion(); + ASSERT(maxVersion >= gl::Version(2, 0)); + bool supportsES3 = maxVersion >= gl::Version(3, 0); + + int contextRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_RED_SIZE); + int contextGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_GREEN_SIZE); + int contextBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_BLUE_SIZE); + int contextAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ALPHA_SIZE); + + int contextDepthSize = getGLXFBConfigAttrib(mContextConfig, GLX_DEPTH_SIZE); + int contextStencilSize = getGLXFBConfigAttrib(mContextConfig, GLX_STENCIL_SIZE); + + int contextSamples = mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLES) : 0; + int contextSampleBuffers = + mHasMultisample ? getGLXFBConfigAttrib(mContextConfig, GLX_SAMPLE_BUFFERS) : 0; + + int contextAccumRedSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_RED_SIZE); + int contextAccumGreenSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_GREEN_SIZE); + int contextAccumBlueSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_BLUE_SIZE); + int contextAccumAlphaSize = getGLXFBConfigAttrib(mContextConfig, GLX_ACCUM_ALPHA_SIZE); + + int attribList[] = + { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_RENDERABLE, True, + GLX_DOUBLEBUFFER, True, + None, + }; + + int glxConfigCount; + glx::FBConfig *glxConfigs = mGLX.chooseFBConfig(attribList, &glxConfigCount); + + for (int i = 0; i < glxConfigCount; i++) + { + glx::FBConfig glxConfig = glxConfigs[i]; + egl::Config config; + + // Native stuff + config.nativeVisualID = getGLXFBConfigAttrib(glxConfig, GLX_VISUAL_ID); + config.nativeVisualType = getGLXFBConfigAttrib(glxConfig, GLX_X_VISUAL_TYPE); + config.nativeRenderable = EGL_TRUE; + + // When a visual ID has been specified with EGL_ANGLE_x11_visual we should + // only return configs with this visual: it will maximize performance by avoid + // blits in the driver when showing the window on the screen. + if (mRequestedVisual != -1 && config.nativeVisualID != mRequestedVisual) + { + continue; + } + + // Buffer sizes + config.redSize = getGLXFBConfigAttrib(glxConfig, GLX_RED_SIZE); + config.greenSize = getGLXFBConfigAttrib(glxConfig, GLX_GREEN_SIZE); + config.blueSize = getGLXFBConfigAttrib(glxConfig, GLX_BLUE_SIZE); + config.alphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ALPHA_SIZE); + config.depthSize = getGLXFBConfigAttrib(glxConfig, GLX_DEPTH_SIZE); + config.stencilSize = getGLXFBConfigAttrib(glxConfig, GLX_STENCIL_SIZE); + + // We require RGBA8 and the D24S8 (or no DS buffer) + if (config.redSize != contextRedSize || config.greenSize != contextGreenSize || + config.blueSize != contextBlueSize || config.alphaSize != contextAlphaSize) + { + continue; + } + // The GLX spec says that it is ok for a whole buffer to not be present + // however the Mesa Intel driver (and probably on other Mesa drivers) + // fails to make current when the Depth stencil doesn't exactly match the + // configuration. + bool hasSameDepthStencil = + config.depthSize == contextDepthSize && config.stencilSize == contextStencilSize; + bool hasNoDepthStencil = config.depthSize == 0 && config.stencilSize == 0; + if (!hasSameDepthStencil && (mIsMesa || !hasNoDepthStencil)) + { + continue; + } + + config.colorBufferType = EGL_RGB_BUFFER; + config.luminanceSize = 0; + config.alphaMaskSize = 0; + + config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; + + // Multisample and accumulation buffers + int samples = mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLES) : 0; + int sampleBuffers = + mHasMultisample ? getGLXFBConfigAttrib(glxConfig, GLX_SAMPLE_BUFFERS) : 0; + + int accumRedSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_RED_SIZE); + int accumGreenSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_GREEN_SIZE); + int accumBlueSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_BLUE_SIZE); + int accumAlphaSize = getGLXFBConfigAttrib(glxConfig, GLX_ACCUM_ALPHA_SIZE); + + if (samples != contextSamples || + sampleBuffers != contextSampleBuffers || + accumRedSize != contextAccumRedSize || + accumGreenSize != contextAccumGreenSize || + accumBlueSize != contextAccumBlueSize || + accumAlphaSize != contextAccumAlphaSize) + { + continue; + } + + config.samples = samples; + config.sampleBuffers = sampleBuffers; + + // Transparency + if (getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_TYPE) == GLX_TRANSPARENT_RGB) + { + config.transparentType = EGL_TRANSPARENT_RGB; + config.transparentRedValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_RED_VALUE); + config.transparentGreenValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_GREEN_VALUE); + config.transparentBlueValue = getGLXFBConfigAttrib(glxConfig, GLX_TRANSPARENT_BLUE_VALUE); + } + else + { + config.transparentType = EGL_NONE; + } + + // Pbuffer + config.maxPBufferWidth = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_WIDTH); + config.maxPBufferHeight = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_HEIGHT); + config.maxPBufferPixels = getGLXFBConfigAttrib(glxConfig, GLX_MAX_PBUFFER_PIXELS); + + // Caveat + config.configCaveat = EGL_NONE; + + int caveat = getGLXFBConfigAttrib(glxConfig, GLX_CONFIG_CAVEAT); + if (caveat == GLX_SLOW_CONFIG) + { + config.configCaveat = EGL_SLOW_CONFIG; + } + else if (caveat == GLX_NON_CONFORMANT_CONFIG) + { + continue; + } + + // Misc + config.level = getGLXFBConfigAttrib(glxConfig, GLX_LEVEL); + + config.bindToTextureRGB = EGL_FALSE; + config.bindToTextureRGBA = EGL_FALSE; + + int glxDrawable = getGLXFBConfigAttrib(glxConfig, GLX_DRAWABLE_TYPE); + config.surfaceType = 0 | + (glxDrawable & GLX_WINDOW_BIT ? EGL_WINDOW_BIT : 0) | + (glxDrawable & GLX_PBUFFER_BIT ? EGL_PBUFFER_BIT : 0) | + (glxDrawable & GLX_PIXMAP_BIT ? EGL_PIXMAP_BIT : 0); + + config.minSwapInterval = mMinSwapInterval; + config.maxSwapInterval = mMaxSwapInterval; + + // TODO(cwallez) wildly guessing these formats, another TODO says they should be removed anyway + config.renderTargetFormat = GL_RGBA8; + config.depthStencilFormat = GL_DEPTH24_STENCIL8; + + config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); + config.renderableType = config.conformant; + + // TODO(cwallez) I have no idea what this is + config.matchNativePixmap = EGL_NONE; + + int id = configs.add(config); + configIdToGLXConfig[id] = glxConfig; + } + + XFree(glxConfigs); + + return configs; +} + +bool DisplayGLX::testDeviceLost() +{ + if (mHasARBCreateContextRobustness) + { + return getRenderer()->getResetStatus() != GL_NO_ERROR; + } + + return false; +} + +egl::Error DisplayGLX::restoreLostDevice() +{ + return egl::Error(EGL_BAD_DISPLAY); +} + +bool DisplayGLX::isValidNativeWindow(EGLNativeWindowType window) const +{ + // There is no function in Xlib to check the validity of a Window directly. + // However a small number of functions used to obtain window information + // return a status code (0 meaning failure) and guarantee that they will + // fail if the window doesn't exist (the rational is that these function + // are used by window managers). Out of these function we use XQueryTree + // as it seems to be the simplest; a drawback is that it will allocate + // memory for the list of children, because we use a child window for + // WindowSurface. + Window root; + Window parent; + Window *children = nullptr; + unsigned nChildren; + int status = XQueryTree(mGLX.getDisplay(), window, &root, &parent, &children, &nChildren); + if (children) + { + XFree(children); + } + return status != 0; +} + +std::string DisplayGLX::getVendorString() const +{ + // UNIMPLEMENTED(); + return ""; +} + +egl::Error DisplayGLX::waitClient() const +{ + mGLX.waitGL(); + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayGLX::getDriverVersion(std::string *version) const +{ + VendorID vendor = GetVendorID(mFunctionsGL); + + switch (vendor) + { + case VENDOR_ID_NVIDIA: + return getNVIDIADriverVersion(version); + case VENDOR_ID_AMD: + return GetAMDDriverVersion(version); + default: + *version = ""; + return egl::Error(EGL_SUCCESS); + } +} + +egl::Error DisplayGLX::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // eglWaitNative is used to notice the driver of changes in X11 for the current surface, such as + // changes of the window size. We use this event to update the child window of WindowSurfaceGLX + // to match its parent window's size. + // Handling eglWaitNative this way helps the application control when resize happens. This is + // important because drivers have a tendency to clobber the back buffer when the windows are + // resized. See http://crbug.com/326995 + if (drawSurface != nullptr) + { + SurfaceGLX *glxDrawSurface = GetImplAs<SurfaceGLX>(drawSurface); + egl::Error error = glxDrawSurface->checkForResize(); + if (error.isError()) + { + return error; + } + } + + if (readSurface != drawSurface && readSurface != nullptr) + { + SurfaceGLX *glxReadSurface = GetImplAs<SurfaceGLX>(readSurface); + egl::Error error = glxReadSurface->checkForResize(); + if (error.isError()) + { + return error; + } + } + + // We still need to forward the resizing of the child window to the driver. + mGLX.waitX(); + return egl::Error(EGL_SUCCESS); +} + +void DisplayGLX::syncXCommands() const +{ + if (mUsesNewXDisplay) + { + XSync(mGLX.getDisplay(), False); + } +} + +void DisplayGLX::setSwapInterval(glx::Drawable drawable, SwapControlData *data) +{ + ASSERT(data != nullptr); + + // TODO(cwallez) error checking? + if (mSwapControl == SwapControl::EXT) + { + // Prefer the EXT extension, it gives per-drawable swap intervals, which will + // minimize the number of driver calls. + if (data->maxSwapInterval < 0) + { + unsigned int maxSwapInterval = 0; + mGLX.queryDrawable(drawable, GLX_MAX_SWAP_INTERVAL_EXT, &maxSwapInterval); + data->maxSwapInterval = static_cast<int>(maxSwapInterval); + } + + // When the egl configs were generated we had to guess what the max swap interval + // was because we didn't have a window to query it one (and that this max could + // depend on the monitor). This means that the target interval might be higher + // than the max interval and needs to be clamped. + const int realInterval = std::min(data->targetSwapInterval, data->maxSwapInterval); + if (data->currentSwapInterval != realInterval) + { + mGLX.swapIntervalEXT(drawable, realInterval); + data->currentSwapInterval = realInterval; + } + } + else if (mCurrentSwapInterval != data->targetSwapInterval) + { + // With the Mesa or SGI extensions we can still do per-drawable swap control + // manually but it is more expensive in number of driver calls. + if (mSwapControl == SwapControl::Mesa) + { + mGLX.swapIntervalMESA(data->targetSwapInterval); + } + else if (mSwapControl == SwapControl::SGI) + { + mGLX.swapIntervalSGI(data->targetSwapInterval); + } + mCurrentSwapInterval = data->targetSwapInterval; + } +} + +bool DisplayGLX::isValidWindowVisualId(unsigned long visualId) const +{ + return mRequestedVisual == -1 || static_cast<unsigned long>(mRequestedVisual) == visualId; +} + +const FunctionsGL *DisplayGLX::getFunctionsGL() const +{ + return mFunctionsGL; +} + +void DisplayGLX::generateExtensions(egl::DisplayExtensions *outExtensions) const +{ + outExtensions->createContextRobustness = mHasARBCreateContextRobustness; +} + +void DisplayGLX::generateCaps(egl::Caps *outCaps) const +{ + outCaps->textureNPOT = true; +} + +int DisplayGLX::getGLXFBConfigAttrib(glx::FBConfig config, int attrib) const +{ + int result; + mGLX.getFBConfigAttrib(config, attrib, &result); + return result; +} + +egl::Error DisplayGLX::createContextAttribs(glx::FBConfig, + const Optional<gl::Version> &version, + int profileMask, + glx::Context *context) const +{ + std::vector<int> attribs; + + if (mHasARBCreateContextRobustness) + { + attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB); + attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB); + } + + if (version.valid()) + { + attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); + attribs.push_back(version.value().major); + + attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); + attribs.push_back(version.value().minor); + } + + if (profileMask != 0 && mHasARBCreateContextProfile) + { + attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); + attribs.push_back(profileMask); + } + + attribs.push_back(None); + + // When creating a context with glXCreateContextAttribsARB, a variety of X11 errors can + // be generated. To prevent these errors from crashing our process, we simply ignore + // them and only look if GLXContext was created. + // Process all events before setting the error handler to avoid desynchronizing XCB instances + // (the error handler is NOT per-display). + XSync(mXDisplay, False); + auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors); + *context = mGLX.createContextAttribsARB(mContextConfig, nullptr, True, attribs.data()); + XSetErrorHandler(oldErrorHandler); + + if (!*context) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create GL context."); + } + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayGLX::getNVIDIADriverVersion(std::string *version) const +{ + *version = ""; + + int eventBase = 0; + int errorBase = 0; + if (XNVCTRLQueryExtension(mXDisplay, &eventBase, &errorBase)) + { + int screenCount = ScreenCount(mXDisplay); + for (int screen = 0; screen < screenCount; ++screen) + { + char *buffer = nullptr; + if (XNVCTRLIsNvScreen(mXDisplay, screen) && + XNVCTRLQueryStringAttribute(mXDisplay, screen, 0, + NV_CTRL_STRING_NVIDIA_DRIVER_VERSION, &buffer)) + { + *version = buffer; + XFree(buffer); + } + } + } + + return egl::Error(EGL_SUCCESS); +} +} |