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
15 using sk_app::window_context_factory::XlibWindowInfo;
16 using sk_app::DisplayParams;
17 using sk_app::GLWindowContext;
18
19 namespace {
20
21 static bool gCtxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)22 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
23 gCtxErrorOccurred = true;
24 return 0;
25 }
26
27 class GLWindowContext_xlib : public GLWindowContext {
28 public:
29 GLWindowContext_xlib(const XlibWindowInfo&, const DisplayParams&);
30 ~GLWindowContext_xlib() override;
31
32 void onSwapBuffers() override;
33
34 void onDestroyContext() override;
35
36 protected:
37 sk_sp<const GrGLInterface> onInitializeContext() override;
38
39 private:
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
GLWindowContext_xlib(const XlibWindowInfo & winInfo,const DisplayParams & params)51 GLWindowContext_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
63 using CreateContextAttribsFn = GLXContext(Display*, GLXFBConfig, GLXContext, Bool, const int*);
64
onInitializeContext()65 sk_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
~GLWindowContext_xlib()159 GLWindowContext_xlib::~GLWindowContext_xlib() {
160 this->destroyContext();
161 }
162
onDestroyContext()163 void GLWindowContext_xlib::onDestroyContext() {
164 if (!fDisplay || !fGLContext) {
165 return;
166 }
167 glXMakeCurrent(fDisplay, None, nullptr);
168 glXDestroyContext(fDisplay, fGLContext);
169 fGLContext = nullptr;
170 }
171
onSwapBuffers()172 void GLWindowContext_xlib::onSwapBuffers() {
173 if (fDisplay && fGLContext) {
174 glXSwapBuffers(fDisplay, fWindow);
175 }
176 }
177
178 } // anonymous namespace
179
180 namespace sk_app {
181
182 namespace window_context_factory {
183
MakeGLForXlib(const XlibWindowInfo & winInfo,const DisplayParams & params)184 std::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