| |
| /* |
| * 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 |