Panda3D
Loading...
Searching...
No Matches
glxGraphicsStateGuardian.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file glxGraphicsStateGuardian.cxx
10 * @author drose
11 * @date 2003-01-27
12 */
13
15#include "config_glxdisplay.h"
16#include "config_glgsg.h"
17#include "lightReMutexHolder.h"
18
19#include <dlfcn.h>
20
21using std::string;
22
23
24TypeHandle glxGraphicsStateGuardian::_type_handle;
25
26/**
27 *
28 */
29glxGraphicsStateGuardian::
30glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
31 glxGraphicsStateGuardian *share_with) :
32 PosixGraphicsStateGuardian(engine, pipe)
33{
34 _share_context=nullptr;
35 _context=nullptr;
36 _display=nullptr;
37 _screen=0;
38 _visual=nullptr;
39 _visuals=nullptr;
40 _fbconfig=nullptr;
41 _context_has_pbuffer = false;
42 _context_has_pixmap = false;
43 _slow = false;
44
45 _supports_swap_control = false;
46 _supports_fbconfig = false;
47 _supports_pbuffer = false;
48 _uses_sgix_pbuffer = false;
49
50 if (share_with != nullptr) {
51 _prepared_objects = share_with->get_prepared_objects();
52 _share_context = share_with->_context;
53 }
54
55 _checked_get_proc_address = false;
56 _glXGetProcAddress = nullptr;
57 _temp_context = (GLXContext)nullptr;
58 _temp_xwindow = (X11_Window)nullptr;
59 _temp_colormap = (Colormap)nullptr;
60}
61
62/**
63 *
64 */
65glxGraphicsStateGuardian::
66~glxGraphicsStateGuardian() {
67 // Actually, the lock might have already destructed, so we can't reliably
68 // grab the X11 lock here.
69 //LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
70 destroy_temp_xwindow();
71 if (_visuals != nullptr) {
72 XFree(_visuals);
73 }
74 if (_context != (GLXContext)nullptr) {
75 glXDestroyContext(_display, _context);
76 _context = (GLXContext)nullptr;
77 }
78}
79
80/**
81 * Gets the FrameBufferProperties to match the indicated visual.
82 */
84get_properties(FrameBufferProperties &properties, XVisualInfo *visual) {
85
86 int use_gl, render_mode, double_buffer, stereo,
87 red_size, green_size, blue_size,
88 alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
89 depth_size, stencil_size;
90
91 glXGetConfig(_display, visual, GLX_USE_GL, &use_gl);
92 glXGetConfig(_display, visual, GLX_RGBA, &render_mode);
93 glXGetConfig(_display, visual, GLX_DOUBLEBUFFER, &double_buffer);
94 glXGetConfig(_display, visual, GLX_STEREO, &stereo);
95 glXGetConfig(_display, visual, GLX_RED_SIZE, &red_size);
96 glXGetConfig(_display, visual, GLX_GREEN_SIZE, &green_size);
97 glXGetConfig(_display, visual, GLX_BLUE_SIZE, &blue_size);
98 glXGetConfig(_display, visual, GLX_ALPHA_SIZE, &alpha_size);
99 glXGetConfig(_display, visual, GLX_ACCUM_RED_SIZE, &ared_size);
100 glXGetConfig(_display, visual, GLX_ACCUM_GREEN_SIZE, &agreen_size);
101 glXGetConfig(_display, visual, GLX_ACCUM_BLUE_SIZE, &ablue_size);
102 glXGetConfig(_display, visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
103 glXGetConfig(_display, visual, GLX_DEPTH_SIZE, &depth_size);
104 glXGetConfig(_display, visual, GLX_STENCIL_SIZE, &stencil_size);
105
106 properties.clear();
107
108 if (use_gl == 0) {
109 // If we return a set of properties without setting either rgb_color or
110 // indexed_color, then this indicates a visual that's no good for any kind
111 // of rendering.
112 return;
113 }
114
115 if (double_buffer) {
116 properties.set_back_buffers(1);
117 }
118 if (stereo) {
119 properties.set_stereo(1);
120 }
121 if (render_mode) {
122 properties.set_rgb_color(1);
123 } else {
124 properties.set_indexed_color(1);
125 }
126
127 properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
128 properties.set_stencil_bits(stencil_size);
129 properties.set_depth_bits(depth_size);
130 properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
131
132 // Set both hardware and software bits, indicating not-yet-known.
133 properties.set_force_software(1);
134 properties.set_force_hardware(1);
135}
136
137/**
138 * Gets the FrameBufferProperties to match the indicated GLXFBConfig
139 */
142 bool &context_has_pbuffer, bool &context_has_pixmap,
143 bool &slow, GLXFBConfig config) {
144 properties.clear();
145
146 if (_supports_fbconfig) {
147 // Now update our framebuffer_mode and bit depth appropriately.
148 int render_type, double_buffer, stereo, red_size, green_size, blue_size,
149 alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
150 depth_size, stencil_size, samples, drawable_type, caveat, srgb_capable;
151
152 _glXGetFBConfigAttrib(_display, config, GLX_RENDER_TYPE, &render_type);
153 _glXGetFBConfigAttrib(_display, config, GLX_DOUBLEBUFFER, &double_buffer);
154 _glXGetFBConfigAttrib(_display, config, GLX_STEREO, &stereo);
155 _glXGetFBConfigAttrib(_display, config, GLX_RED_SIZE, &red_size);
156 _glXGetFBConfigAttrib(_display, config, GLX_GREEN_SIZE, &green_size);
157 _glXGetFBConfigAttrib(_display, config, GLX_BLUE_SIZE, &blue_size);
158 _glXGetFBConfigAttrib(_display, config, GLX_ALPHA_SIZE, &alpha_size);
159 _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_RED_SIZE, &ared_size);
160 _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_GREEN_SIZE, &agreen_size);
161 _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_BLUE_SIZE, &ablue_size);
162 _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
163 _glXGetFBConfigAttrib(_display, config, GLX_DEPTH_SIZE, &depth_size);
164 _glXGetFBConfigAttrib(_display, config, GLX_STENCIL_SIZE, &stencil_size);
165 _glXGetFBConfigAttrib(_display, config, GLX_SAMPLES, &samples);
166 _glXGetFBConfigAttrib(_display, config, GLX_DRAWABLE_TYPE, &drawable_type);
167 _glXGetFBConfigAttrib(_display, config, GLX_CONFIG_CAVEAT, &caveat);
168 _glXGetFBConfigAttrib(_display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);
169
170 context_has_pbuffer = false;
171 if ((drawable_type & GLX_PBUFFER_BIT)!=0) {
172 context_has_pbuffer = true;
173 }
174
175 context_has_pixmap = false;
176 if ((drawable_type & GLX_PIXMAP_BIT)!=0) {
177 context_has_pixmap = true;
178 }
179
180 slow = false;
181 if (caveat == GLX_SLOW_CONFIG) {
182 slow = true;
183 }
184
185 if ((drawable_type & GLX_WINDOW_BIT)==0) {
186 // We insist on having a context that will support an onscreen window.
187 return;
188 }
189
190 if (double_buffer) {
191 properties.set_back_buffers(1);
192 }
193
194 if (stereo) {
195 properties.set_stereo(true);
196 }
197
198 if (srgb_capable) {
199 properties.set_srgb_color(true);
200 }
201
202 if ((render_type & GLX_RGBA_BIT)!=0) {
203 properties.set_rgb_color(true);
204 }
205 if ((render_type & GLX_COLOR_INDEX_BIT)!=0) {
206 properties.set_indexed_color(true);
207 }
208
209 properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
210 properties.set_stencil_bits(stencil_size);
211 properties.set_depth_bits(depth_size);
212 properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
213 properties.set_multisamples(samples);
214
215 // Set both hardware and software bits, indicating not-yet-known.
216 properties.set_force_software(1);
217 properties.set_force_hardware(1);
218 }
219}
220
221/**
222 * Selects a visual or fbconfig for all the windows and buffers that use this
223 * gsg. Also creates the GL context and obtains the visual.
224 */
227 X11_Display *display,
228 int screen, bool need_pbuffer, bool need_pixmap) {
229
230 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
231 _display = display;
232 _screen = screen;
233 _context = nullptr;
234 _fbconfig = nullptr;
235 _visual = nullptr;
236 if (_visuals != nullptr) {
237 XFree(_visuals);
238 _visuals = nullptr;
239 }
240
241 _fbprops.clear();
242
243 // First, attempt to create a context using the XVisual interface. We need
244 // this before we can query the FBConfig interface, because we need an
245 // OpenGL context to get the required extension function pointers.
246 destroy_temp_xwindow();
247 choose_temp_visual(properties);
248 if (_temp_context == nullptr) {
249 // No good.
250 return;
251 }
252
253 // Now we have to initialize the context so we can query its capabilities
254 // and extensions. This also means creating a temporary window, so we have
255 // something to bind the context to and make it current.
256 init_temp_context();
257
258 if (!_supports_fbconfig) {
259 // We have a good OpenGL context, but it doesn't support the FBConfig
260 // interface, so we'll stop there.
261 glxdisplay_cat.debug()
262 <<" No FBConfig supported; using XVisual only.\n"
263 << _fbprops << "\n";
264
265 _context = _temp_context;
266 _temp_context = (GLXContext)nullptr;
267
268 // By convention, every indirect XVisual that can render to a window can
269 // also render to a GLXPixmap. Direct visuals we're not as sure about.
270 _context_has_pixmap = !glXIsDirect(_display, _context);
271
272 // Pbuffers aren't supported at all with the XVisual interface.
273 _context_has_pbuffer = false;
274 return;
275 }
276
277 // The OpenGL context supports the FBConfig interface, so we can use that
278 // more advanced interface to choose the actual window format we'll use.
279 // FBConfig provides for more options than the older XVisual interface, so
280 // we'd much rather use it if it's available.
281
282 int best_quality = 0;
283 int best_result = 0;
284 FrameBufferProperties best_props;
285
286 int render_type = GLX_RGBA_TYPE;
287 if (properties.get_indexed_color()) {
288 render_type = GLX_COLOR_INDEX_TYPE;
289 }
290
291 static const int max_attrib_list = 32;
292 int attrib_list[max_attrib_list];
293 int n = 0;
294 attrib_list[n++] = GLX_STEREO;
295 attrib_list[n++] = GLX_DONT_CARE;
296 attrib_list[n++] = GLX_RENDER_TYPE;
297 attrib_list[n++] = GLX_DONT_CARE;
298 attrib_list[n++] = GLX_DRAWABLE_TYPE;
299 attrib_list[n++] = GLX_DONT_CARE;
300 attrib_list[n] = (int)None;
301
302 int num_configs = 0;
303 GLXFBConfig *configs =
304 _glXChooseFBConfig(_display, _screen, attrib_list, &num_configs);
305
306 if (configs != nullptr) {
307 bool context_has_pbuffer, context_has_pixmap, slow;
308 int quality, i;
309 for (i = 0; i < num_configs; ++i) {
310 FrameBufferProperties fbprops;
311 get_properties_advanced(fbprops, context_has_pbuffer, context_has_pixmap,
312 slow, configs[i]);
313 quality = fbprops.get_quality(properties);
314 if ((quality > 0)&&(slow)) quality -= 10000000;
315 if (glxdisplay_cat.is_debug()) {
316 const char *pbuffertext = context_has_pbuffer ? " (pbuffer)" : "";
317 const char *pixmaptext = context_has_pixmap ? " (pixmap)" : "";
318 const char *slowtext = slow ? " (slow)" : "";
319 glxdisplay_cat.debug()
320 << i << ": " << fbprops << " quality=" << quality << pbuffertext
321 << pixmaptext << slowtext << "\n";
322 }
323 if (need_pbuffer && !context_has_pbuffer) {
324 continue;
325 }
326 if (need_pixmap && !context_has_pixmap) {
327 continue;
328 }
329
330 if (quality > best_quality) {
331 best_quality = quality;
332 best_result = i;
333 best_props = fbprops;
334 }
335 }
336 }
337
338 if (best_quality > 0) {
339 _fbconfig = configs[best_result];
340
341 if (_glXCreateContextAttribs != nullptr) {
342 // NB. This is a wholly different type of attrib list than below, the
343 // same values are not used!
344 n = 0;
345 attrib_list[n++] = GLX_RENDER_TYPE;
346 attrib_list[n++] = render_type;
347 if (gl_version.get_num_words() > 0) {
348 attrib_list[n++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
349 attrib_list[n++] = gl_version[0];
350 if (gl_version.get_num_words() > 1) {
351 attrib_list[n++] = GLX_CONTEXT_MINOR_VERSION_ARB;
352 attrib_list[n++] = gl_version[1];
353 }
354 }
355 int flags = 0;
356 if (gl_debug) {
357 flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
358 }
359 if (gl_forward_compatible) {
360 flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
361 if (gl_version.get_num_words() == 0 || gl_version[0] < 2) {
362 glxdisplay_cat.error()
363 << "gl-forward-compatible requires gl-version >= 3 0\n";
364 }
365 }
366 if (flags != 0) {
367 attrib_list[n++] = GLX_CONTEXT_FLAGS_ARB;
368 attrib_list[n++] = flags;
369 }
370 attrib_list[n] = None;
371 _context = _glXCreateContextAttribs(_display, _fbconfig, _share_context,
372 GL_TRUE, attrib_list);
373 } else {
374 _context =
375 _glXCreateNewContext(_display, _fbconfig, render_type, _share_context,
376 GL_TRUE);
377 }
378
379 if (_context) {
380 mark_new();
381
382 if (_visuals != nullptr) {
383 XFree(_visuals);
384 _visuals = nullptr;
385 }
386 _visuals = _glXGetVisualFromFBConfig(_display, _fbconfig);
387 _visual = _visuals;
388
389 if (_visual) {
390 get_properties_advanced(_fbprops, _context_has_pbuffer, _context_has_pixmap,
391 _slow, _fbconfig);
392
393 if (!properties.get_srgb_color()) {
394 _fbprops.set_srgb_color(false);
395 }
396
397 if (glxdisplay_cat.is_debug()) {
398 glxdisplay_cat.debug()
399 << "Selected context " << best_result << ": " << _fbprops << "\n";
400 glxdisplay_cat.debug()
401 << "context_has_pbuffer = " << _context_has_pbuffer
402 << ", context_has_pixmap = " << _context_has_pixmap << "\n";
403 }
404
405 return;
406 }
407 }
408 // This really shouldn't happen, so I'm not too careful about cleanup.
409 glxdisplay_cat.error()
410 << "Could not create FBConfig context!\n";
411 _fbconfig = nullptr;
412 _context = nullptr;
413 _visual = nullptr;
414 _visuals = nullptr;
415 }
416
417 glxdisplay_cat.warning()
418 << "No suitable FBConfig contexts available; using XVisual only.\n"
419 << _fbprops << "\n";
420
421 _context = _temp_context;
422 _temp_context = (GLXContext)nullptr;
423
424 // By convention, every indirect XVisual that can render to a window can
425 // also render to a GLXPixmap. Direct visuals we're not as sure about.
426 _context_has_pixmap = !glXIsDirect(_display, _context);
427
428 // Pbuffers aren't supported at all with the XVisual interface.
429 _context_has_pbuffer = false;
430}
431
432/**
433 * Returns true if the runtime GLX version number is at least the indicated
434 * value, false otherwise.
435 */
437glx_is_at_least_version(int major_version, int minor_version) const {
438 if (_glx_version_major < major_version) {
439 return false;
440 }
441 if (_glx_version_major > major_version) {
442 return true;
443 }
444 if (_glx_version_minor < minor_version) {
445 return false;
446 }
447 return true;
448}
449
450/**
451 * Calls glFlush().
452 */
453void glxGraphicsStateGuardian::
454gl_flush() const {
455 // This call requires synchronization with X.
456 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
457 PosixGraphicsStateGuardian::gl_flush();
458}
459
460/**
461 * Returns the result of glGetError().
462 */
463GLenum glxGraphicsStateGuardian::
464gl_get_error() const {
465 // This call requires synchronization with X.
466 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
467 return PosixGraphicsStateGuardian::gl_get_error();
468}
469
470/**
471 * Queries the runtime version of OpenGL in use.
472 */
473void glxGraphicsStateGuardian::
474query_gl_version() {
475 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
476 PosixGraphicsStateGuardian::query_gl_version();
477
478 show_glx_client_string("GLX_VENDOR", GLX_VENDOR);
479 show_glx_client_string("GLX_VERSION", GLX_VERSION);
480 show_glx_server_string("GLX_VENDOR", GLX_VENDOR);
481 show_glx_server_string("GLX_VERSION", GLX_VERSION);
482
483 glXQueryVersion(_display, &_glx_version_major, &_glx_version_minor);
484
485 // We output to glgsg_cat instead of glxdisplay_cat, since this is where the
486 // GL version has been output, and it's nice to see the two of these
487 // together.
488 if (glgsg_cat.is_debug()) {
489 glgsg_cat.debug()
490 << "GLX_VERSION = " << _glx_version_major << "." << _glx_version_minor
491 << "\n";
492 }
493}
494
495/**
496 * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
497 * further extensions strings may be appropriate to that interface, in
498 * addition to the GL extension strings return by glGetString().
499 */
500void glxGraphicsStateGuardian::
501get_extra_extensions() {
502 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
503 save_extensions(glXQueryExtensionsString(_display, _screen));
504}
505
506/**
507 * Returns the pointer to the GL extension function with the indicated name.
508 * It is the responsibility of the caller to ensure that the required
509 * extension is defined in the OpenGL runtime prior to calling this; it is an
510 * error to call this for a function that is not defined.
511 */
512void *glxGraphicsStateGuardian::
513do_get_extension_func(const char *name) {
514 nassertr(name != nullptr, nullptr);
515
516 if (glx_get_proc_address) {
517 LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
518
519 // First, check if we have glXGetProcAddress available. This will be
520 // superior if we can get it.
521
522#if defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESS)
523 // If we are confident the system headers defined it, we can call it
524 // directly. This is more reliable than trying to determine its address
525 // dynamically, but it may make libpandagl.so fail to load if the symbol
526 // isn't in the runtime library.
527 return (void *)glXGetProcAddress((const GLubyte *)name);
528
529#elif defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESSARB)
530 // The ARB extension version is OK too. Sometimes the prototype isn't
531 // supplied for some reason.
532 return (void *)glXGetProcAddressARB((const GLubyte *)name);
533
534#else
535 // Otherwise, we have to fiddle around with the dynamic runtime.
536
537 if (!_checked_get_proc_address) {
538 const char *funcName = nullptr;
539
540 if (glx_is_at_least_version(1, 4)) {
541 funcName = "glXGetProcAddress";
542
543 } else if (has_extension("GLX_ARB_get_proc_address")) {
544 funcName = "glXGetProcAddressARB";
545 }
546
547 if (funcName != nullptr) {
548 _glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)get_system_func(funcName);
549 if (_glXGetProcAddress == nullptr) {
550 glxdisplay_cat.warning()
551 << "Couldn't load function " << funcName
552 << ", GL extensions may be unavailable.\n";
553 }
554 }
555
556 _checked_get_proc_address = true;
557 }
558
559 // Use glxGetProcAddress() if we've got it; it should be more robust.
560 if (_glXGetProcAddress != nullptr) {
561 return (void *)_glXGetProcAddress((const GLubyte *)name);
562 }
563#endif // HAVE_GLXGETPROCADDRESS
564 }
565
566 // Otherwise, fall back to the OS-provided calls.
567 return PosixGraphicsStateGuardian::do_get_extension_func(name);
568}
569
570/**
571 * Queries the GLX extension pointers.
572 */
573void glxGraphicsStateGuardian::
574query_glx_extensions() {
575 _supports_swap_control = has_extension("GLX_SGI_swap_control");
576
577 if (_supports_swap_control) {
578 _glXSwapIntervalSGI =
579 (PFNGLXSWAPINTERVALSGIPROC)get_extension_func("glXSwapIntervalSGI");
580 if (_glXSwapIntervalSGI == nullptr) {
581 glxdisplay_cat.error()
582 << "Driver claims to support GLX_SGI_swap_control extension, but does not define all functions.\n";
583 _supports_swap_control = false;
584 }
585 }
586
587 if (_supports_swap_control) {
588 // Set the video-sync setting up front, if we have the extension that
589 // supports it.
590 _glXSwapIntervalSGI(sync_video ? 1 : 0);
591 }
592
593 if (glx_support_fbconfig) {
594 if (glx_is_at_least_version(1, 3)) {
595 // If we have glx 1.3 or better, we have the FBConfig interface.
596 _supports_fbconfig = true;
597
598 _glXChooseFBConfig =
599 (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfig");
600 _glXCreateNewContext =
601 (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateNewContext");
602 _glXGetVisualFromFBConfig =
603 (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfig");
604 _glXGetFBConfigAttrib =
605 (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttrib");
606 _glXCreatePixmap =
607 (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreatePixmap");
608
609 if (_glXChooseFBConfig == nullptr ||
610 _glXCreateNewContext == nullptr ||
611 _glXGetVisualFromFBConfig == nullptr ||
612 _glXGetFBConfigAttrib == nullptr ||
613 _glXCreatePixmap == nullptr) {
614 glxdisplay_cat.error()
615 << "Driver claims to support GLX_fbconfig extension, but does not define all functions.\n";
616 _supports_fbconfig = false;
617 }
618 } else if (has_extension("GLX_SGIX_fbconfig")) {
619 // Or maybe we have the old SGIX extension for FBConfig. This is the
620 // same, but the function names are different--we just remap them to the
621 // same function pointers.
622 _supports_fbconfig = true;
623
624 _glXChooseFBConfig =
625 (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfigSGIX");
626 _glXCreateNewContext =
627 (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateContextWithConfigSGIX");
628 _glXGetVisualFromFBConfig =
629 (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfigSGIX");
630 _glXGetFBConfigAttrib =
631 (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttribSGIX");
632 _glXCreatePixmap =
633 (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreateGLXPixmapWithConfigSGIX");
634
635 if (_glXChooseFBConfig == nullptr ||
636 _glXCreateNewContext == nullptr ||
637 _glXGetVisualFromFBConfig == nullptr ||
638 _glXGetFBConfigAttrib == nullptr ||
639 _glXCreatePixmap == nullptr) {
640 glxdisplay_cat.error()
641 << "Driver claims to support GLX_SGIX_fbconfig extension, but does not define all functions.\n";
642 _supports_fbconfig = false;
643 }
644 }
645
646 if (glx_is_at_least_version(1, 3)) {
647 // If we have glx 1.3 or better, we have the PBuffer interface.
648 _supports_pbuffer = true;
649 _uses_sgix_pbuffer = false;
650
651 _glXCreatePbuffer =
652 (PFNGLXCREATEPBUFFERPROC)get_extension_func("glXCreatePbuffer");
653 _glXCreateGLXPbufferSGIX = nullptr;
654 _glXDestroyPbuffer =
655 (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyPbuffer");
656 if (_glXCreatePbuffer == nullptr ||
657 _glXDestroyPbuffer == nullptr) {
658 glxdisplay_cat.error()
659 << "Driver claims to support GLX_pbuffer extension, but does not define all functions.\n";
660 _supports_pbuffer = false;
661 }
662
663 } else if (has_extension("GLX_SGIX_pbuffer")) {
664 // Or maybe we have the old SGIX extension for PBuffers.
665 _uses_sgix_pbuffer = true;
666
667 // CreatePbuffer has a different form between SGIX and 1.3, however, so
668 // we must treat it specially. But we can use the same function pointer
669 // for DestroyPbuffer.
670 _glXCreatePbuffer = nullptr;
671 _glXCreateGLXPbufferSGIX =
672 (PFNGLXCREATEGLXPBUFFERSGIXPROC)get_extension_func("glXCreateGLXPbufferSGIX");
673 _glXDestroyPbuffer =
674 (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyGLXPbufferSGIX");
675 if (_glXCreateGLXPbufferSGIX == nullptr ||
676 _glXDestroyPbuffer == nullptr) {
677 glxdisplay_cat.error()
678 << "Driver claims to support GLX_SGIX_pbuffer extension, but does not define all functions.\n";
679 _supports_pbuffer = false;
680 }
681 }
682
683 if (has_extension("GLX_ARB_create_context")) {
684 _glXCreateContextAttribs =
685 (PFNGLXCREATECONTEXTATTRIBSARBPROC)get_extension_func("glXCreateContextAttribsARB");
686 } else {
687 _glXCreateContextAttribs = nullptr;
688 }
689 }
690
691 if (glxdisplay_cat.is_debug()) {
692 glxdisplay_cat.debug()
693 << "supports_swap_control = " << _supports_swap_control << "\n";
694 glxdisplay_cat.debug()
695 << "supports_fbconfig = " << _supports_fbconfig << "\n";
696 glxdisplay_cat.debug()
697 << "supports_pbuffer = " << _supports_pbuffer
698 << " sgix = " << _uses_sgix_pbuffer << "\n";
699 }
700
701 // If "Mesa" is present, assume software. However, if "Mesa DRI" is found,
702 // it's actually a Mesa-based OpenGL layer running over a hardware driver.
703 if (_gl_renderer.find("Mesa") != string::npos &&
704 _gl_renderer.find("Mesa DRI") == string::npos) {
705 // It's Mesa, therefore probably a software context.
706 _fbprops.set_force_software(1);
707 _fbprops.set_force_hardware(0);
708 } else {
709 _fbprops.set_force_hardware(1);
710 _fbprops.set_force_software(0);
711 }
712}
713
714/**
715 * Outputs the result of glxGetClientString() on the indicated tag.
716 */
717void glxGraphicsStateGuardian::
718show_glx_client_string(const string &name, int id) {
719 if (glgsg_cat.is_debug()) {
720 const char *text = glXGetClientString(_display, id);
721 if (text == nullptr) {
722 glgsg_cat.debug()
723 << "Unable to query " << name << " (client)\n";
724 } else {
725 glgsg_cat.debug()
726 << name << " (client) = " << (const char *)text << "\n";
727 }
728 }
729}
730
731/**
732 * Outputs the result of glxQueryServerString() on the indicated tag.
733 */
734void glxGraphicsStateGuardian::
735show_glx_server_string(const string &name, int id) {
736 if (glgsg_cat.is_debug()) {
737 const char *text = glXQueryServerString(_display, _screen, id);
738 if (text == nullptr) {
739 glgsg_cat.debug()
740 << "Unable to query " << name << " (server)\n";
741 } else {
742 glgsg_cat.debug()
743 << name << " (server) = " << (const char *)text << "\n";
744 }
745 }
746}
747
748/**
749 * Selects an XVisual for an initial OpenGL context. This may be called
750 * initially, to create the first context needed in order to create the
751 * fbconfig. On successful return, _visual and _temp_context will be filled
752 * in with a non-NULL value.
753 */
754void glxGraphicsStateGuardian::
755choose_temp_visual(const FrameBufferProperties &properties) {
756 nassertv(_temp_context == (GLXContext)nullptr);
757
758 int best_quality = 0;
759 int best_result = 0;
760 FrameBufferProperties best_props;
761
762 // Scan available visuals.
763 if (_visuals != nullptr) {
764 XFree(_visuals);
765 _visuals = nullptr;
766 }
767 int nvisuals = 0;
768 _visuals = XGetVisualInfo(_display, 0, nullptr, &nvisuals);
769 if (_visuals != nullptr) {
770 for (int i = 0; i < nvisuals; i++) {
771 FrameBufferProperties fbprops;
772 get_properties(fbprops, _visuals + i);
773 int quality = fbprops.get_quality(properties);
774 if (quality > best_quality) {
775 best_quality = quality;
776 best_result = i;
777 best_props = fbprops;
778 }
779 }
780 }
781
782 if (best_quality > 0) {
783 _visual = _visuals + best_result;
784 _temp_context = glXCreateContext(_display, _visual, None, GL_TRUE);
785 if (_temp_context) {
786 _fbprops = best_props;
787 return;
788 }
789 }
790
791 glxdisplay_cat.error()
792 << "Could not find a usable pixel format.\n";
793}
794
795/**
796 * Initializes the context created in choose_temp_visual() by creating a
797 * temporary window and binding the context to that window.
798 */
799void glxGraphicsStateGuardian::
800init_temp_context() {
801 x11GraphicsPipe *x11_pipe;
802 DCAST_INTO_V(x11_pipe, get_pipe());
803 X11_Window root_window = x11_pipe->get_root();
804
805 // Assume everyone uses TrueColor or DirectColor these days.
806 Visual *visual = _visual->visual;
807 nassertv(visual->c_class == DirectColor || visual->c_class == TrueColor);
808 _temp_colormap = XCreateColormap(_display, root_window,
809 visual, AllocNone);
810 XSetWindowAttributes wa;
811 wa.colormap = _temp_colormap;
812 unsigned long attrib_mask = CWColormap;
813
814 _temp_xwindow = XCreateWindow
815 (_display, root_window, 0, 0, 100, 100,
816 0, _visual->depth, InputOutput,
817 visual, attrib_mask, &wa);
818 if (_temp_xwindow == (X11_Window)nullptr) {
819 glxdisplay_cat.error()
820 << "Could not create temporary window for context\n";
821 return;
822 }
823
824 // Now use it to query the available GLX features.
825 glXMakeCurrent(_display, _temp_xwindow, _temp_context);
826 query_gl_version();
827 get_extra_extensions();
828 query_glx_extensions();
829}
830
831/**
832 * Destroys the temporary unmapped window created by init_temp_context().
833 */
834void glxGraphicsStateGuardian::
835destroy_temp_xwindow() {
836 glXMakeCurrent(_display, None, nullptr);
837
838 if (_temp_colormap != (Colormap)nullptr) {
839 XFreeColormap(_display, _temp_colormap);
840 _temp_colormap = (Colormap)nullptr;
841 }
842 if (_temp_xwindow != (X11_Window)nullptr) {
843 XDestroyWindow(_display, _temp_xwindow);
844 _temp_xwindow = (X11_Window)nullptr;
845 }
846
847 if (_temp_context != (GLXContext)nullptr) {
848 glXDestroyContext(_display, _temp_context);
849 _temp_context = (GLXContext)nullptr;
850 }
851}
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
void set_rgba_bits(int r, int g, int b, int a)
Convenience method for setting the red, green, blue and alpha bits in one go.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.
This class is the main interface to controlling the render process.
An object to create GraphicsOutputs that share a particular 3-D API.
Similar to MutexHolder, but for a light reentrant mutex.
This GSG is used only for CallbackGraphicsWindow (which might not be using the glx interfaces),...
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
A tiny specialization on GLGraphicsStateGuardian to add some glx-specific information.
void get_properties(FrameBufferProperties &properties, XVisualInfo *visual)
Gets the FrameBufferProperties to match the indicated visual.
void get_properties_advanced(FrameBufferProperties &properties, bool &context_has_pbuffer, bool &pixmap_supported, bool &slow, GLXFBConfig config)
Gets the FrameBufferProperties to match the indicated GLXFBConfig.
bool glx_is_at_least_version(int major_version, int minor_version) const
Returns true if the runtime GLX version number is at least the indicated value, false otherwise.
void choose_pixel_format(const FrameBufferProperties &properties, X11_Display *_display, int _screen, bool need_pbuffer, bool need_pixmap)
Selects a visual or fbconfig for all the windows and buffers that use this gsg.
This graphics pipe represents the interface for creating graphics windows on an X-based client.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.