Commit d82f068f authored by Camilla Berglund's avatar Camilla Berglund
Browse files

Reintroduced manual framebuffer config selection.

The default behavior of WGL, EGL and GLX is to choose a config that has
/at least/ the specified number of bits, whereas the GLFW 2 behavior was
to choose the closest match with very few hard constraints.  Moving the
responsibility of finding the supported minimum values to the client was
problematic, as there's no way to enumerate supported configurations,
forcing the client to perform multiple (and slow) window/context
creation attempts.  Not even the currently set defaults (24-bit color
and depth, 8-bit stencil) is universally supported, as bug reports show.
parent 538556bf
......@@ -204,6 +204,163 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig)
return GL_TRUE;
}
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
const _GLFWfbconfig* alternatives,
unsigned int count)
{
unsigned int i;
unsigned int missing, leastMissing = UINT_MAX;
unsigned int colorDiff, leastColorDiff = UINT_MAX;
unsigned int extraDiff, leastExtraDiff = UINT_MAX;
const _GLFWfbconfig* current;
const _GLFWfbconfig* closest = NULL;
for (i = 0; i < count; i++)
{
current = alternatives + i;
if (desired->stereo > 0 && current->stereo == 0)
{
// Stereo is a hard constraint
continue;
}
// Count number of missing buffers
{
missing = 0;
if (desired->alphaBits > 0 && current->alphaBits == 0)
missing++;
if (desired->depthBits > 0 && current->depthBits == 0)
missing++;
if (desired->stencilBits > 0 && current->stencilBits == 0)
missing++;
if (desired->auxBuffers > 0 && current->auxBuffers < desired->auxBuffers)
missing += desired->auxBuffers - current->auxBuffers;
if (desired->samples > 0 && current->samples == 0)
{
// Technically, several multisampling buffers could be
// involved, but that's a lower level implementation detail and
// not important to us here, so we count them as one
missing++;
}
}
// These polynomials make many small channel size differences matter
// less than one large channel size difference
// Calculate color channel size difference value
{
colorDiff = 0;
if (desired->redBits > 0)
{
colorDiff += (desired->redBits - current->redBits) *
(desired->redBits - current->redBits);
}
if (desired->greenBits > 0)
{
colorDiff += (desired->greenBits - current->greenBits) *
(desired->greenBits - current->greenBits);
}
if (desired->blueBits > 0)
{
colorDiff += (desired->blueBits - current->blueBits) *
(desired->blueBits - current->blueBits);
}
}
// Calculate non-color channel size difference value
{
extraDiff = 0;
if (desired->alphaBits > 0)
{
extraDiff += (desired->alphaBits - current->alphaBits) *
(desired->alphaBits - current->alphaBits);
}
if (desired->depthBits > 0)
{
extraDiff += (desired->depthBits - current->depthBits) *
(desired->depthBits - current->depthBits);
}
if (desired->stencilBits > 0)
{
extraDiff += (desired->stencilBits - current->stencilBits) *
(desired->stencilBits - current->stencilBits);
}
if (desired->accumRedBits > 0)
{
extraDiff += (desired->accumRedBits - current->accumRedBits) *
(desired->accumRedBits - current->accumRedBits);
}
if (desired->accumGreenBits > 0)
{
extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
(desired->accumGreenBits - current->accumGreenBits);
}
if (desired->accumBlueBits > 0)
{
extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
(desired->accumBlueBits - current->accumBlueBits);
}
if (desired->accumAlphaBits > 0)
{
extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
(desired->accumAlphaBits - current->accumAlphaBits);
}
if (desired->samples > 0)
{
extraDiff += (desired->samples - current->samples) *
(desired->samples - current->samples);
}
if (desired->sRGB)
{
if (!current->sRGB)
extraDiff++;
}
}
// Figure out if the current one is better than the best one found so far
// Least number of missing buffers is the most important heuristic,
// then color buffer size match and lastly size match for other buffers
if (missing < leastMissing)
closest = current;
else if (missing == leastMissing)
{
if ((colorDiff < leastColorDiff) ||
(colorDiff == leastColorDiff && extraDiff < leastExtraDiff))
{
closest = current;
}
}
if (current == closest)
{
leastMissing = missing;
leastColorDiff = colorDiff;
leastExtraDiff = extraDiff;
}
}
return closest;
}
GLboolean _glfwRefreshContextParams(void)
{
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
......
......@@ -100,6 +100,107 @@ static const char* getErrorString(EGLint error)
return "UNKNOWN EGL ERROR";
}
// Returns the specified attribute of the specified EGLConfig
//
static int getConfigAttrib(EGLConfig config, int attrib)
{
int value;
eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value);
return value;
}
// Return a list of available and usable framebuffer configs
//
static GLboolean chooseFBConfigs(const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* desired,
EGLConfig* result)
{
EGLConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest;
int i, nativeCount, usableCount;
eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount);
if (!nativeCount)
{
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned");
return GL_FALSE;
}
nativeConfigs = (EGLConfig*) calloc(nativeCount, sizeof(EGLConfig));
eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount);
usableConfigs = (_GLFWfbconfig*) calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0;
for (i = 0; i < nativeCount; i++)
{
const EGLConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
#if defined(_GLFW_X11)
if (!getConfigAttrib(n, EGL_NATIVE_VISUAL_ID))
{
// Only consider EGLConfigs with associated visuals
continue;
}
#endif // _GLFW_X11
if (!(getConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER))
{
// Only consider RGB(A) EGLConfigs
continue;
}
if (!(getConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
{
// Only consider window EGLConfigs
continue;
}
if (wndconfig->clientAPI == GLFW_OPENGL_ES_API)
{
if (wndconfig->glMajor == 1)
{
if (!(getConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT))
continue;
}
else
{
if (!(getConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT))
continue;
}
}
else if (wndconfig->clientAPI == GLFW_OPENGL_API)
{
if (!(getConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT))
continue;
}
u->redBits = getConfigAttrib(n, EGL_RED_SIZE);
u->greenBits = getConfigAttrib(n, EGL_GREEN_SIZE);
u->blueBits = getConfigAttrib(n, EGL_BLUE_SIZE);
u->alphaBits = getConfigAttrib(n, EGL_ALPHA_SIZE);
u->depthBits = getConfigAttrib(n, EGL_DEPTH_SIZE);
u->stencilBits = getConfigAttrib(n, EGL_STENCIL_SIZE);
u->samples = getConfigAttrib(n, EGL_SAMPLES);
u->egl = n;
usableCount++;
}
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest)
*result = closest->egl;
free(nativeConfigs);
free(usableConfigs);
return closest ? GL_TRUE : GL_FALSE;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
......@@ -162,57 +263,11 @@ int _glfwCreateContext(_GLFWwindow* window,
if (wndconfig->share)
share = wndconfig->share->egl.context;
// Find a suitable EGLConfig
if (!chooseFBConfigs(wndconfig, fbconfig, &config))
{
int index = 0;
if (wndconfig->clientAPI == GLFW_OPENGL_API)
setEGLattrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT);
if (wndconfig->clientAPI == GLFW_OPENGL_ES_API)
{
if (wndconfig->glMajor == 1)
setEGLattrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT);
if (wndconfig->glMajor == 2)
setEGLattrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
if (wndconfig->glMajor == 3)
setEGLattrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR);
}
setEGLattrib(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER);
if (fbconfig->redBits)
setEGLattrib(EGL_RED_SIZE, fbconfig->redBits);
if (fbconfig->greenBits)
setEGLattrib(EGL_GREEN_SIZE, fbconfig->greenBits);
if (fbconfig->blueBits)
setEGLattrib(EGL_BLUE_SIZE, fbconfig->blueBits);
if (fbconfig->alphaBits)
setEGLattrib(EGL_ALPHA_SIZE, fbconfig->alphaBits);
if (fbconfig->depthBits)
setEGLattrib(EGL_DEPTH_SIZE, fbconfig->depthBits);
if (fbconfig->stencilBits)
setEGLattrib(EGL_STENCIL_SIZE, fbconfig->stencilBits);
if (fbconfig->samples)
{
setEGLattrib(EGL_SAMPLE_BUFFERS, 1);
setEGLattrib(EGL_SAMPLES, fbconfig->samples);
}
setEGLattrib(EGL_NONE, EGL_NONE);
eglChooseConfig(_glfw.egl.display, attribs, &config, 1, &count);
if (!count)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to find a suitable EGLConfig: %s",
getErrorString(eglGetError()));
return GL_FALSE;
}
_glfwInputError(GLFW_PLATFORM_ERROR,
"EGL: Failed to find a suitable EGLConfig");
return GL_FALSE;
}
#if defined(_GLFW_X11)
......
......@@ -43,6 +43,7 @@
#include <dlfcn.h>
#endif
#define _GLFW_PLATFORM_FBCONFIG EGLConfig egl
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextEGL egl
#define _GLFW_PLATFORM_LIBRARY_OPENGL_STATE _GLFWlibraryEGL egl
......
......@@ -53,6 +53,130 @@ static int errorHandler(Display *display, XErrorEvent* event)
return 0;
}
// Returns the specified attribute of the specified GLXFBConfig
// NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig
//
static int getFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
{
int value;
if (_glfw.glx.SGIX_fbconfig)
{
_glfw.glx.GetFBConfigAttribSGIX(_glfw.x11.display,
fbconfig, attrib, &value);
}
else
glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value);
return value;
}
// Return a list of available and usable framebuffer configs
//
static GLboolean chooseFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result)
{
GLXFBConfig* nativeConfigs;
_GLFWfbconfig* usableConfigs;
const _GLFWfbconfig* closest;
int i, nativeCount, usableCount;
const char* vendor;
GLboolean trustWindowBit = GL_TRUE;
vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
if (strcmp(vendor, "Chromium") == 0)
{
// HACK: This is a (hopefully temporary) workaround for Chromium
// (VirtualBox GL) not setting the window bit on any GLXFBConfigs
trustWindowBit = GL_FALSE;
}
if (_glfw.glx.SGIX_fbconfig)
{
nativeConfigs = _glfw.glx.ChooseFBConfigSGIX(_glfw.x11.display,
_glfw.x11.screen,
NULL,
&nativeCount);
}
else
{
nativeConfigs = glXGetFBConfigs(_glfw.x11.display,
_glfw.x11.screen,
&nativeCount);
}
if (!nativeCount)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
"GLX: No GLXFBConfigs returned");
return GL_FALSE;
}
usableConfigs = (_GLFWfbconfig*) calloc(nativeCount, sizeof(_GLFWfbconfig));
usableCount = 0;
for (i = 0; i < nativeCount; i++)
{
const GLXFBConfig n = nativeConfigs[i];
_GLFWfbconfig* u = usableConfigs + usableCount;
if (!getFBConfigAttrib(n, GLX_DOUBLEBUFFER) ||
!getFBConfigAttrib(n, GLX_VISUAL_ID))
{
// Only consider double-buffered GLXFBConfigs with associated visuals
continue;
}
if (!(getFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
{
// Only consider RGBA GLXFBConfigs
continue;
}
if (!(getFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT))
{
if (trustWindowBit)
{
// Only consider window GLXFBConfigs
continue;
}
}
u->redBits = getFBConfigAttrib(n, GLX_RED_SIZE);
u->greenBits = getFBConfigAttrib(n, GLX_GREEN_SIZE);
u->blueBits = getFBConfigAttrib(n, GLX_BLUE_SIZE);
u->alphaBits = getFBConfigAttrib(n, GLX_ALPHA_SIZE);
u->depthBits = getFBConfigAttrib(n, GLX_DEPTH_SIZE);
u->stencilBits = getFBConfigAttrib(n, GLX_STENCIL_SIZE);
u->accumRedBits = getFBConfigAttrib(n, GLX_ACCUM_RED_SIZE);
u->accumGreenBits = getFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE);
u->accumBlueBits = getFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE);
u->accumAlphaBits = getFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE);
u->auxBuffers = getFBConfigAttrib(n, GLX_AUX_BUFFERS);
u->stereo = getFBConfigAttrib(n, GLX_STEREO);
if (_glfw.glx.ARB_multisample)
u->samples = getFBConfigAttrib(n, GLX_SAMPLES);
if (_glfw.glx.ARB_framebuffer_sRGB)
u->sRGB = getFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
u->glx = n;
usableCount++;
}
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
if (closest)
*result = closest->glx;
XFree(nativeConfigs);
free(usableConfigs);
return closest ? GL_TRUE : GL_FALSE;
}
// Create the OpenGL context using legacy API
//
static GLXContext createLegacyContext(_GLFWwindow* window,
......@@ -240,104 +364,30 @@ int _glfwCreateContext(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig)
{
int attribs[40];
GLXFBConfig* native;
GLXFBConfig native;
GLXContext share = NULL;
if (wndconfig->share)
share = wndconfig->share->glx.context;
// Find a suitable GLXFBConfig
if (!chooseFBConfig(fbconfig, &native))
{
int count, index = 0;
setGLXattrib(GLX_DOUBLEBUFFER, True);
setGLXattrib(GLX_X_RENDERABLE, True);
if (fbconfig->redBits)
setGLXattrib(GLX_RED_SIZE, fbconfig->redBits);
if (fbconfig->greenBits)
setGLXattrib(GLX_GREEN_SIZE, fbconfig->greenBits);
if (fbconfig->blueBits)
setGLXattrib(GLX_BLUE_SIZE, fbconfig->blueBits);
if (fbconfig->alphaBits)
setGLXattrib(GLX_ALPHA_SIZE, fbconfig->alphaBits);
if (fbconfig->depthBits)
setGLXattrib(GLX_DEPTH_SIZE, fbconfig->depthBits);
if (fbconfig->stencilBits)
setGLXattrib(GLX_STENCIL_SIZE, fbconfig->stencilBits);
if (fbconfig->auxBuffers)
setGLXattrib(GLX_AUX_BUFFERS, fbconfig->auxBuffers);
if (fbconfig->accumRedBits)
setGLXattrib(GLX_ACCUM_RED_SIZE, fbconfig->accumRedBits);
if (fbconfig->accumGreenBits)
setGLXattrib(GLX_ACCUM_GREEN_SIZE, fbconfig->accumGreenBits);
if (fbconfig->accumBlueBits)
setGLXattrib(GLX_ACCUM_BLUE_SIZE, fbconfig->accumBlueBits);
if (fbconfig->accumAlphaBits)
setGLXattrib(GLX_ACCUM_BLUE_SIZE, fbconfig->accumAlphaBits);
if (fbconfig->stereo)
setGLXattrib(GLX_STEREO, True);
if (_glfw.glx.ARB_multisample)
{
if (fbconfig->samples)
{
setGLXattrib(GLX_SAMPLE_BUFFERS_ARB, 1);
setGLXattrib(GLX_SAMPLES_ARB, fbconfig->samples);
}
}
if (_glfw.glx.ARB_framebuffer_sRGB)
{
if (fbconfig->sRGB)
setGLXattrib(GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, True);
}
setGLXattrib(None, None);
if (_glfw.glx.SGIX_fbconfig)
{
native = _glfw.glx.ChooseFBConfigSGIX(_glfw.x11.display,
_glfw.x11.screen,
attribs,
&count);
}
else
{
native = glXChooseFBConfig(_glfw.x11.display,
_glfw.x11.screen,
attribs,
&count);
}
if (native == NULL)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to find a suitable GLXFBConfig");
return GL_FALSE;
}
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to find a suitable GLXFBConfig");
return GL_FALSE;
}
// Retrieve the corresponding visual
if (_glfw.glx.SGIX_fbconfig)
{
window->glx.visual =
_glfw.glx.GetVisualFromFBConfigSGIX(_glfw.x11.display, *native);
_glfw.glx.GetVisualFromFBConfigSGIX(_glfw.x11.display, native);
}
else
{
window->glx.visual = glXGetVisualFromFBConfig(_glfw.x11.display,
*native);
}
window->glx.visual = glXGetVisualFromFBConfig(_glfw.x11.display, native);
if (window->glx.visual == NULL)
{
XFree(native);
_glfwInputError(GLFW_PLATFORM_ERROR,
"GLX: Failed to retrieve visual for GLXFBConfig");
return GL_FALSE;
......@@ -349,8 +399,6 @@ int _glfwCreateContext(_GLFWwindow* window,
!_glfw.glx.ARB_create_context_profile ||
!_glfw.glx.EXT_create_context_es2_profile)
{
XFree(native);
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"GLX: OpenGL ES requested but "
"GLX_EXT_create_context_es2_profile is unavailable");
......@@ -362,8 +410,6 @@ int _glfwCreateContext(_GLFWwindow* window,
{
if (!_glfw.glx.ARB_create_context)
{
XFree(native);
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"GLX: Forward compatibility requested but "
"GLX_ARB_create_context_profile is unavailable");
......@@ -376,8 +422,6 @@ int _glfwCreateContext(_GLFWwindow* window,
if (!_glfw.glx.ARB_create_context ||
!_glfw.glx.ARB_create_context_profile)
{
XFree(native);
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
"GLX: An OpenGL profile requested but "
"GLX_ARB_create_context_profile is unavailable");
......@@ -447,7 +491,7 @@ int _glfwCreateContext(_GLFWwindow* window,
window->glx.context =
_glfw.glx.CreateContextAttribsARB(_glfw.x11.display,
*native,
native,
share,
True,
attribs);
......@@ -462,17 +506,15 @@ int _glfwCreateContext(_GLFWwindow* window,
wndconfig->glProfile == GLFW_OPENGL_NO_PROFILE &&
wndconfig->glForward == GL_FALSE)
{
window->glx.context = createLegacyContext(window, *native, share);
window->glx.context = createLegacyContext(window, native, share);