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