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