1 /*
2 * Copyright © 2009 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Eric Anholt <eric@anholt.net>
25 *
26 */
27
28 #include "piglit-util-gl.h"
29 #include "piglit-glx-util.h"
30
31 #ifndef _WIN32
32 __attribute__((weak)) int piglit_width = 100;
33 __attribute__((weak)) int piglit_height = 100;
34 #endif
35
36 Display *
piglit_get_glx_display()37 piglit_get_glx_display()
38 {
39 Display *dpy;
40
41 dpy = XOpenDisplay(NULL);
42 if (!dpy) {
43 fprintf(stderr, "couldn't open display\n");
44 piglit_report_result(PIGLIT_FAIL);
45 }
46
47 return dpy;
48 }
49
50 XVisualInfo *
piglit_get_glx_visual(Display * dpy)51 piglit_get_glx_visual(Display *dpy)
52 {
53 XVisualInfo *visinfo;
54 int attrib[] = {
55 GLX_RGBA,
56 GLX_RED_SIZE, 1,
57 GLX_GREEN_SIZE, 1,
58 GLX_BLUE_SIZE, 1,
59 GLX_DEPTH_SIZE, 1,
60 GLX_STENCIL_SIZE, 1,
61 GLX_DOUBLEBUFFER,
62 None
63 };
64 int screen = DefaultScreen(dpy);
65
66 visinfo = glXChooseVisual(dpy, screen, attrib);
67 if (visinfo == NULL) {
68 fprintf(stderr,
69 "Couldn't get an RGBA, double-buffered visual\n");
70 piglit_report_result(PIGLIT_FAIL);
71 }
72
73 return visinfo;
74 }
75
76 GLXContext
piglit_get_glx_context(Display * dpy,XVisualInfo * visinfo)77 piglit_get_glx_context(Display *dpy, XVisualInfo *visinfo)
78 {
79 return piglit_get_glx_context_share(dpy, visinfo, NULL);
80 }
81
82 GLXContext
piglit_get_glx_context_share(Display * dpy,XVisualInfo * visinfo,GLXContext share)83 piglit_get_glx_context_share(Display *dpy, XVisualInfo *visinfo, GLXContext share)
84 {
85 GLXContext ctx;
86
87 ctx = glXCreateContext(dpy, visinfo, share, True);
88 if (ctx == None) {
89 fprintf(stderr, "glXCreateContext failed\n");
90 piglit_report_result(PIGLIT_FAIL);
91 }
92
93 return ctx;
94 }
95
96 static Window
_piglit_get_glx_window(Display * dpy,XVisualInfo * visinfo,bool map,bool fullscreen)97 _piglit_get_glx_window(Display *dpy, XVisualInfo *visinfo, bool map, bool fullscreen)
98 {
99 XSetWindowAttributes window_attr;
100 unsigned long mask;
101 int screen = DefaultScreen(dpy);
102 Window root_win = RootWindow(dpy, screen);
103 Window win;
104
105 window_attr.background_pixel = 0;
106 window_attr.border_pixel = 0;
107 window_attr.colormap = XCreateColormap(dpy, root_win,
108 visinfo->visual, AllocNone);
109 window_attr.event_mask = StructureNotifyMask | ExposureMask |
110 KeyPressMask;
111 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
112
113 if (fullscreen) {
114 window_attr.override_redirect = True;
115 mask |= CWOverrideRedirect;
116 piglit_width = DisplayWidth(dpy, screen);
117 piglit_height = DisplayHeight(dpy, screen);
118 }
119
120 win = XCreateWindow(dpy, root_win, 0, 0,
121 piglit_width, piglit_height,
122 0, visinfo->depth, InputOutput,
123 visinfo->visual, mask, &window_attr);
124
125 if (piglit_automatic)
126 piglit_glx_window_set_no_input(dpy, win);
127
128 if (map)
129 XMapWindow(dpy, win);
130
131 return win;
132 }
133
134 Window
piglit_get_glx_window_unmapped(Display * dpy,XVisualInfo * visinfo)135 piglit_get_glx_window_unmapped(Display *dpy, XVisualInfo *visinfo)
136 {
137 return _piglit_get_glx_window(dpy, visinfo, false, false);
138 }
139
140 Window
piglit_get_glx_window_fullscreen(Display * dpy,XVisualInfo * visinfo)141 piglit_get_glx_window_fullscreen(Display *dpy, XVisualInfo *visinfo)
142 {
143 return _piglit_get_glx_window(dpy, visinfo, true, true);
144 }
145
146 Window
piglit_get_glx_window(Display * dpy,XVisualInfo * visinfo)147 piglit_get_glx_window(Display *dpy, XVisualInfo *visinfo)
148 {
149 return _piglit_get_glx_window(dpy, visinfo, true, false);
150 }
151
152 bool
piglit_is_glx_extension_supported(Display * dpy,const char * name)153 piglit_is_glx_extension_supported(Display *dpy, const char *name)
154 {
155 int screen = DefaultScreen(dpy);
156 const char *const glx_extension_list =
157 glXQueryExtensionsString(dpy, screen);
158
159 return piglit_is_extension_in_string(glx_extension_list, name);
160 }
161
162 void
piglit_require_glx_extension(Display * dpy,const char * name)163 piglit_require_glx_extension(Display *dpy, const char *name)
164 {
165 if (!piglit_is_glx_extension_supported(dpy, name)) {
166 fprintf(stderr, "Test requires %s\n", name);
167 piglit_report_result(PIGLIT_SKIP);
168 }
169 }
170
171
172 void
piglit_require_glx_version(Display * dpy,int major,int minor)173 piglit_require_glx_version(Display *dpy, int major, int minor)
174 {
175 int glxMajor;
176 int glxMinor;
177
178 if (! glXQueryVersion(dpy, & glxMajor, & glxMinor)) {
179 fprintf(stderr, "Could not query GLX version!\n");
180 piglit_report_result(PIGLIT_FAIL);
181 }
182
183 if (glxMajor != major || glxMinor < minor) {
184 fprintf(stderr, "Test requires GLX %d.%d. Got %d.%d.\n",
185 major, minor, glxMajor, glxMinor);
186 piglit_report_result(PIGLIT_SKIP);
187 }
188 }
189
190
191 void
piglit_glx_event_loop(Display * dpy,enum piglit_result (* draw)(Display * dpy))192 piglit_glx_event_loop(Display *dpy, enum piglit_result (*draw)(Display *dpy))
193 {
194 for (;;) {
195 XEvent event;
196 XNextEvent (dpy, &event);
197
198 if (event.type == KeyPress) {
199 int keysyms_per_keycode_return;
200 KeySym *sym = XGetKeyboardMapping (dpy,
201 event.xkey.keycode,
202 1,
203 &keysyms_per_keycode_return);
204
205 if (sym[0] == XK_Escape || sym[0] == XK_q || sym[0] == XK_Q)
206 break;
207 else
208 draw(dpy);
209 } else if (event.type == Expose) {
210 enum piglit_result result = draw(dpy);
211
212 if (piglit_automatic) {
213 /*
214 * Rerun if we have failed and have a
215 * pending expose event, which might be an
216 * indication of invalid front buffer
217 * contents.
218 */
219 if (result == PIGLIT_FAIL &&
220 XCheckTypedEvent(dpy, Expose, &event)) {
221 fprintf(stderr,
222 "Pending expose event- "
223 "rerunning.\n");
224 XPutBackEvent(dpy, &event);
225 continue;
226 }
227 XCloseDisplay(dpy);
228 piglit_report_result(result);
229 break;
230 }
231 }
232 }
233 }
234
235
236 static enum piglit_result
piglit_iterate_visuals_event_loop(Display * dpy,enum piglit_result (* draw)(Display * dpy,GLXFBConfig config),GLXFBConfig config)237 piglit_iterate_visuals_event_loop(Display *dpy,
238 enum piglit_result (*draw)(Display *dpy,
239 GLXFBConfig config),
240 GLXFBConfig config)
241 {
242 for (;;) {
243 XEvent event;
244 XNextEvent (dpy, &event);
245
246 if (event.type == Expose) {
247 return draw(dpy, config);
248 }
249 }
250 }
251
252 void
piglit_glx_window_set_no_input(Display * dpy,GLXDrawable win)253 piglit_glx_window_set_no_input(Display *dpy, GLXDrawable win)
254 {
255 XWMHints *hints;
256 hints = XAllocWMHints();
257 hints->flags |= InputHint;
258 hints->input = False;
259
260 XSetWMHints(dpy, win, hints);
261
262 XFree(hints);
263 }
264
265 void
piglit_glx_set_no_input(void)266 piglit_glx_set_no_input(void)
267 {
268 Display *d;
269 GLXDrawable win;
270
271 d = glXGetCurrentDisplay();
272 win = glXGetCurrentDrawable();
273
274 piglit_glx_window_set_no_input(d, win);
275 }
276
277 enum piglit_result
piglit_glx_iterate_pixmap_fbconfigs(enum piglit_result (* draw)(Display * dpy,GLXFBConfig config))278 piglit_glx_iterate_pixmap_fbconfigs(enum piglit_result (*draw)(Display *dpy,
279 GLXFBConfig config))
280 {
281 int screen;
282 GLXFBConfig *configs;
283 int n_configs;
284 int i;
285 bool any_fail = false;
286 bool any_pass = false;
287 Window root_win;
288
289 Display *dpy = XOpenDisplay(NULL);
290 if (!dpy) {
291 fprintf(stderr, "couldn't open display\n");
292 piglit_report_result(PIGLIT_FAIL);
293 }
294 screen = DefaultScreen(dpy);
295 root_win = RootWindow(dpy, screen);
296
297 configs = glXGetFBConfigs(dpy, screen, &n_configs);
298 if (!configs) {
299 fprintf(stderr, "No GLX FB configs\n");
300 piglit_report_result(PIGLIT_SKIP);
301 }
302
303 for (i = 0; i < n_configs; i++) {
304 GLXFBConfig config = configs[i];
305 enum piglit_result result;
306 GLXContext ctx;
307 Pixmap pix;
308 GLXPixmap glx_pix;
309 int draw_types;
310 int depth;
311
312 glXGetFBConfigAttrib(dpy, config, GLX_DRAWABLE_TYPE,
313 &draw_types);
314
315 if (!(draw_types & GLX_PIXMAP_BIT))
316 continue;
317
318 glXGetFBConfigAttrib(dpy, config, GLX_BUFFER_SIZE,
319 &depth);
320 ctx = glXCreateNewContext(dpy, config, GLX_RGBA_TYPE,
321 NULL, true);
322 pix = XCreatePixmap(dpy, root_win,
323 piglit_width, piglit_height, depth);
324 glx_pix = glXCreatePixmap(dpy, config, pix, NULL);
325 glXMakeCurrent(dpy, glx_pix, ctx);
326
327 result = draw(dpy, config);
328
329 if (result == PIGLIT_FAIL)
330 any_fail = true;
331 else if (result == PIGLIT_PASS)
332 any_pass = true;
333
334 XFreePixmap(dpy, pix);
335 glXDestroyContext(dpy, ctx);
336 }
337
338 XFree(configs);
339
340 if (any_fail)
341 return PIGLIT_FAIL;
342 else if (any_pass)
343 return PIGLIT_PASS;
344 else
345 return PIGLIT_SKIP;
346 }
347
348 enum piglit_result
piglit_glx_iterate_visuals(enum piglit_result (* draw)(Display * dpy,GLXFBConfig config))349 piglit_glx_iterate_visuals(enum piglit_result (*draw)(Display *dpy,
350 GLXFBConfig config))
351 {
352 int screen;
353 GLXFBConfig *configs;
354 int n_configs;
355 int i;
356 bool any_fail = false;
357 bool any_pass = false;
358
359 Display *dpy = XOpenDisplay(NULL);
360 if (!dpy) {
361 fprintf(stderr, "couldn't open display\n");
362 piglit_report_result(PIGLIT_FAIL);
363 }
364 screen = DefaultScreen(dpy);
365
366 configs = glXGetFBConfigs(dpy, screen, &n_configs);
367 if (!configs) {
368 fprintf(stderr, "No GLX FB configs\n");
369 piglit_report_result(PIGLIT_SKIP);
370 }
371
372 for (i = 0; i < n_configs; i++) {
373 enum piglit_result result;
374 XVisualInfo *visinfo;
375 GLXContext ctx;
376 GLXDrawable d;
377
378 visinfo = glXGetVisualFromFBConfig(dpy, configs[i]);
379 if (!visinfo)
380 continue;
381
382 ctx = piglit_get_glx_context(dpy, visinfo);
383 d = piglit_get_glx_window(dpy, visinfo);
384 glXMakeCurrent(dpy, d, ctx);
385 XFree(visinfo);
386
387 result = piglit_iterate_visuals_event_loop(dpy, draw,
388 configs[i]);
389 if (result == PIGLIT_FAIL)
390 any_fail = true;
391 else if (result == PIGLIT_PASS)
392 any_pass = true;
393
394 XDestroyWindow(dpy, d);
395 glXDestroyContext(dpy, ctx);
396 }
397
398 XFree(configs);
399
400 if (any_fail)
401 return PIGLIT_FAIL;
402 else if (any_pass)
403 return PIGLIT_PASS;
404 else
405 return PIGLIT_SKIP;
406 }
407
408 GLXFBConfig
piglit_glx_get_fbconfig_for_visinfo(Display * dpy,XVisualInfo * visinfo)409 piglit_glx_get_fbconfig_for_visinfo(Display *dpy, XVisualInfo *visinfo)
410 {
411 int i, nconfigs;
412 GLXFBConfig ret = None, *configs;
413
414 configs = glXGetFBConfigs(dpy, visinfo->screen, &nconfigs);
415 if (!configs)
416 return None;
417
418 for (i = 0; i < nconfigs; i++) {
419 int v;
420
421 if (glXGetFBConfigAttrib(dpy, configs[i], GLX_VISUAL_ID, &v))
422 continue;
423
424 if (v == visinfo->visualid) {
425 ret = configs[i];
426 break;
427 }
428 }
429
430 XFree(configs);
431 return ret;
432 }
433
434 /*
435 * If you use this in an X error handler - and you will - pre-call it as:
436 * piglit_glx_get_error(dpy, NULL);
437 * outside the error handler to cache errbase. Otherwise this will
438 * generate protocol, and you'll deadlock.
439 *
440 * Returns -1 if the error is not a GLX error, otherwise returns the
441 * GLX error code.
442 */
443 int
piglit_glx_get_error(Display * dpy,XErrorEvent * err)444 piglit_glx_get_error(Display *dpy, XErrorEvent *err)
445 {
446 static int errbase, evbase;
447
448 if (!errbase)
449 glXQueryExtension(dpy, &errbase, &evbase);
450
451 if (!err)
452 return -1;
453
454 if (err->error_code < errbase ||
455 err->error_code > errbase + GLXBadProfileARB)
456 return -1;
457
458 return err->error_code - errbase;
459 }
460
461 /**
462 * Convert a GLX error code to a printable string
463 *
464 * \sa piglit_glx_get_error
465 */
466 const char *
piglit_glx_error_string(int err)467 piglit_glx_error_string(int err)
468 {
469 static const char *const error_table[] = {
470 "GLXBadContext",
471 "GLXBadContextState",
472 "GLXBadDrawable",
473 "GLXBadPixmap",
474 "GLXBadContextTag",
475 "GLXBadCurrentWindow",
476 "GLXBadRenderRequest",
477 "GLXBadLargeRequest",
478 "GLXUnsupportedPrivateRequest",
479 "GLXBadFBConfig",
480 "GLXBadPbuffer",
481 "GLXBadCurrentDrawable",
482 "GLXBadWindow",
483 "GLXBadProfileARB"
484 };
485
486 if (err < 0) {
487 return "non-GLX error";
488 } else if (err >= ARRAY_SIZE(error_table)) {
489 return "unknown GLX error";
490 } else {
491 return error_table[err];
492 }
493 }
494
495 /**
496 * Get the procedure adddresses for a group of function names
497 *
498 * \note
499 * If any call to \c glXGetProcAddress fails, this function will call
500 * \c piglit_report_result with \c PIGLIT_FAIL.
501 */
502 void
piglit_glx_get_all_proc_addresses(const struct piglit_glx_proc_reference * procedures,unsigned num)503 piglit_glx_get_all_proc_addresses(const struct piglit_glx_proc_reference *procedures,
504 unsigned num)
505 {
506 unsigned i;
507
508 for (i = 0; i < num; i++) {
509 *procedures[i].procedure =
510 glXGetProcAddress((const GLubyte *) procedures[i].name);
511 if (*procedures[i].procedure == NULL) {
512 fprintf(stderr,
513 "Failed to get function pointer for %s.\n",
514 procedures[i].name);
515 piglit_report_result(PIGLIT_FAIL);
516 }
517 }
518 }
519