mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
869 lines
31 KiB
C++
869 lines
31 KiB
C++
// Filename: glxGraphicsStateGuardian.cxx
|
|
// Created by: drose (27Jan03)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "glxGraphicsStateGuardian.h"
|
|
#include "config_glxdisplay.h"
|
|
#include "config_glgsg.h"
|
|
#include "lightReMutexHolder.h"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
TypeHandle glxGraphicsStateGuardian::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
glxGraphicsStateGuardian::
|
|
glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
|
|
glxGraphicsStateGuardian *share_with) :
|
|
GLGraphicsStateGuardian(engine, pipe)
|
|
{
|
|
_share_context=0;
|
|
_context=0;
|
|
_display=0;
|
|
_screen=0;
|
|
_visual=0;
|
|
_visuals=0;
|
|
_fbconfig=0;
|
|
_context_has_pbuffer = false;
|
|
_context_has_pixmap = false;
|
|
_slow = false;
|
|
|
|
_supports_swap_control = false;
|
|
_supports_fbconfig = false;
|
|
_supports_pbuffer = false;
|
|
_uses_sgix_pbuffer = false;
|
|
|
|
if (share_with != (glxGraphicsStateGuardian *)NULL) {
|
|
_prepared_objects = share_with->get_prepared_objects();
|
|
_share_context = share_with->_context;
|
|
}
|
|
|
|
_libgl_handle = NULL;
|
|
_checked_get_proc_address = false;
|
|
_glXGetProcAddress = NULL;
|
|
_temp_context = (GLXContext)NULL;
|
|
_temp_xwindow = (Window)NULL;
|
|
_temp_colormap = (Colormap)NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::Destructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
glxGraphicsStateGuardian::
|
|
~glxGraphicsStateGuardian() {
|
|
destroy_temp_xwindow();
|
|
if (_visuals != (XVisualInfo *)NULL) {
|
|
XFree(_visuals);
|
|
}
|
|
if (_context != (GLXContext)NULL) {
|
|
glXDestroyContext(_display, _context);
|
|
_context = (GLXContext)NULL;
|
|
}
|
|
if (_libgl_handle != (void *)NULL) {
|
|
dlclose(_libgl_handle);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::get_properties
|
|
// Access: Public
|
|
// Description: Gets the FrameBufferProperties to match the
|
|
// indicated visual.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
get_properties(FrameBufferProperties &properties, XVisualInfo *visual) {
|
|
|
|
int use_gl, render_mode, double_buffer, stereo,
|
|
red_size, green_size, blue_size,
|
|
alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
|
|
depth_size, stencil_size;
|
|
|
|
glXGetConfig(_display, visual, GLX_USE_GL, &use_gl);
|
|
glXGetConfig(_display, visual, GLX_RGBA, &render_mode);
|
|
glXGetConfig(_display, visual, GLX_DOUBLEBUFFER, &double_buffer);
|
|
glXGetConfig(_display, visual, GLX_STEREO, &stereo);
|
|
glXGetConfig(_display, visual, GLX_RED_SIZE, &red_size);
|
|
glXGetConfig(_display, visual, GLX_GREEN_SIZE, &green_size);
|
|
glXGetConfig(_display, visual, GLX_BLUE_SIZE, &blue_size);
|
|
glXGetConfig(_display, visual, GLX_ALPHA_SIZE, &alpha_size);
|
|
glXGetConfig(_display, visual, GLX_ACCUM_RED_SIZE, &ared_size);
|
|
glXGetConfig(_display, visual, GLX_ACCUM_GREEN_SIZE, &agreen_size);
|
|
glXGetConfig(_display, visual, GLX_ACCUM_BLUE_SIZE, &ablue_size);
|
|
glXGetConfig(_display, visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
|
|
glXGetConfig(_display, visual, GLX_DEPTH_SIZE, &depth_size);
|
|
glXGetConfig(_display, visual, GLX_STENCIL_SIZE, &stencil_size);
|
|
|
|
properties.clear();
|
|
|
|
if (use_gl == 0) {
|
|
// If we return a set of properties without setting either
|
|
// rgb_color or indexed_color, then this indicates a visual
|
|
// that's no good for any kind of rendering.
|
|
return;
|
|
}
|
|
|
|
if (double_buffer) {
|
|
properties.set_back_buffers(1);
|
|
}
|
|
if (stereo) {
|
|
properties.set_stereo(1);
|
|
}
|
|
if (render_mode) {
|
|
properties.set_rgb_color(1);
|
|
} else {
|
|
properties.set_indexed_color(1);
|
|
}
|
|
properties.set_color_bits(red_size+green_size+blue_size);
|
|
properties.set_stencil_bits(stencil_size);
|
|
properties.set_depth_bits(depth_size);
|
|
properties.set_alpha_bits(alpha_size);
|
|
properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
|
|
|
|
// Set both hardware and software bits, indicating not-yet-known.
|
|
properties.set_force_software(1);
|
|
properties.set_force_hardware(1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::get_properties_advanced
|
|
// Access: Public
|
|
// Description: Gets the FrameBufferProperties to match the
|
|
// indicated GLXFBConfig
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
get_properties_advanced(FrameBufferProperties &properties,
|
|
bool &context_has_pbuffer, bool &context_has_pixmap,
|
|
bool &slow, GLXFBConfig config) {
|
|
properties.clear();
|
|
|
|
if (_supports_fbconfig) {
|
|
// Now update our framebuffer_mode and bit depth appropriately.
|
|
int render_type, double_buffer, stereo, red_size, green_size, blue_size,
|
|
alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
|
|
depth_size, stencil_size, samples, drawable_type, caveat;
|
|
|
|
_glXGetFBConfigAttrib(_display, config, GLX_RENDER_TYPE, &render_type);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_DOUBLEBUFFER, &double_buffer);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_STEREO, &stereo);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_RED_SIZE, &red_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_GREEN_SIZE, &green_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_BLUE_SIZE, &blue_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_ALPHA_SIZE, &alpha_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_ACCUM_RED_SIZE, &ared_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_ACCUM_GREEN_SIZE, &agreen_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_ACCUM_BLUE_SIZE, &ablue_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_DEPTH_SIZE, &depth_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_STENCIL_SIZE, &stencil_size);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_SAMPLES, &samples);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_DRAWABLE_TYPE, &drawable_type);
|
|
_glXGetFBConfigAttrib(_display, config, GLX_CONFIG_CAVEAT, &caveat);
|
|
|
|
context_has_pbuffer = false;
|
|
if ((drawable_type & GLX_PBUFFER_BIT)!=0) {
|
|
context_has_pbuffer = true;
|
|
}
|
|
|
|
context_has_pixmap = false;
|
|
if ((drawable_type & GLX_PIXMAP_BIT)!=0) {
|
|
context_has_pixmap = true;
|
|
}
|
|
|
|
slow = false;
|
|
if (caveat == GLX_SLOW_CONFIG) {
|
|
slow = true;
|
|
}
|
|
|
|
if ((drawable_type & GLX_WINDOW_BIT)==0) {
|
|
// We insist on having a context that will support an onscreen window.
|
|
return;
|
|
}
|
|
|
|
if (double_buffer) {
|
|
properties.set_back_buffers(1);
|
|
}
|
|
if (stereo) {
|
|
properties.set_stereo(1);
|
|
}
|
|
|
|
if ((render_type & GLX_RGBA_BIT)!=0) {
|
|
properties.set_rgb_color(1);
|
|
}
|
|
if ((render_type & GLX_COLOR_INDEX_BIT)!=0) {
|
|
properties.set_indexed_color(1);
|
|
}
|
|
properties.set_color_bits(red_size+green_size+blue_size);
|
|
properties.set_stencil_bits(stencil_size);
|
|
properties.set_depth_bits(depth_size);
|
|
properties.set_alpha_bits(alpha_size);
|
|
properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
|
|
properties.set_multisamples(samples);
|
|
|
|
// Set both hardware and software bits, indicating not-yet-known.
|
|
properties.set_force_software(1);
|
|
properties.set_force_hardware(1);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::choose_pixel_format
|
|
// Access: Public
|
|
// Description: Selects a visual or fbconfig for all the windows
|
|
// and buffers that use this gsg. Also creates the GL
|
|
// context and obtains the visual.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
choose_pixel_format(const FrameBufferProperties &properties,
|
|
Display *display,
|
|
int screen, bool need_pbuffer, bool need_pixmap) {
|
|
|
|
_display = display;
|
|
_screen = screen;
|
|
_context = 0;
|
|
_fbconfig = 0;
|
|
_visual = 0;
|
|
if (_visuals != (XVisualInfo *)NULL) {
|
|
XFree(_visuals);
|
|
_visuals = NULL;
|
|
}
|
|
|
|
_fbprops.clear();
|
|
|
|
// First, attempt to create a context using the XVisual interface.
|
|
// We need this before we can query the FBConfig interface, because
|
|
// we need an OpenGL context to get the required extension function
|
|
// pointers.
|
|
destroy_temp_xwindow();
|
|
choose_temp_visual(properties);
|
|
if (_temp_context == NULL) {
|
|
// No good.
|
|
return;
|
|
}
|
|
|
|
// Now we have to initialize the context so we can query its
|
|
// capabilities and extensions. This also means creating a
|
|
// temporary window, so we have something to bind the context to and
|
|
// make it current.
|
|
init_temp_context();
|
|
|
|
if (!_supports_fbconfig) {
|
|
// We have a good OpenGL context, but it doesn't support the
|
|
// FBConfig interface, so we'll stop there.
|
|
if (glxdisplay_cat.is_debug()) {
|
|
glxdisplay_cat.debug()
|
|
<<" No FBConfig supported; using XVisual only.\n";
|
|
|
|
glxdisplay_cat.debug()
|
|
<< _fbprops << "\n";
|
|
|
|
_context = _temp_context;
|
|
_temp_context = (GLXContext)NULL;
|
|
|
|
// By convention, every indirect XVisual that can render to a
|
|
// window can also render to a GLXPixmap. Direct visuals we're
|
|
// not as sure about.
|
|
_context_has_pixmap = !glXIsDirect(_display, _context);
|
|
|
|
// Pbuffers aren't supported at all with the XVisual interface.
|
|
_context_has_pbuffer = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// The OpenGL context supports the FBConfig interface, so we can use
|
|
// that more advanced interface to choose the actual window format
|
|
// we'll use. FBConfig provides for more options than the older
|
|
// XVisual interface, so we'd much rather use it if it's available.
|
|
|
|
int best_quality = 0;
|
|
int best_result = 0;
|
|
FrameBufferProperties best_props;
|
|
|
|
static const int max_attrib_list = 32;
|
|
int attrib_list[max_attrib_list];
|
|
int n = 0;
|
|
attrib_list[n++] = GLX_STEREO;
|
|
attrib_list[n++] = GLX_DONT_CARE;
|
|
attrib_list[n++] = GLX_RENDER_TYPE;
|
|
attrib_list[n++] = GLX_DONT_CARE;
|
|
attrib_list[n++] = GLX_DRAWABLE_TYPE;
|
|
attrib_list[n++] = GLX_DONT_CARE;
|
|
attrib_list[n] = (int)None;
|
|
|
|
int num_configs = 0;
|
|
GLXFBConfig *configs =
|
|
_glXChooseFBConfig(_display, _screen, attrib_list, &num_configs);
|
|
|
|
if (configs != 0) {
|
|
bool context_has_pbuffer, context_has_pixmap, slow;
|
|
int quality, i;
|
|
for (i = 0; i < num_configs; ++i) {
|
|
FrameBufferProperties fbprops;
|
|
get_properties_advanced(fbprops, context_has_pbuffer, context_has_pixmap,
|
|
slow, configs[i]);
|
|
quality = fbprops.get_quality(properties);
|
|
if ((quality > 0)&&(slow)) quality -= 10000000;
|
|
if (glxdisplay_cat.is_debug()) {
|
|
const char *pbuffertext = context_has_pbuffer ? " (pbuffer)" : "";
|
|
const char *pixmaptext = context_has_pixmap ? " (pixmap)" : "";
|
|
const char *slowtext = slow ? " (slow)" : "";
|
|
glxdisplay_cat.debug()
|
|
<< i << ": " << fbprops << " quality=" << quality << pbuffertext << pixmaptext << slowtext << "\n";
|
|
}
|
|
if (need_pbuffer && !context_has_pbuffer) {
|
|
continue;
|
|
}
|
|
if (need_pixmap && !context_has_pixmap) {
|
|
continue;
|
|
}
|
|
|
|
if (quality > best_quality) {
|
|
best_quality = quality;
|
|
best_result = i;
|
|
best_props = fbprops;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_quality > 0) {
|
|
_fbconfig = configs[best_result];
|
|
_context =
|
|
_glXCreateNewContext(_display, _fbconfig, GLX_RGBA_TYPE, _share_context,
|
|
GL_TRUE);
|
|
if (_context) {
|
|
if (_visuals != (XVisualInfo *)NULL) {
|
|
XFree(_visuals);
|
|
_visuals = NULL;
|
|
}
|
|
_visuals = _glXGetVisualFromFBConfig(_display, _fbconfig);
|
|
_visual = _visuals;
|
|
|
|
if (_visual) {
|
|
get_properties_advanced(_fbprops, _context_has_pbuffer, _context_has_pixmap,
|
|
_slow, _fbconfig);
|
|
|
|
if (glxdisplay_cat.is_debug()) {
|
|
glxdisplay_cat.debug()
|
|
<< "Selected context " << best_result << ": " << _fbprops << "\n";
|
|
glxdisplay_cat.debug()
|
|
<< "context_has_pbuffer = " << _context_has_pbuffer
|
|
<< ", context_has_pixmap = " << _context_has_pixmap << "\n";
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
// This really shouldn't happen, so I'm not too careful about cleanup.
|
|
glxdisplay_cat.error()
|
|
<< "Could not create FBConfig context!\n";
|
|
_fbconfig = 0;
|
|
_context = 0;
|
|
_visual = 0;
|
|
_visuals = 0;
|
|
}
|
|
|
|
glxdisplay_cat.info()
|
|
<< "No suitable FBConfig contexts available.\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::reset
|
|
// Access: Public, Virtual
|
|
// Description: Resets all internal state as if the gsg were newly
|
|
// created.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
reset() {
|
|
GLGraphicsStateGuardian::reset();
|
|
|
|
_supports_swap_control = has_extension("GLX_SGI_swap_control");
|
|
|
|
if (_supports_swap_control) {
|
|
_glXSwapIntervalSGI =
|
|
(PFNGLXSWAPINTERVALSGIPROC)get_extension_func("glX", "SwapIntervalSGI");
|
|
if (_glXSwapIntervalSGI == NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Driver claims to support GLX_SGI_swap_control extension, but does not define all functions.\n";
|
|
_supports_swap_control = false;
|
|
}
|
|
}
|
|
|
|
if (_supports_swap_control) {
|
|
// Set the video-sync setting up front, if we have the extension
|
|
// that supports it.
|
|
_glXSwapIntervalSGI(sync_video ? 1 : 0);
|
|
}
|
|
|
|
if (glx_support_fbconfig) {
|
|
if (glx_is_at_least_version(1, 3)) {
|
|
// If we have glx 1.3 or better, we have the FBConfig interface.
|
|
_supports_fbconfig = true;
|
|
|
|
_glXChooseFBConfig =
|
|
(PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glX", "ChooseFBConfig");
|
|
_glXCreateNewContext =
|
|
(PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glX", "CreateNewContext");
|
|
_glXGetVisualFromFBConfig =
|
|
(PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glX", "GetVisualFromFBConfig");
|
|
_glXGetFBConfigAttrib =
|
|
(PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glX", "GetFBConfigAttrib");
|
|
_glXCreatePixmap =
|
|
(PFNGLXCREATEPIXMAPPROC)get_extension_func("glX", "CreatePixmap");
|
|
|
|
if (_glXChooseFBConfig == NULL ||
|
|
_glXCreateNewContext == NULL ||
|
|
_glXGetVisualFromFBConfig == NULL ||
|
|
_glXGetFBConfigAttrib == NULL ||
|
|
_glXCreatePixmap == NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Driver claims to support GLX_fbconfig extension, but does not define all functions.\n";
|
|
_supports_fbconfig = false;
|
|
}
|
|
} else if (has_extension("GLX_SGIX_fbconfig")) {
|
|
// Or maybe we have the old SGIX extension for FBConfig. This is
|
|
// the same, but the function names are different--we just remap
|
|
// them to the same function pointers.
|
|
_supports_fbconfig = true;
|
|
|
|
_glXChooseFBConfig =
|
|
(PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glX", "ChooseFBConfigSGIX");
|
|
_glXCreateNewContext =
|
|
(PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glX", "CreateContextWithConfigSGIX");
|
|
_glXGetVisualFromFBConfig =
|
|
(PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glX", "GetVisualFromFBConfigSGIX");
|
|
_glXGetFBConfigAttrib =
|
|
(PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glX", "GetFBConfigAttribSGIX");
|
|
_glXCreatePixmap =
|
|
(PFNGLXCREATEPIXMAPPROC)get_extension_func("glX", "CreateGLXPixmapWithConfigSGIX");
|
|
|
|
if (_glXChooseFBConfig == NULL ||
|
|
_glXCreateNewContext == NULL ||
|
|
_glXGetVisualFromFBConfig == NULL ||
|
|
_glXGetFBConfigAttrib == NULL ||
|
|
_glXCreatePixmap == NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Driver claims to support GLX_SGIX_fbconfig extension, but does not define all functions.\n";
|
|
_supports_fbconfig = false;
|
|
}
|
|
}
|
|
|
|
if (glx_is_at_least_version(1, 3)) {
|
|
// If we have glx 1.3 or better, we have the PBuffer interface.
|
|
_supports_pbuffer = true;
|
|
_uses_sgix_pbuffer = false;
|
|
|
|
_glXCreatePbuffer =
|
|
(PFNGLXCREATEPBUFFERPROC)get_extension_func("glX", "CreatePbuffer");
|
|
_glXCreateGLXPbufferSGIX = NULL;
|
|
_glXDestroyPbuffer =
|
|
(PFNGLXDESTROYPBUFFERPROC)get_extension_func("glX", "DestroyPbuffer");
|
|
if (_glXCreatePbuffer == NULL ||
|
|
_glXDestroyPbuffer == NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Driver claims to support GLX_pbuffer extension, but does not define all functions.\n";
|
|
_supports_pbuffer = false;
|
|
}
|
|
|
|
} else if (has_extension("GLX_SGIX_pbuffer")) {
|
|
// Or maybe we have the old SGIX extension for PBuffers.
|
|
_uses_sgix_pbuffer = true;
|
|
|
|
// CreatePbuffer has a different form between SGIX and 1.3,
|
|
// however, so we must treat it specially. But we can use the
|
|
// same function pointer for DestroyPbuffer.
|
|
_glXCreatePbuffer = NULL;
|
|
_glXCreateGLXPbufferSGIX =
|
|
(PFNGLXCREATEGLXPBUFFERSGIXPROC)get_extension_func("glX", "CreateGLXPbufferSGIX");
|
|
_glXDestroyPbuffer =
|
|
(PFNGLXDESTROYPBUFFERPROC)get_extension_func("glX", "DestroyGLXPbufferSGIX");
|
|
if (_glXCreateGLXPbufferSGIX == NULL ||
|
|
_glXDestroyPbuffer == NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Driver claims to support GLX_SGIX_pbuffer extension, but does not define all functions.\n";
|
|
_supports_pbuffer = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (glxdisplay_cat.is_debug()) {
|
|
glxdisplay_cat.debug()
|
|
<< "supports_swap_control = " << _supports_swap_control << "\n";
|
|
glxdisplay_cat.debug()
|
|
<< "supports_fbconfig = " << _supports_fbconfig << "\n";
|
|
glxdisplay_cat.debug()
|
|
<< "supports_pbuffer = " << _supports_pbuffer
|
|
<< " sgix = " << _uses_sgix_pbuffer << "\n";
|
|
}
|
|
|
|
// If "Mesa" is present, assume software. However, if "Mesa DRI" is
|
|
// found, it's actually a Mesa-based OpenGL layer running over a
|
|
// hardware driver.
|
|
if (_gl_renderer.find("Mesa") != string::npos &&
|
|
_gl_renderer.find("Mesa DRI") == string::npos) {
|
|
// It's Mesa, therefore probably a software context.
|
|
_fbprops.set_force_software(1);
|
|
_fbprops.set_force_hardware(0);
|
|
} else {
|
|
_fbprops.set_force_hardware(1);
|
|
_fbprops.set_force_software(0);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::glx_is_at_least_version
|
|
// Access: Public
|
|
// Description: Returns true if the runtime GLX version number is at
|
|
// least the indicated value, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool glxGraphicsStateGuardian::
|
|
glx_is_at_least_version(int major_version, int minor_version) const {
|
|
if (_glx_version_major < major_version) {
|
|
return false;
|
|
}
|
|
if (_glx_version_major > major_version) {
|
|
return true;
|
|
}
|
|
if (_glx_version_minor < minor_version) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::gl_flush
|
|
// Access: Protected, Virtual
|
|
// Description: Calls glFlush().
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
gl_flush() const {
|
|
// This call requires synchronization with X.
|
|
LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
|
|
GLGraphicsStateGuardian::gl_flush();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::gl_get_error
|
|
// Access: Protected, Virtual
|
|
// Description: Returns the result of glGetError().
|
|
////////////////////////////////////////////////////////////////////
|
|
GLenum glxGraphicsStateGuardian::
|
|
gl_get_error() const {
|
|
// This call requires synchronization with X.
|
|
LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
|
|
return GLGraphicsStateGuardian::gl_get_error();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::query_gl_version
|
|
// Access: Protected, Virtual
|
|
// Description: Queries the runtime version of OpenGL in use.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
query_gl_version() {
|
|
GLGraphicsStateGuardian::query_gl_version();
|
|
|
|
show_glx_client_string("GLX_VENDOR", GLX_VENDOR);
|
|
show_glx_client_string("GLX_VERSION", GLX_VERSION);
|
|
show_glx_server_string("GLX_VENDOR", GLX_VENDOR);
|
|
show_glx_server_string("GLX_VERSION", GLX_VERSION);
|
|
|
|
glXQueryVersion(_display, &_glx_version_major, &_glx_version_minor);
|
|
|
|
// We output to glgsg_cat instead of glxdisplay_cat, since this is
|
|
// where the GL version has been output, and it's nice to see the
|
|
// two of these together.
|
|
if (glgsg_cat.is_debug()) {
|
|
glgsg_cat.debug()
|
|
<< "GLX_VERSION = " << _glx_version_major << "." << _glx_version_minor
|
|
<< "\n";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::get_extra_extensions
|
|
// Access: Protected, Virtual
|
|
// Description: This may be redefined by a derived class (e.g. glx or
|
|
// wgl) to get whatever further extensions strings may
|
|
// be appropriate to that interface, in addition to the
|
|
// GL extension strings return by glGetString().
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
get_extra_extensions() {
|
|
save_extensions(glXQueryExtensionsString(_display, _screen));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::do_get_extension_func
|
|
// Access: Public, Virtual
|
|
// Description: Returns the pointer to the GL extension function with
|
|
// the indicated name. It is the responsibility of the
|
|
// caller to ensure that the required extension is
|
|
// defined in the OpenGL runtime prior to calling this;
|
|
// it is an error to call this for a function that is
|
|
// not defined.
|
|
////////////////////////////////////////////////////////////////////
|
|
void *glxGraphicsStateGuardian::
|
|
do_get_extension_func(const char *prefix, const char *name) {
|
|
nassertr(prefix != NULL, NULL);
|
|
nassertr(name != NULL, NULL);
|
|
string fullname = string(prefix) + string(name);
|
|
|
|
if (glx_get_proc_address) {
|
|
// First, check if we have glXGetProcAddress available. This will
|
|
// be superior if we can get it.
|
|
|
|
#if defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESS)
|
|
// If we are confident the system headers defined it, we can
|
|
// call it directly. This is more reliable than trying to
|
|
// determine its address dynamically, but it may make
|
|
// libpandagl.so fail to load if the symbol isn't in the runtime
|
|
// library.
|
|
return (void *)glXGetProcAddress((const GLubyte *)fullname.c_str());
|
|
|
|
#elif defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESSARB)
|
|
// The ARB extension version is OK too. Sometimes the prototype
|
|
// isn't supplied for some reason.
|
|
return (void *)glXGetProcAddressARB((const GLubyte *)fullname.c_str());
|
|
|
|
#else
|
|
// Otherwise, we have to fiddle around with the dynamic runtime.
|
|
|
|
if (!_checked_get_proc_address) {
|
|
const char *funcName = NULL;
|
|
|
|
if (glx_is_at_least_version(1, 4)) {
|
|
funcName = "glXGetProcAddress";
|
|
|
|
} else if (has_extension("GLX_ARB_get_proc_address")) {
|
|
funcName = "glXGetProcAddressARB";
|
|
}
|
|
|
|
if (funcName != NULL) {
|
|
_glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)get_system_func(funcName);
|
|
if (_glXGetProcAddress == NULL) {
|
|
glxdisplay_cat.warning()
|
|
<< "Couldn't load function " << funcName
|
|
<< ", GL extensions may be unavailable.\n";
|
|
}
|
|
}
|
|
|
|
_checked_get_proc_address = true;
|
|
}
|
|
|
|
// Use glxGetProcAddress() if we've got it; it should be more robust.
|
|
if (_glXGetProcAddress != NULL) {
|
|
return (void *)_glXGetProcAddress((const GLubyte *)fullname.c_str());
|
|
}
|
|
#endif // HAVE_GLXGETPROCADDRESS
|
|
}
|
|
|
|
if (glx_get_os_address) {
|
|
// Otherwise, fall back to the OS-provided calls.
|
|
return get_system_func(fullname.c_str());
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::get_system_func
|
|
// Access: Private
|
|
// Description: Support for get_extension_func(), above, that uses
|
|
// system calls to find a GL or GLX function (in the
|
|
// absence of a working glxGetProcAddress() function to
|
|
// call).
|
|
////////////////////////////////////////////////////////////////////
|
|
void *glxGraphicsStateGuardian::
|
|
get_system_func(const char *name) {
|
|
if (_libgl_handle == (void *)NULL) {
|
|
// We open the current executable, rather than naming a particular
|
|
// library. Presumably libGL.so (or whatever the library should
|
|
// be called) is already available in the current executable
|
|
// address space, so this is more portable than insisting on a
|
|
// particular shared library name.
|
|
_libgl_handle = dlopen(NULL, RTLD_LAZY);
|
|
nassertr(_libgl_handle != (void *)NULL, NULL);
|
|
|
|
// If that doesn't locate the symbol we expected, then fall back
|
|
// to loading the GL library by its usual name.
|
|
if (dlsym(_libgl_handle, name) == NULL) {
|
|
dlclose(_libgl_handle);
|
|
glxdisplay_cat.warning()
|
|
<< name << " not found in executable; looking in libGL.so instead.\n";
|
|
_libgl_handle = dlopen("libGL.so", RTLD_LAZY);
|
|
nassertr(_libgl_handle != (void *)NULL, NULL);
|
|
}
|
|
}
|
|
|
|
return dlsym(_libgl_handle, name);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::show_glx_client_string
|
|
// Access: Protected
|
|
// Description: Outputs the result of glxGetClientString() on the
|
|
// indicated tag.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
show_glx_client_string(const string &name, int id) {
|
|
if (glgsg_cat.is_debug()) {
|
|
const char *text = glXGetClientString(_display, id);
|
|
if (text == (const char *)NULL) {
|
|
glgsg_cat.debug()
|
|
<< "Unable to query " << name << " (client)\n";
|
|
} else {
|
|
glgsg_cat.debug()
|
|
<< name << " (client) = " << (const char *)text << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::show_glx_server_string
|
|
// Access: Protected
|
|
// Description: Outputs the result of glxQueryServerString() on the
|
|
// indicated tag.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
show_glx_server_string(const string &name, int id) {
|
|
if (glgsg_cat.is_debug()) {
|
|
const char *text = glXQueryServerString(_display, _screen, id);
|
|
if (text == (const char *)NULL) {
|
|
glgsg_cat.debug()
|
|
<< "Unable to query " << name << " (server)\n";
|
|
} else {
|
|
glgsg_cat.debug()
|
|
<< name << " (server) = " << (const char *)text << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::choose_temp_visual
|
|
// Access: Private
|
|
// Description: Selects an XVisual for an initial OpenGL context.
|
|
// This may be called initially, to create the first
|
|
// context needed in order to create the fbconfig. On
|
|
// successful return, _visual and _temp_context will be
|
|
// filled in with a non-NULL value.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
choose_temp_visual(const FrameBufferProperties &properties) {
|
|
nassertv(_temp_context == (GLXContext)NULL);
|
|
|
|
int best_quality = 0;
|
|
int best_result = 0;
|
|
FrameBufferProperties best_props;
|
|
|
|
// Scan available visuals.
|
|
if (_visuals != (XVisualInfo *)NULL) {
|
|
XFree(_visuals);
|
|
_visuals = NULL;
|
|
}
|
|
int nvisuals = 0;
|
|
_visuals = XGetVisualInfo(_display, 0, 0, &nvisuals);
|
|
if (_visuals != 0) {
|
|
for (int i = 0; i < nvisuals; i++) {
|
|
FrameBufferProperties fbprops;
|
|
get_properties(fbprops, _visuals + i);
|
|
int quality = fbprops.get_quality(properties);
|
|
if (quality > best_quality) {
|
|
best_quality = quality;
|
|
best_result = i;
|
|
best_props = fbprops;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_quality > 0) {
|
|
_visual = _visuals + best_result;
|
|
_temp_context = glXCreateContext(_display, _visual, None, GL_TRUE);
|
|
if (_temp_context) {
|
|
_fbprops = best_props;
|
|
return;
|
|
}
|
|
}
|
|
|
|
glxdisplay_cat.error()
|
|
<< "Could not find a usable pixel format.\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::init_temp_context
|
|
// Access: Private
|
|
// Description: Initializes the context created in
|
|
// choose_temp_visual() by creating a temporary window
|
|
// and binding the context to that window.
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
init_temp_context() {
|
|
x11GraphicsPipe *x11_pipe;
|
|
DCAST_INTO_V(x11_pipe, get_pipe());
|
|
Window root_window = x11_pipe->get_root();
|
|
|
|
// Assume everyone uses TrueColor or DirectColor these days.
|
|
Visual *visual = _visual->visual;
|
|
nassertv(visual->c_class == DirectColor || visual->c_class == TrueColor);
|
|
_temp_colormap = XCreateColormap(_display, root_window,
|
|
visual, AllocNone);
|
|
XSetWindowAttributes wa;
|
|
wa.colormap = _temp_colormap;
|
|
unsigned long attrib_mask = CWColormap;
|
|
|
|
_temp_xwindow = XCreateWindow
|
|
(_display, root_window, 0, 0, 100, 100,
|
|
0, _visual->depth, InputOutput,
|
|
visual, attrib_mask, &wa);
|
|
if (_temp_xwindow == (Window)NULL) {
|
|
glxdisplay_cat.error()
|
|
<< "Could not create temporary window for context\n";
|
|
return;
|
|
}
|
|
|
|
glXMakeCurrent(_display, _temp_xwindow, _temp_context);
|
|
reset();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: glxGraphicsStateGuardian::destroy_temp_xwindow
|
|
// Access: Private
|
|
// Description: Destroys the temporary unmapped window created by
|
|
// init_temp_context().
|
|
////////////////////////////////////////////////////////////////////
|
|
void glxGraphicsStateGuardian::
|
|
destroy_temp_xwindow() {
|
|
glXMakeCurrent(_display, None, NULL);
|
|
|
|
if (_temp_colormap != (Colormap)NULL) {
|
|
XFreeColormap(_display, _temp_colormap);
|
|
_temp_colormap = (Colormap)NULL;
|
|
}
|
|
if (_temp_xwindow != (Window)NULL) {
|
|
XDestroyWindow(_display, _temp_xwindow);
|
|
_temp_xwindow = (Window)NULL;
|
|
}
|
|
|
|
if (_temp_context != (GLXContext)NULL){
|
|
glXDestroyContext(_display, _temp_context);
|
|
_temp_context = (GLXContext)NULL;
|
|
}
|
|
}
|