|
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 |
|
14 SkNativeGLContext::AutoContextRestore::AutoContextRestore() { |
|
15 fOldGLXContext = glXGetCurrentContext(); |
|
16 fOldDisplay = glXGetCurrentDisplay(); |
|
17 fOldDrawable = glXGetCurrentDrawable(); |
|
18 } |
|
19 |
|
20 SkNativeGLContext::AutoContextRestore::~AutoContextRestore() { |
|
21 if (NULL != fOldDisplay) { |
|
22 glXMakeCurrent(fOldDisplay, fOldDrawable, fOldGLXContext); |
|
23 } |
|
24 } |
|
25 |
|
26 /////////////////////////////////////////////////////////////////////////////// |
|
27 |
|
28 static bool ctxErrorOccurred = false; |
|
29 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { |
|
30 ctxErrorOccurred = true; |
|
31 return 0; |
|
32 } |
|
33 |
|
34 SkNativeGLContext::SkNativeGLContext() |
|
35 : fContext(NULL) |
|
36 , fDisplay(NULL) |
|
37 , fPixmap(0) |
|
38 , fGlxPixmap(0) { |
|
39 } |
|
40 |
|
41 SkNativeGLContext::~SkNativeGLContext() { |
|
42 this->destroyGLContext(); |
|
43 } |
|
44 |
|
45 void 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 |
|
69 const 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 |
|
283 void SkNativeGLContext::makeCurrent() const { |
|
284 if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) { |
|
285 SkDebugf("Could not set the context.\n"); |
|
286 } |
|
287 } |
|
288 |
|
289 void SkNativeGLContext::swapBuffers() const { |
|
290 glXSwapBuffers(fDisplay, fGlxPixmap); |
|
291 } |