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 typedef GLWindowContext INHERITED;
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 compatibility profile for possible NVPR support. However, RenderDoc
83 // requires a core profile. Edit this code to use RenderDoc.
84 for (int profile : {GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
85 GLX_CONTEXT_CORE_PROFILE_BIT_ARB}) {
86 gCtxErrorOccurred = false;
87 int attribs[] = {
88 GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, minor,
89 GLX_CONTEXT_PROFILE_MASK_ARB, profile,
90 0
91 };
92 fGLContext = createContextAttribs(fDisplay, *fFBConfig, nullptr, True, attribs);
93
94 // Sync to ensure any errors generated are processed.
95 XSync(fDisplay, False);
96 if (gCtxErrorOccurred) { continue; }
97
98 if (fGLContext && profile == GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB &&
99 glXMakeCurrent(fDisplay, fWindow, fGLContext)) {
100 current = true;
101 // Look to see if RenderDoc is attached. If so, re-create the context with a
102 // core profile.
103 interface = GrGLMakeNativeInterface();
104 if (interface && interface->fExtensions.has("GL_EXT_debug_tool")) {
105 interface.reset();
106 glXMakeCurrent(fDisplay, None, nullptr);
107 glXDestroyContext(fDisplay, fGLContext);
108 current = false;
109 fGLContext = nullptr;
110 }
111 }
112 if (fGLContext) {
113 break;
114 }
115 }
116 }
117 // Restore the original error handler
118 XSetErrorHandler(oldHandler);
119 }
120 if (!fGLContext) {
121 fGLContext = glXCreateContext(fDisplay, fVisualInfo, nullptr, GL_TRUE);
122 }
123 if (!fGLContext) {
124 return nullptr;
125 }
126
127 if (!current && !glXMakeCurrent(fDisplay, fWindow, fGLContext)) {
128 return nullptr;
129 }
130
131 const char* glxExtensions = glXQueryExtensionsString(fDisplay, DefaultScreen(fDisplay));
132 if (glxExtensions) {
133 if (strstr(glxExtensions, "GLX_EXT_swap_control")) {
134 PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
135 (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB(
136 (const GLubyte*)"glXSwapIntervalEXT");
137 glXSwapIntervalEXT(fDisplay, fWindow, fDisplayParams.fDisableVsync ? 0 : 1);
138 }
139 }
140
141 glClearStencil(0);
142 glClearColor(0, 0, 0, 0);
143 glStencilMask(0xffffffff);
144 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
145
146 glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits);
147 glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount);
148 fSampleCount = SkTMax(fSampleCount, 1);
149
150 XWindow root;
151 int x, y;
152 unsigned int border_width, depth;
153 XGetGeometry(fDisplay, fWindow, &root, &x, &y, (unsigned int*)&fWidth, (unsigned int*)&fHeight,
154 &border_width, &depth);
155 glViewport(0, 0, fWidth, fHeight);
156
157 return interface ? interface : GrGLMakeNativeInterface();
158 }
159
~GLWindowContext_xlib()160 GLWindowContext_xlib::~GLWindowContext_xlib() {
161 this->destroyContext();
162 }
163
onDestroyContext()164 void GLWindowContext_xlib::onDestroyContext() {
165 if (!fDisplay || !fGLContext) {
166 return;
167 }
168 glXMakeCurrent(fDisplay, None, nullptr);
169 glXDestroyContext(fDisplay, fGLContext);
170 fGLContext = nullptr;
171 }
172
onSwapBuffers()173 void GLWindowContext_xlib::onSwapBuffers() {
174 if (fDisplay && fGLContext) {
175 glXSwapBuffers(fDisplay, fWindow);
176 }
177 }
178
179 } // anonymous namespace
180
181 namespace sk_app {
182
183 namespace window_context_factory {
184
MakeGLForXlib(const XlibWindowInfo & winInfo,const DisplayParams & params)185 std::unique_ptr<WindowContext> MakeGLForXlib(const XlibWindowInfo& winInfo,
186 const DisplayParams& params) {
187 std::unique_ptr<WindowContext> ctx(new GLWindowContext_xlib(winInfo, params));
188 if (!ctx->isValid()) {
189 return nullptr;
190 }
191 return ctx;
192 }
193
194 } // namespace window_context_factory
195
196 } // namespace sk_app
197