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