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