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