Fixed GLX context creation and reorganized code to be more understandable.

This commit is contained in:
binary1248 2015-01-08 19:11:57 +01:00
parent d53338298a
commit ea2bf65186
2 changed files with 239 additions and 128 deletions

View File

@ -45,20 +45,30 @@ m_ownsWindow(true)
// Open a connection with the X server // Open a connection with the X server
m_display = OpenDisplay(); m_display = OpenDisplay();
m_connection = XGetXCBConnection(m_display); m_connection = XGetXCBConnection(m_display);
xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
// Choose the visual according to the context settings
XVisualInfo visualInfo = selectBestVisual(m_display, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings());
// Define the window attributes
xcb_colormap_t colormap = xcb_generate_id(m_connection);
xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visualInfo.visualid);
const uint32_t value_list[] = {colormap};
// Create a dummy window (disabled and hidden) // Create a dummy window (disabled and hidden)
xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
m_window = xcb_generate_id(m_connection); m_window = xcb_generate_id(m_connection);
xcb_create_window( xcb_create_window(
m_connection, m_connection,
screen->root_depth, static_cast<uint8_t>(visualInfo.depth),
m_window, screen->root, m_window,
screen->root,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, visualInfo.visualid,
0, NULL XCB_CW_COLORMAP,
value_list
); );
// Create the context // Create the context
@ -95,20 +105,30 @@ m_ownsWindow(true)
// Open a connection with the X server // Open a connection with the X server
m_display = OpenDisplay(); m_display = OpenDisplay();
m_connection = XGetXCBConnection(m_display); m_connection = XGetXCBConnection(m_display);
xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
// Choose the visual according to the context settings
XVisualInfo visualInfo = selectBestVisual(m_display, VideoMode::getDesktopMode().bitsPerPixel, settings);
// Define the window attributes
xcb_colormap_t colormap = xcb_generate_id(m_connection);
xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visualInfo.visualid);
const uint32_t value_list[] = {colormap};
// Create the hidden window // Create the hidden window
xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
m_window = xcb_generate_id(m_connection); m_window = xcb_generate_id(m_connection);
xcb_create_window( xcb_create_window(
m_connection, m_connection,
screen->root_depth, static_cast<uint8_t>(visualInfo.depth),
m_window, screen->root, m_window,
screen->root,
0, 0, 0, 0,
width, height, width, height,
0, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, visualInfo.visualid,
0, NULL XCB_CW_COLORMAP,
value_list
); );
// Create the context // Create the context
@ -167,6 +187,80 @@ void GlxContext::setVerticalSyncEnabled(bool enabled)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPerPixel, const ContextSettings& settings) XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPerPixel, const ContextSettings& settings)
{ {
// First try to get a visual via an associated GLXFBConfig (requires GLX 1.3 or greater)
// There are no GLX versions prior to 1.0
int major, minor;
if (glXQueryVersion(display, &major, &minor) && ((major > 1) || (minor >= 3)))
{
// Select a GLXFB config that matches the requested context settings
int nbConfigs = 0;
int fbAttributes[] =
{
GLX_DEPTH_SIZE, static_cast<int>(settings.depthBits),
GLX_STENCIL_SIZE, static_cast<int>(settings.stencilBits),
GLX_SAMPLE_BUFFERS, settings.antialiasingLevel > 0,
GLX_SAMPLES, static_cast<int>(settings.antialiasingLevel),
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, bitsPerPixel == 32 ? 8 : 0,
GLX_DOUBLEBUFFER, True,
GLX_X_RENDERABLE, True,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_CONFIG_CAVEAT, GLX_NONE,
None
};
GLXFBConfig* configs = glXChooseFBConfig(display, DefaultScreen(display), fbAttributes, &nbConfigs);
int bestScore = 0xFFFF;
XVisualInfo bestVisual;
for (int i = 0; configs && (i < nbConfigs); ++i)
{
XVisualInfo* visual = glXGetVisualFromFBConfig(display, configs[i]);
// We only want FBConfigs with associated X visuals (most have one)
if (!visual)
continue;
// Check mandatory attributes
int doubleBuffer;
glXGetFBConfigAttrib(display, configs[i], GLX_DOUBLEBUFFER, &doubleBuffer);
if (!doubleBuffer)
continue;
// Extract the components of the current visual
int red, green, blue, alpha, depth, stencil, multiSampling, samples;
glXGetFBConfigAttrib(display, configs[i], GLX_RED_SIZE, &red);
glXGetFBConfigAttrib(display, configs[i], GLX_GREEN_SIZE, &green);
glXGetFBConfigAttrib(display, configs[i], GLX_BLUE_SIZE, &blue);
glXGetFBConfigAttrib(display, configs[i], GLX_ALPHA_SIZE, &alpha);
glXGetFBConfigAttrib(display, configs[i], GLX_DEPTH_SIZE, &depth);
glXGetFBConfigAttrib(display, configs[i], GLX_STENCIL_SIZE, &stencil);
glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetFBConfigAttrib(display, configs[i], GLX_SAMPLES_ARB, &samples);
// Evaluate the visual
int color = red + green + blue + alpha;
int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0);
// If it's better than the current best, make it the new best
if (score < bestScore)
{
bestScore = score;
bestVisual = *visual;
}
XFree(visual);
}
XFree(configs);
if (bestScore < 0xFFFF)
return bestVisual;
}
// Retrieve all the visuals // Retrieve all the visuals
int count; int count;
XVisualInfo* visuals = XGetVisualInfo(display, 0, NULL, &count); XVisualInfo* visuals = XGetVisualInfo(display, 0, NULL, &count);
@ -226,82 +320,24 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co
// Save the creation settings // Save the creation settings
m_settings = settings; m_settings = settings;
// Get the attributes of the target window
XWindowAttributes windowAttributes;
if (XGetWindowAttributes(m_display, m_window, &windowAttributes) == 0)
{
err() << "Failed to get the window attributes" << std::endl;
return;
}
// Setup the visual infos to match
XVisualInfo tpl;
tpl.depth = windowAttributes.depth;
tpl.visualid = XVisualIDFromVisual(windowAttributes.visual);
tpl.screen = DefaultScreen(m_display);
// Get all the visuals matching the template
int nbVisuals = 0;
XVisualInfo* visuals = XGetVisualInfo(m_display, VisualDepthMask | VisualIDMask | VisualScreenMask, &tpl, &nbVisuals);
if (!visuals || (nbVisuals == 0))
{
if (visuals)
XFree(visuals);
err() << "There is no valid visual for the selected screen" << std::endl;
return;
}
// Find the best visual
int bestScore = 0xFFFF;
XVisualInfo* bestVisual = NULL;
for (int i = 0; i < nbVisuals; ++i)
{
// Get the current visual attributes
int RGBA, doubleBuffer, red, green, blue, alpha, depth, stencil, multiSampling, samples;
glXGetConfig(m_display, &visuals[i], GLX_RGBA, &RGBA);
glXGetConfig(m_display, &visuals[i], GLX_DOUBLEBUFFER, &doubleBuffer);
glXGetConfig(m_display, &visuals[i], GLX_RED_SIZE, &red);
glXGetConfig(m_display, &visuals[i], GLX_GREEN_SIZE, &green);
glXGetConfig(m_display, &visuals[i], GLX_BLUE_SIZE, &blue);
glXGetConfig(m_display, &visuals[i], GLX_ALPHA_SIZE, &alpha);
glXGetConfig(m_display, &visuals[i], GLX_DEPTH_SIZE, &depth);
glXGetConfig(m_display, &visuals[i], GLX_STENCIL_SIZE, &stencil);
glXGetConfig(m_display, &visuals[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetConfig(m_display, &visuals[i], GLX_SAMPLES_ARB, &samples);
// First check the mandatory parameters
if ((RGBA == 0) || (doubleBuffer == 0))
continue;
// Evaluate the current configuration
int color = red + green + blue + alpha;
int score = evaluateFormat(bitsPerPixel, m_settings, color, depth, stencil, multiSampling ? samples : 0);
// Keep it if it's better than the current best
if (score < bestScore)
{
bestScore = score;
bestVisual = &visuals[i];
}
}
// Make sure that we have found a visual
if (!bestVisual)
{
err() << "Failed to find a suitable pixel format for the window -- cannot create OpenGL context" << std::endl;
return;
}
// Get the context to share display lists with // Get the context to share display lists with
GLXContext toShare = shared ? shared->m_context : NULL; GLXContext toShare = shared ? shared->m_context : NULL;
// Create the OpenGL context -- first try context versions >= 3.0 if it is requested (they require special code) // There are no GLX versions prior to 1.0
while (!m_context && (m_settings.majorVersion >= 3)) int major = 0;
int minor = 0;
if (!glXQueryVersion(m_display, &major, &minor))
err() << "Failed to query GLX version, limited to legacy context creation" << std::endl;
// Create the OpenGL context -- first try context versions >= 3.0 if it is requested (they require special code and GLX 1.3 or greater)
if ((m_settings.majorVersion >= 3) && ((major > 1) || (minor >= 3)))
{ {
const GLubyte* name = reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB"); const GLubyte* name = reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB");
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(name)); PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(name));
if (glXCreateContextAttribsARB) if (glXCreateContextAttribsARB)
{ {
// Select a GLXFB config that matches the requested context settings
int nbConfigs = 0; int nbConfigs = 0;
int fbAttributes[] = int fbAttributes[] =
{ {
@ -322,39 +358,98 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co
}; };
GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), fbAttributes, &nbConfigs); GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), fbAttributes, &nbConfigs);
if (configs && nbConfigs) int bestScore = 0xFFFF;
GLXFBConfig* bestConfig;
for (int i = 0; configs && (i < nbConfigs); ++i)
{ {
// Create the context XVisualInfo* visual = glXGetVisualFromFBConfig(m_display, configs[i]);
int attributes[] =
// We only want FBConfigs with associated X visuals (most have one)
// We do this to match what was used during window creation
if (!visual)
continue;
// We won't need the visual for glXCreateContextAttribsARB, so get rid of it
XFree(visual);
// Check mandatory attributes
int doubleBuffer;
glXGetFBConfigAttrib(m_display, configs[i], GLX_DOUBLEBUFFER, &doubleBuffer);
if (!doubleBuffer)
continue;
// Extract the components of the current visual
int red, green, blue, alpha, depth, stencil, multiSampling, samples;
glXGetFBConfigAttrib(m_display, configs[i], GLX_RED_SIZE, &red);
glXGetFBConfigAttrib(m_display, configs[i], GLX_GREEN_SIZE, &green);
glXGetFBConfigAttrib(m_display, configs[i], GLX_BLUE_SIZE, &blue);
glXGetFBConfigAttrib(m_display, configs[i], GLX_ALPHA_SIZE, &alpha);
glXGetFBConfigAttrib(m_display, configs[i], GLX_DEPTH_SIZE, &depth);
glXGetFBConfigAttrib(m_display, configs[i], GLX_STENCIL_SIZE, &stencil);
glXGetFBConfigAttrib(m_display, configs[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetFBConfigAttrib(m_display, configs[i], GLX_SAMPLES_ARB, &samples);
// Evaluate the visual
int color = red + green + blue + alpha;
int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0);
// If it's better than the current best, make it the new best
if (score < bestScore)
{ {
GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion), bestScore = score;
GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion), bestConfig = &configs[i];
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, }
0, 0 }
};
m_context = glXCreateContextAttribsARB(m_display, configs[0], toShare, true, attributes); if (bestScore < 0xFFFF)
{
while (!m_context && (m_settings.majorVersion >= 3))
{
// Create the context
int attributes[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion),
GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion),
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0, 0
};
m_context = glXCreateContextAttribsARB(m_display, *bestConfig, toShare, true, attributes);
if (m_context)
{
// Update the creation settings from the chosen format
int depth, stencil, multiSampling, samples;
glXGetFBConfigAttrib(m_display, *bestConfig, GLX_DEPTH_SIZE, &depth);
glXGetFBConfigAttrib(m_display, *bestConfig, GLX_STENCIL_SIZE, &stencil);
glXGetFBConfigAttrib(m_display, *bestConfig, GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetFBConfigAttrib(m_display, *bestConfig, GLX_SAMPLES_ARB, &samples);
m_settings.depthBits = static_cast<unsigned int>(depth);
m_settings.stencilBits = static_cast<unsigned int>(stencil);
m_settings.antialiasingLevel = multiSampling ? samples : 0;
}
else
{
// If we couldn't create the context, lower the version number and try again -- stop at 3.0
// Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care
if (m_settings.minorVersion > 0)
{
// If the minor version is not 0, we decrease it and try again
m_settings.minorVersion--;
}
else
{
// If the minor version is 0, we decrease the major version
m_settings.majorVersion--;
m_settings.minorVersion = 9;
}
}
}
} }
if (configs) if (configs)
XFree(configs); XFree(configs);
} }
// If we couldn't create the context, lower the version number and try again -- stop at 3.0
// Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care
if (!m_context)
{
if (m_settings.minorVersion > 0)
{
// If the minor version is not 0, we decrease it and try again
m_settings.minorVersion--;
}
else
{
// If the minor version is 0, we decrease the major version
m_settings.majorVersion--;
m_settings.minorVersion = 9;
}
}
} }
// If the OpenGL >= 3.0 context failed or if we don't want one, create a regular OpenGL 1.x/2.x context // If the OpenGL >= 3.0 context failed or if we don't want one, create a regular OpenGL 1.x/2.x context
@ -364,31 +459,42 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co
m_settings.majorVersion = 2; m_settings.majorVersion = 2;
m_settings.minorVersion = 0; m_settings.minorVersion = 0;
m_context = glXCreateContext(m_display, bestVisual, toShare, true); // Retrieve the attributes of the target window
XWindowAttributes windowAttributes;
if (XGetWindowAttributes(m_display, m_window, &windowAttributes) == 0)
{
err() << "Failed to get the window attributes" << std::endl;
return;
}
// Get its visuals
XVisualInfo tpl;
tpl.screen = DefaultScreen(m_display);
tpl.visualid = XVisualIDFromVisual(windowAttributes.visual);
int nbVisuals = 0;
XVisualInfo* visualInfo = XGetVisualInfo(m_display, VisualIDMask | VisualScreenMask, &tpl, &nbVisuals);
// Create the context, using the target window's visual
m_context = glXCreateContext(m_display, visualInfo, toShare, true);
if (!m_context) if (!m_context)
{ {
err() << "Failed to create an OpenGL context for this window" << std::endl; err() << "Failed to create an OpenGL context for this window" << std::endl;
return; return;
} }
// Update the creation settings from the chosen format
int depth, stencil, multiSampling, samples;
glXGetConfig(m_display, visualInfo, GLX_DEPTH_SIZE, &depth);
glXGetConfig(m_display, visualInfo, GLX_STENCIL_SIZE, &stencil);
glXGetConfig(m_display, visualInfo, GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetConfig(m_display, visualInfo, GLX_SAMPLES_ARB, &samples);
m_settings.depthBits = static_cast<unsigned int>(depth);
m_settings.stencilBits = static_cast<unsigned int>(stencil);
m_settings.antialiasingLevel = multiSampling ? samples : 0;
// Free the visual info
XFree(visualInfo);
} }
// Update the creation settings from the chosen format
int depth, stencil, multiSampling, samples;
glXGetConfig(m_display, bestVisual, GLX_DEPTH_SIZE, &depth);
glXGetConfig(m_display, bestVisual, GLX_STENCIL_SIZE, &stencil);
glXGetConfig(m_display, bestVisual, GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
glXGetConfig(m_display, bestVisual, GLX_SAMPLES_ARB, &samples);
m_settings.depthBits = static_cast<unsigned int>(depth);
m_settings.stencilBits = static_cast<unsigned int>(stencil);
m_settings.antialiasingLevel = multiSampling ? samples : 0;
// Change the target window's colormap so that it matches the context's one
::Window root = RootWindow(m_display, DefaultScreen(m_display));
Colormap colorMap = XCreateColormap(m_display, root, bestVisual->visual, AllocNone);
XSetWindowColormap(m_display, m_window, colorMap);
// Free the temporary visuals array
XFree(visuals);
} }
} // namespace priv } // namespace priv

View File

@ -140,7 +140,7 @@ m_useSizeHints(false)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& /*settings*/) : WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) :
m_window (0), m_window (0),
m_inputMethod (NULL), m_inputMethod (NULL),
m_inputContext(NULL), m_inputContext(NULL),
@ -176,8 +176,13 @@ m_useSizeHints(false)
if (fullscreen) if (fullscreen)
switchToFullscreen(mode); switchToFullscreen(mode);
// Choose the visual according to the context settings
XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings);
// Define the window attributes // Define the window attributes
const uint32_t value_list[] = {fullscreen, static_cast<uint32_t>(eventMask)}; xcb_colormap_t colormap = xcb_generate_id(m_connection);
xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid);
const uint32_t value_list[] = {fullscreen, static_cast<uint32_t>(eventMask), colormap};
// Create the window // Create the window
m_window = xcb_generate_id(m_connection); m_window = xcb_generate_id(m_connection);
@ -186,15 +191,15 @@ m_useSizeHints(false)
m_connection, m_connection,
xcb_create_window_checked( xcb_create_window_checked(
m_connection, m_connection,
XCB_COPY_FROM_PARENT, static_cast<uint8_t>(visualInfo.depth),
m_window, m_window,
m_screen->root, m_screen->root,
left, top, left, top,
width, height, width, height,
0, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
XCB_COPY_FROM_PARENT, visualInfo.visualid,
XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT, XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP,
value_list value_list
) )
)); ));