• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 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 "gl/SkGLContext.h"
9 
10 #include <X11/Xlib.h>
11 #include <GL/glx.h>
12 #include <GL/glu.h>
13 
14 namespace {
15 
16 /* Note: Skia requires glx 1.3 or newer */
17 
18 /* This struct is taken from a mesa demo.  Please update as required */
19 static const struct { int major, minor; } gl_versions[] = {
20    {1, 0},
21    {1, 1},
22    {1, 2},
23    {1, 3},
24    {1, 4},
25    {1, 5},
26    {2, 0},
27    {2, 1},
28    {3, 0},
29    {3, 1},
30    {3, 2},
31    {3, 3},
32    {4, 0},
33    {4, 1},
34    {4, 2},
35    {4, 3},
36    {4, 4},
37    {0, 0} /* end of list */
38 };
39 #define NUM_GL_VERSIONS SK_ARRAY_COUNT(gl_versions)
40 
41 static bool ctxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * ev)42 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
43     ctxErrorOccurred = true;
44     return 0;
45 }
46 
47 class GLXGLContext : public SkGLContext {
48 public:
49     GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareList);
50     ~GLXGLContext() override;
51 
52 private:
53     void destroyGLContext();
54 
55     void onPlatformMakeCurrent() const override;
56     void onPlatformSwapBuffers() const override;
57     GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
58 
59     GLXContext fContext;
60     Display* fDisplay;
61     Pixmap fPixmap;
62     GLXPixmap fGlxPixmap;
63 };
64 
GLXGLContext(GrGLStandard forcedGpuAPI,GLXGLContext * shareContext)65 GLXGLContext::GLXGLContext(GrGLStandard forcedGpuAPI, GLXGLContext* shareContext)
66     : fContext(nullptr)
67     , fDisplay(nullptr)
68     , fPixmap(0)
69     , fGlxPixmap(0) {
70     fDisplay = XOpenDisplay(0);
71 
72     GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr;
73 
74     if (!fDisplay) {
75         SkDebugf("Failed to open X display.\n");
76         this->destroyGLContext();
77         return;
78     }
79 
80     // Get a matching FB config
81     static int visual_attribs[] = {
82         GLX_X_RENDERABLE    , True,
83         GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
84         None
85     };
86 
87     int glx_major, glx_minor;
88 
89     // FBConfigs were added in GLX version 1.3.
90     if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
91             ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) {
92         SkDebugf("GLX version 1.3 or higher required.\n");
93         this->destroyGLContext();
94         return;
95     }
96 
97     //SkDebugf("Getting matching framebuffer configs.\n");
98     int fbcount;
99     GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
100                                           visual_attribs, &fbcount);
101     if (!fbc) {
102         SkDebugf("Failed to retrieve a framebuffer config.\n");
103         this->destroyGLContext();
104         return;
105     }
106     //SkDebugf("Found %d matching FB configs.\n", fbcount);
107 
108     // Pick the FB config/visual with the most samples per pixel
109     //SkDebugf("Getting XVisualInfos.\n");
110     int best_fbc = -1, best_num_samp = -1;
111 
112     int i;
113     for (i = 0; i < fbcount; ++i) {
114         XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
115         if (vi) {
116             int samp_buf, samples;
117             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
118             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
119 
120             //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
121             //       " SAMPLES = %d\n",
122             //        i, (unsigned int)vi->visualid, samp_buf, samples);
123 
124             if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
125                 best_fbc = i, best_num_samp = samples;
126         }
127         XFree(vi);
128     }
129 
130     GLXFBConfig bestFbc = fbc[best_fbc];
131 
132     // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
133     XFree(fbc);
134 
135     // Get a visual
136     XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
137     //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
138 
139     fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
140 
141     if (!fPixmap) {
142         SkDebugf("Failed to create pixmap.\n");
143         this->destroyGLContext();
144         return;
145     }
146 
147     fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
148 
149     // Done with the visual info data
150     XFree(vi);
151 
152     // Create the context
153 
154     // Install an X error handler so the application won't exit if GL 3.0
155     // context allocation fails.
156     //
157     // Note this error handler is global.
158     // All display connections in all threads of a process use the same
159     // error handler, so be sure to guard against other threads issuing
160     // X commands while this code is running.
161     ctxErrorOccurred = false;
162     int (*oldHandler)(Display*, XErrorEvent*) =
163         XSetErrorHandler(&ctxErrorHandler);
164 
165     // Get the default screen's GLX extension list
166     const char *glxExts = glXQueryExtensionsString(
167         fDisplay, DefaultScreen(fDisplay)
168     );
169 
170 
171     // Check for the GLX_ARB_create_context extension string and the function.
172     // If either is not present, use GLX 1.3 context creation method.
173     if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"),
174                            reinterpret_cast<const GLubyte*>(glxExts))) {
175         if (kGLES_GrGLStandard != forcedGpuAPI) {
176             fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
177         }
178     } else {
179         //SkDebugf("Creating context.\n");
180         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
181             (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
182 
183         if (kGLES_GrGLStandard == forcedGpuAPI) {
184             if (gluCheckExtension(
185                     reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"),
186                     reinterpret_cast<const GLubyte*>(glxExts))) {
187                 static const int context_attribs_gles[] = {
188                     GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
189                     GLX_CONTEXT_MINOR_VERSION_ARB, 0,
190                     GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT,
191                     None
192                 };
193                 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
194                                                       context_attribs_gles);
195             }
196         } else {
197             // Well, unfortunately GLX will not just give us the highest context so instead we have
198             // to do this nastiness
199             for (i = NUM_GL_VERSIONS - 2; i > 0 ; i--) {
200                 /* don't bother below GL 3.0 */
201                 if (gl_versions[i].major == 3 && gl_versions[i].minor == 0) {
202                     break;
203                 }
204                 // On Nvidia GPUs, to use Nv Path rendering we need a compatibility profile for the
205                 // time being.
206                 // TODO when Nvidia implements NVPR on Core profiles, we should start requesting
207                 // core here
208                 static const int context_attribs_gl[] = {
209                       GLX_CONTEXT_MAJOR_VERSION_ARB, gl_versions[i].major,
210                       GLX_CONTEXT_MINOR_VERSION_ARB, gl_versions[i].minor,
211                       GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
212                       None
213                 };
214                 fContext =
215                         glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
216                                                    context_attribs_gl);
217 
218                 // Sync to ensure any errors generated are processed.
219                 XSync(fDisplay, False);
220 
221                 if (!ctxErrorOccurred && fContext) {
222                     break;
223                 }
224                 // try again
225                 ctxErrorOccurred = false;
226             }
227 
228             // Couldn't create GL 3.0 context.
229             // Fall back to old-style 2.x context.
230             // When a context version below 3.0 is requested,
231             // implementations will return the newest context version
232             // compatible with OpenGL versions less than version 3.0.
233             if (ctxErrorOccurred || !fContext) {
234                 static const int context_attribs_gl_fallback[] = {
235                     GLX_CONTEXT_MAJOR_VERSION_ARB, 1,
236                     GLX_CONTEXT_MINOR_VERSION_ARB, 0,
237                     None
238                 };
239 
240                 ctxErrorOccurred = false;
241 
242                 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, glxShareContext, True,
243                                                       context_attribs_gl_fallback);
244             }
245         }
246     }
247 
248     // Sync to ensure any errors generated are processed.
249     XSync(fDisplay, False);
250 
251     // Restore the original error handler
252     XSetErrorHandler(oldHandler);
253 
254     if (ctxErrorOccurred || !fContext) {
255         SkDebugf("Failed to create an OpenGL context.\n");
256         this->destroyGLContext();
257         return;
258     }
259 
260     // Verify that context is a direct context
261     if (!glXIsDirect(fDisplay, fContext)) {
262         //SkDebugf("Indirect GLX rendering context obtained.\n");
263     } else {
264         //SkDebugf("Direct GLX rendering context obtained.\n");
265     }
266 
267     //SkDebugf("Making context current.\n");
268     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
269       SkDebugf("Could not set the context.\n");
270         this->destroyGLContext();
271         return;
272     }
273 
274     SkAutoTUnref<const GrGLInterface> gl(GrGLCreateNativeInterface());
275     if (nullptr == gl.get()) {
276         SkDebugf("Failed to create gl interface");
277         this->destroyGLContext();
278         return;
279     }
280 
281     if (!gl->validate()) {
282         SkDebugf("Failed to validate gl interface");
283         this->destroyGLContext();
284         return;
285     }
286 
287     this->init(gl.detach());
288 }
289 
290 
~GLXGLContext()291 GLXGLContext::~GLXGLContext() {
292     this->teardown();
293     this->destroyGLContext();
294 }
295 
destroyGLContext()296 void GLXGLContext::destroyGLContext() {
297     if (fDisplay) {
298         glXMakeCurrent(fDisplay, 0, 0);
299 
300         if (fContext) {
301             glXDestroyContext(fDisplay, fContext);
302             fContext = nullptr;
303         }
304 
305         if (fGlxPixmap) {
306             glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
307             fGlxPixmap = 0;
308         }
309 
310         if (fPixmap) {
311             XFreePixmap(fDisplay, fPixmap);
312             fPixmap = 0;
313         }
314 
315         XCloseDisplay(fDisplay);
316         fDisplay = nullptr;
317     }
318 }
319 
onPlatformMakeCurrent() const320 void GLXGLContext::onPlatformMakeCurrent() const {
321     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
322         SkDebugf("Could not set the context.\n");
323     }
324 }
325 
onPlatformSwapBuffers() const326 void GLXGLContext::onPlatformSwapBuffers() const {
327     glXSwapBuffers(fDisplay, fGlxPixmap);
328 }
329 
onPlatformGetProcAddress(const char * procName) const330 GrGLFuncPtr GLXGLContext::onPlatformGetProcAddress(const char* procName) const {
331     return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
332 }
333 
334 } // anonymous namespace
335 
SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI,SkGLContext * shareContext)336 SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI, SkGLContext* shareContext) {
337     GLXGLContext* glxShareContext = reinterpret_cast<GLXGLContext*>(shareContext);
338     GLXGLContext *ctx = new GLXGLContext(forcedGpuAPI, glxShareContext);
339     if (!ctx->isValid()) {
340         delete ctx;
341         return nullptr;
342     }
343     return ctx;
344 }
345