|  |  | 
|  | /* | 
|  | * Copyright 2016 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/gpu/gl/GrGLInterface.h" | 
|  | #include "tools/sk_app/GLWindowContext.h" | 
|  | #include "tools/sk_app/unix/WindowContextFactory_unix.h" | 
|  |  | 
|  | #include <GL/gl.h> | 
|  |  | 
|  | using sk_app::window_context_factory::XlibWindowInfo; | 
|  | using sk_app::DisplayParams; | 
|  | using sk_app::GLWindowContext; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | static bool gCtxErrorOccurred = false; | 
|  | static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { | 
|  | gCtxErrorOccurred = true; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | class GLWindowContext_xlib : public GLWindowContext { | 
|  | public: | 
|  | GLWindowContext_xlib(const XlibWindowInfo&, const DisplayParams&); | 
|  | ~GLWindowContext_xlib() override; | 
|  |  | 
|  | void onSwapBuffers() override; | 
|  |  | 
|  | void onDestroyContext() override; | 
|  |  | 
|  | protected: | 
|  | sk_sp<const GrGLInterface> onInitializeContext() override; | 
|  |  | 
|  | private: | 
|  | GLWindowContext_xlib(void*, const DisplayParams&); | 
|  |  | 
|  | Display*     fDisplay; | 
|  | XWindow      fWindow; | 
|  | GLXFBConfig* fFBConfig; | 
|  | XVisualInfo* fVisualInfo; | 
|  | GLXContext   fGLContext; | 
|  |  | 
|  | using INHERITED = GLWindowContext; | 
|  | }; | 
|  |  | 
|  | GLWindowContext_xlib::GLWindowContext_xlib(const XlibWindowInfo& winInfo, const DisplayParams& params) | 
|  | : INHERITED(params) | 
|  | , fDisplay(winInfo.fDisplay) | 
|  | , fWindow(winInfo.fWindow) | 
|  | , fFBConfig(winInfo.fFBConfig) | 
|  | , fVisualInfo(winInfo.fVisualInfo) | 
|  | , fGLContext() { | 
|  | fWidth = winInfo.fWidth; | 
|  | fHeight = winInfo.fHeight; | 
|  | this->initializeContext(); | 
|  | } | 
|  |  | 
|  | using CreateContextAttribsFn = GLXContext(Display*, GLXFBConfig, GLXContext, Bool, const int*); | 
|  |  | 
|  | sk_sp<const GrGLInterface> GLWindowContext_xlib::onInitializeContext() { | 
|  | SkASSERT(fDisplay); | 
|  | SkASSERT(!fGLContext); | 
|  | sk_sp<const GrGLInterface> interface; | 
|  | bool current = false; | 
|  |  | 
|  | // We attempt to use glXCreateContextAttribsARB as RenderDoc requires that the context be | 
|  | // created with this rather than glXCreateContext. | 
|  | CreateContextAttribsFn* createContextAttribs = (CreateContextAttribsFn*)glXGetProcAddressARB( | 
|  | (const GLubyte*)"glXCreateContextAttribsARB"); | 
|  | if (createContextAttribs && fFBConfig) { | 
|  | // Install Xlib error handler that will set gCtxErrorOccurred | 
|  | int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler); | 
|  |  | 
|  | // Specifying 3.2 allows an arbitrarily high context version (so long as no 3.2 features | 
|  | // have been removed). | 
|  | for (int minor = 2; minor >= 0 && !fGLContext; --minor) { | 
|  | // Ganesh prefers a core profile which incidentally allows RenderDoc to work correctly. | 
|  | for (int profile : {GLX_CONTEXT_CORE_PROFILE_BIT_ARB, | 
|  | GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB}) { | 
|  | gCtxErrorOccurred = false; | 
|  | int attribs[] = { | 
|  | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, minor, | 
|  | GLX_CONTEXT_PROFILE_MASK_ARB, profile, | 
|  | 0 | 
|  | }; | 
|  | fGLContext = createContextAttribs(fDisplay, *fFBConfig, nullptr, True, attribs); | 
|  |  | 
|  | // Sync to ensure any errors generated are processed. | 
|  | XSync(fDisplay, False); | 
|  | if (gCtxErrorOccurred) { continue; } | 
|  |  | 
|  | if (fGLContext && profile == GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB && | 
|  | glXMakeCurrent(fDisplay, fWindow, fGLContext)) { | 
|  | current = true; | 
|  | // Look to see if RenderDoc is attached. If so, re-create the context with a | 
|  | // core profile. | 
|  | interface = GrGLMakeNativeInterface(); | 
|  | if (interface && interface->fExtensions.has("GL_EXT_debug_tool")) { | 
|  | interface.reset(); | 
|  | glXMakeCurrent(fDisplay, None, nullptr); | 
|  | glXDestroyContext(fDisplay, fGLContext); | 
|  | current = false; | 
|  | fGLContext = nullptr; | 
|  | } | 
|  | } | 
|  | if (fGLContext) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | // Restore the original error handler | 
|  | XSetErrorHandler(oldHandler); | 
|  | } | 
|  | if (!fGLContext) { | 
|  | fGLContext = glXCreateContext(fDisplay, fVisualInfo, nullptr, GL_TRUE); | 
|  | } | 
|  | if (!fGLContext) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (!current && !glXMakeCurrent(fDisplay, fWindow, fGLContext)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const char* glxExtensions = glXQueryExtensionsString(fDisplay, DefaultScreen(fDisplay)); | 
|  | if (glxExtensions) { | 
|  | if (strstr(glxExtensions, "GLX_EXT_swap_control")) { | 
|  | PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = | 
|  | (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB( | 
|  | (const GLubyte*)"glXSwapIntervalEXT"); | 
|  | glXSwapIntervalEXT(fDisplay, fWindow, fDisplayParams.fDisableVsync ? 0 : 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | glClearStencil(0); | 
|  | glClearColor(0, 0, 0, 0); | 
|  | glStencilMask(0xffffffff); | 
|  | glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); | 
|  |  | 
|  | glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits); | 
|  | glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount); | 
|  | fSampleCount = std::max(fSampleCount, 1); | 
|  |  | 
|  | XWindow root; | 
|  | int x, y; | 
|  | unsigned int border_width, depth; | 
|  | XGetGeometry(fDisplay, fWindow, &root, &x, &y, (unsigned int*)&fWidth, (unsigned int*)&fHeight, | 
|  | &border_width, &depth); | 
|  | glViewport(0, 0, fWidth, fHeight); | 
|  |  | 
|  | return interface ? interface : GrGLMakeNativeInterface(); | 
|  | } | 
|  |  | 
|  | GLWindowContext_xlib::~GLWindowContext_xlib() { | 
|  | this->destroyContext(); | 
|  | } | 
|  |  | 
|  | void GLWindowContext_xlib::onDestroyContext() { | 
|  | if (!fDisplay || !fGLContext) { | 
|  | return; | 
|  | } | 
|  | glXMakeCurrent(fDisplay, None, nullptr); | 
|  | glXDestroyContext(fDisplay, fGLContext); | 
|  | fGLContext = nullptr; | 
|  | } | 
|  |  | 
|  | void GLWindowContext_xlib::onSwapBuffers() { | 
|  | if (fDisplay && fGLContext) { | 
|  | glXSwapBuffers(fDisplay, fWindow); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | namespace sk_app { | 
|  |  | 
|  | namespace window_context_factory { | 
|  |  | 
|  | std::unique_ptr<WindowContext> MakeGLForXlib(const XlibWindowInfo& winInfo, | 
|  | const DisplayParams& params) { | 
|  | std::unique_ptr<WindowContext> ctx(new GLWindowContext_xlib(winInfo, params)); | 
|  | if (!ctx->isValid()) { | 
|  | return nullptr; | 
|  | } | 
|  | return ctx; | 
|  | } | 
|  |  | 
|  | }  // namespace window_context_factory | 
|  |  | 
|  | }  // namespace sk_app |