SkNativeGLContext_unix.cpp revision 78ff6b4ab677e156b016054ece6a4d08d5846cb7
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/SkNativeGLContext.h"
9
10#include <GL/glu.h>
11
12#define GLX_1_3 1
13
14SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
15    fOldGLXContext = glXGetCurrentContext();
16    fOldDisplay = glXGetCurrentDisplay();
17    fOldDrawable = glXGetCurrentDrawable();
18}
19
20SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
21    if (NULL != fOldDisplay) {
22        glXMakeCurrent(fOldDisplay, fOldDrawable, fOldGLXContext);
23    }
24}
25
26///////////////////////////////////////////////////////////////////////////////
27
28static bool ctxErrorOccurred = false;
29static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
30    ctxErrorOccurred = true;
31    return 0;
32}
33
34SkNativeGLContext::SkNativeGLContext()
35    : fContext(NULL)
36    , fDisplay(NULL)
37    , fPixmap(0)
38    , fGlxPixmap(0) {
39}
40
41SkNativeGLContext::~SkNativeGLContext() {
42    this->destroyGLContext();
43}
44
45void SkNativeGLContext::destroyGLContext() {
46    if (fDisplay) {
47        glXMakeCurrent(fDisplay, 0, 0);
48
49        if (fContext) {
50            glXDestroyContext(fDisplay, fContext);
51            fContext = NULL;
52        }
53
54        if (fGlxPixmap) {
55            glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
56            fGlxPixmap = 0;
57        }
58
59        if (fPixmap) {
60            XFreePixmap(fDisplay, fPixmap);
61            fPixmap = 0;
62        }
63
64        XCloseDisplay(fDisplay);
65        fDisplay = NULL;
66    }
67}
68
69const GrGLInterface* SkNativeGLContext::createGLContext() {
70    fDisplay = XOpenDisplay(0);
71
72    if (!fDisplay) {
73        SkDebugf("Failed to open X display.\n");
74        this->destroyGLContext();
75        return NULL;
76    }
77
78    // Get a matching FB config
79    static int visual_attribs[] = {
80        GLX_X_RENDERABLE    , True,
81        GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
82        None
83    };
84
85#ifdef GLX_1_3
86    //SkDebugf("Getting matching framebuffer configs.\n");
87    int fbcount;
88    GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
89                                          visual_attribs, &fbcount);
90    if (!fbc) {
91        SkDebugf("Failed to retrieve a framebuffer config.\n");
92        this->destroyGLContext();
93        return NULL;
94    }
95    //SkDebugf("Found %d matching FB configs.\n", fbcount);
96
97    // Pick the FB config/visual with the most samples per pixel
98    //SkDebugf("Getting XVisualInfos.\n");
99    int best_fbc = -1, best_num_samp = -1;
100
101    int i;
102    for (i = 0; i < fbcount; ++i) {
103        XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
104        if (vi) {
105            int samp_buf, samples;
106            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
107            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
108
109            //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
110            //       " SAMPLES = %d\n",
111            //        i, (unsigned int)vi->visualid, samp_buf, samples);
112
113            if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
114                best_fbc = i, best_num_samp = samples;
115        }
116        XFree(vi);
117    }
118
119    GLXFBConfig bestFbc = fbc[best_fbc];
120
121    // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
122    XFree(fbc);
123
124    // Get a visual
125    XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
126    //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
127#else
128    int numVisuals;
129    XVisualInfo visTemplate, *visReturn;
130
131    visReturn = XGetVisualInfo(fDisplay, VisualNoMask, &visTemplate, &numVisuals);
132    if (NULL == visReturn)
133    {
134        SkDebugf("Failed to get visual information.\n");
135        this->destroyGLContext();
136        return NULL;
137    }
138
139    int best = -1, best_num_samp = -1;
140
141    for (int i = 0; i < numVisuals; ++i)
142    {
143        int samp_buf, samples;
144
145        glXGetConfig(fDisplay, &visReturn[i], GLX_SAMPLE_BUFFERS, &samp_buf);
146        glXGetConfig(fDisplay, &visReturn[i], GLX_SAMPLES, &samples);
147
148        if (best < 0 || (samp_buf && samples > best_num_samp))
149            best = i, best_num_samp = samples;
150    }
151
152    XVisualInfo temp = visReturn[best];
153    XVisualInfo *vi = &temp;
154
155    XFree(visReturn);
156#endif
157
158    fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
159
160    if (!fPixmap) {
161        SkDebugf("Failed to create pixmap.\n");
162        this->destroyGLContext();
163        return NULL;
164    }
165
166    fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
167
168#ifdef GLX_1_3
169    // Done with the visual info data
170    XFree(vi);
171#endif
172
173    // Create the context
174
175    // Install an X error handler so the application won't exit if GL 3.0
176    // context allocation fails.
177    //
178    // Note this error handler is global.
179    // All display connections in all threads of a process use the same
180    // error handler, so be sure to guard against other threads issuing
181    // X commands while this code is running.
182    ctxErrorOccurred = false;
183    int (*oldHandler)(Display*, XErrorEvent*) =
184        XSetErrorHandler(&ctxErrorHandler);
185
186    // Get the default screen's GLX extension list
187    const char *glxExts = glXQueryExtensionsString(
188        fDisplay, DefaultScreen(fDisplay)
189    );
190    // Check for the GLX_ARB_create_context extension string and the function.
191    // If either is not present, use GLX 1.3 context creation method.
192    if (!gluCheckExtension(
193          reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
194          , reinterpret_cast<const GLubyte*>(glxExts)))
195    {
196        //SkDebugf("GLX_ARB_create_context not found."
197        //       " Using old-style GLX context.\n");
198#ifdef GLX_1_3
199        fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
200#else
201        fContext = glXCreateContext(fDisplay, vi, 0, True);
202#endif
203
204    }
205#ifdef GLX_1_3
206    else {
207        //SkDebugf("Creating context.\n");
208
209        PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
210            (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
211        int context_attribs[] = {
212            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
213            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
214            //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
215            None
216        };
217        fContext = glXCreateContextAttribsARB(
218            fDisplay, bestFbc, 0, True, context_attribs
219        );
220
221        // Sync to ensure any errors generated are processed.
222        XSync(fDisplay, False);
223        if (!ctxErrorOccurred && fContext) {
224           //SkDebugf( "Created GL 3.0 context.\n" );
225        } else {
226            // Couldn't create GL 3.0 context.
227            // Fall back to old-style 2.x context.
228            // When a context version below 3.0 is requested,
229            // implementations will return the newest context version compatible
230            // with OpenGL versions less than version 3.0.
231
232            // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
233            context_attribs[1] = 1;
234            // GLX_CONTEXT_MINOR_VERSION_ARB = 0
235            context_attribs[3] = 0;
236
237            ctxErrorOccurred = false;
238
239            //SkDebugf("Failed to create GL 3.0 context."
240            //       " Using old-style GLX context.\n");
241            fContext = glXCreateContextAttribsARB(
242                fDisplay, bestFbc, 0, True, context_attribs
243            );
244        }
245    }
246#endif
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 NULL;
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 NULL;
272    }
273
274    const GrGLInterface* interface = GrGLCreateNativeInterface();
275    if (!interface) {
276        SkDebugf("Failed to create gl interface");
277        this->destroyGLContext();
278        return NULL;
279    }
280    return interface;
281}
282
283void SkNativeGLContext::makeCurrent() const {
284    if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
285        SkDebugf("Could not set the context.\n");
286    }
287}
288