1 2/* 3 * Copyright 2016 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "include/gpu/gl/GrGLInterface.h" 10#include "tools/sk_app/GLWindowContext.h" 11#include "tools/sk_app/unix/WindowContextFactory_unix.h" 12 13#include <GL/gl.h> 14 15using sk_app::window_context_factory::XlibWindowInfo; 16using sk_app::DisplayParams; 17using sk_app::GLWindowContext; 18 19namespace { 20 21static bool gCtxErrorOccurred = false; 22static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { 23 gCtxErrorOccurred = true; 24 return 0; 25} 26 27class GLWindowContext_xlib : public GLWindowContext { 28public: 29 GLWindowContext_xlib(const XlibWindowInfo&, const DisplayParams&); 30 ~GLWindowContext_xlib() override; 31 32 void onSwapBuffers() override; 33 34 void onDestroyContext() override; 35 36protected: 37 sk_sp<const GrGLInterface> onInitializeContext() override; 38 39private: 40 GLWindowContext_xlib(void*, const DisplayParams&); 41 42 Display* fDisplay; 43 XWindow fWindow; 44 GLXFBConfig* fFBConfig; 45 XVisualInfo* fVisualInfo; 46 GLXContext fGLContext; 47 48 using INHERITED = GLWindowContext; 49}; 50 51GLWindowContext_xlib::GLWindowContext_xlib(const XlibWindowInfo& winInfo, const DisplayParams& params) 52 : INHERITED(params) 53 , fDisplay(winInfo.fDisplay) 54 , fWindow(winInfo.fWindow) 55 , fFBConfig(winInfo.fFBConfig) 56 , fVisualInfo(winInfo.fVisualInfo) 57 , fGLContext() { 58 fWidth = winInfo.fWidth; 59 fHeight = winInfo.fHeight; 60 this->initializeContext(); 61} 62 63using CreateContextAttribsFn = GLXContext(Display*, GLXFBConfig, GLXContext, Bool, const int*); 64 65sk_sp<const GrGLInterface> GLWindowContext_xlib::onInitializeContext() { 66 SkASSERT(fDisplay); 67 SkASSERT(!fGLContext); 68 sk_sp<const GrGLInterface> interface; 69 bool current = false; 70 71 // We attempt to use glXCreateContextAttribsARB as RenderDoc requires that the context be 72 // created with this rather than glXCreateContext. 73 CreateContextAttribsFn* createContextAttribs = (CreateContextAttribsFn*)glXGetProcAddressARB( 74 (const GLubyte*)"glXCreateContextAttribsARB"); 75 if (createContextAttribs && fFBConfig) { 76 // Install Xlib error handler that will set gCtxErrorOccurred 77 int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler); 78 79 // Specifying 3.2 allows an arbitrarily high context version (so long as no 3.2 features 80 // have been removed). 81 for (int minor = 2; minor >= 0 && !fGLContext; --minor) { 82 // Ganesh prefers a core profile which incidentally allows RenderDoc to work correctly. 83 for (int profile : {GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 84 GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB}) { 85 gCtxErrorOccurred = false; 86 int attribs[] = { 87 GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, minor, 88 GLX_CONTEXT_PROFILE_MASK_ARB, profile, 89 0 90 }; 91 fGLContext = createContextAttribs(fDisplay, *fFBConfig, nullptr, True, attribs); 92 93 // Sync to ensure any errors generated are processed. 94 XSync(fDisplay, False); 95 if (gCtxErrorOccurred) { continue; } 96 97 if (fGLContext && profile == GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB && 98 glXMakeCurrent(fDisplay, fWindow, fGLContext)) { 99 current = true; 100 // Look to see if RenderDoc is attached. If so, re-create the context with a 101 // core profile. 102 interface = GrGLMakeNativeInterface(); 103 if (interface && interface->fExtensions.has("GL_EXT_debug_tool")) { 104 interface.reset(); 105 glXMakeCurrent(fDisplay, None, nullptr); 106 glXDestroyContext(fDisplay, fGLContext); 107 current = false; 108 fGLContext = nullptr; 109 } 110 } 111 if (fGLContext) { 112 break; 113 } 114 } 115 } 116 // Restore the original error handler 117 XSetErrorHandler(oldHandler); 118 } 119 if (!fGLContext) { 120 fGLContext = glXCreateContext(fDisplay, fVisualInfo, nullptr, GL_TRUE); 121 } 122 if (!fGLContext) { 123 return nullptr; 124 } 125 126 if (!current && !glXMakeCurrent(fDisplay, fWindow, fGLContext)) { 127 return nullptr; 128 } 129 130 const char* glxExtensions = glXQueryExtensionsString(fDisplay, DefaultScreen(fDisplay)); 131 if (glxExtensions) { 132 if (strstr(glxExtensions, "GLX_EXT_swap_control")) { 133 PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = 134 (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB( 135 (const GLubyte*)"glXSwapIntervalEXT"); 136 glXSwapIntervalEXT(fDisplay, fWindow, fDisplayParams.fDisableVsync ? 0 : 1); 137 } 138 } 139 140 glClearStencil(0); 141 glClearColor(0, 0, 0, 0); 142 glStencilMask(0xffffffff); 143 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 144 145 glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits); 146 glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount); 147 fSampleCount = std::max(fSampleCount, 1); 148 149 XWindow root; 150 int x, y; 151 unsigned int border_width, depth; 152 XGetGeometry(fDisplay, fWindow, &root, &x, &y, (unsigned int*)&fWidth, (unsigned int*)&fHeight, 153 &border_width, &depth); 154 glViewport(0, 0, fWidth, fHeight); 155 156 return interface ? interface : GrGLMakeNativeInterface(); 157} 158 159GLWindowContext_xlib::~GLWindowContext_xlib() { 160 this->destroyContext(); 161} 162 163void GLWindowContext_xlib::onDestroyContext() { 164 if (!fDisplay || !fGLContext) { 165 return; 166 } 167 glXMakeCurrent(fDisplay, None, nullptr); 168 glXDestroyContext(fDisplay, fGLContext); 169 fGLContext = nullptr; 170} 171 172void GLWindowContext_xlib::onSwapBuffers() { 173 if (fDisplay && fGLContext) { 174 glXSwapBuffers(fDisplay, fWindow); 175 } 176} 177 178} // anonymous namespace 179 180namespace sk_app { 181 182namespace window_context_factory { 183 184std::unique_ptr<WindowContext> MakeGLForXlib(const XlibWindowInfo& winInfo, 185 const DisplayParams& params) { 186 std::unique_ptr<WindowContext> ctx(new GLWindowContext_xlib(winInfo, params)); 187 if (!ctx->isValid()) { 188 return nullptr; 189 } 190 return ctx; 191} 192 193} // namespace window_context_factory 194 195} // namespace sk_app 196