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