From d82f068f7efc63c103886fd5c153fff0cf8b94ec Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 13 May 2013 15:49:59 +0200 Subject: [PATCH] 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. --- src/context.c | 157 +++++++++++++++++++++++++ src/egl_context.c | 155 ++++++++++++++++-------- src/egl_platform.h | 1 + src/glx_context.c | 226 ++++++++++++++++++++--------------- src/glx_platform.h | 1 + src/internal.h | 22 +++- src/nsgl_platform.h | 1 + src/wgl_context.c | 279 ++++++++++++++++++++++++++------------------ src/wgl_platform.h | 3 +- 9 files changed, 589 insertions(+), 256 deletions(-) diff --git a/src/context.c b/src/context.c index 0966f39c..81b16876 100644 --- a/src/context.c +++ b/src/context.c @@ -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(); diff --git a/src/egl_context.c b/src/egl_context.c index b1abe6bc..de58774f 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -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) diff --git a/src/egl_platform.h b/src/egl_platform.h index 53917b7b..90f15797 100644 --- a/src/egl_platform.h +++ b/src/egl_platform.h @@ -43,6 +43,7 @@ #include #endif +#define _GLFW_PLATFORM_FBCONFIG EGLConfig egl #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_PLATFORM_LIBRARY_OPENGL_STATE _GLFWlibraryEGL egl diff --git a/src/glx_context.c b/src/glx_context.c index 46b502d3..262d165c 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -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); } } } else - window->glx.context = createLegacyContext(window, *native, share); + window->glx.context = createLegacyContext(window, native, share); XSetErrorHandler(NULL); - XFree(native); - if (window->glx.context == NULL) { char buffer[8192]; diff --git a/src/glx_platform.h b/src/glx_platform.h index 77d4690d..b259fe4f 100644 --- a/src/glx_platform.h +++ b/src/glx_platform.h @@ -60,6 +60,7 @@ #error "No OpenGL entry point retrieval mechanism was enabled" #endif +#define _GLFW_PLATFORM_FBCONFIG GLXFBConfig glx #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx #define _GLFW_PLATFORM_LIBRARY_OPENGL_STATE _GLFWlibraryGLX glx diff --git a/src/internal.h b/src/internal.h index 9e91613f..8121ce93 100644 --- a/src/internal.h +++ b/src/internal.h @@ -153,8 +153,11 @@ struct _GLFWwndconfig /*! @brief Framebuffer configuration. * - * This describes buffers and their sizes. It is used to pass framebuffer - * parameters from shared code to the platform API. + * This describes buffers and their sizes. It also contains + * a platform-specific ID used to map back to the backend API's object. + * + * It is used to pass framebuffer parameters from shared code to the platform + * API and also to enumerate and select available framebuffer configs. */ struct _GLFWfbconfig { @@ -172,6 +175,9 @@ struct _GLFWfbconfig GLboolean stereo; int samples; GLboolean sRGB; + + // This is defined in the context API's platform.h + _GLFW_PLATFORM_FBCONFIG; }; @@ -670,6 +676,18 @@ void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); */ int _glfwStringInExtensionString(const char* string, const GLubyte* extensions); +/*! @brief Chooses the framebuffer config that best matches the desired one. + * @param[in] desired The desired framebuffer config. + * @param[in] alternatives The framebuffer configs supported by the system. + * @param[in] count The number of entries in the alternatives array. + * @return The framebuffer config most closely matching the desired one, or @c + * NULL if none fulfilled the hard constraints of the desired values. + * @ingroup utility + */ +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count); + /*! @brief Checks and reads back properties from the current context. * @return `GL_TRUE` if successful, or `GL_FALSE` if the context is unusable. * @ingroup utility diff --git a/src/nsgl_platform.h b/src/nsgl_platform.h index 71041202..220246da 100644 --- a/src/nsgl_platform.h +++ b/src/nsgl_platform.h @@ -31,6 +31,7 @@ #define _nsgl_platform_h_ +#define _GLFW_PLATFORM_FBCONFIG #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_OPENGL_STATE _GLFWlibraryNSGL nsgl diff --git a/src/wgl_context.c b/src/wgl_context.c index 5f86dae2..c283e99f 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -45,7 +45,7 @@ static void initWGLExtensions(_GLFWwindow* window) { // This needs to include every function pointer loaded below window->wgl.SwapIntervalEXT = NULL; - window->wgl.ChoosePixelFormatARB = NULL; + window->wgl.GetPixelFormatAttribivARB = NULL; window->wgl.GetExtensionsStringARB = NULL; window->wgl.GetExtensionsStringEXT = NULL; window->wgl.CreateContextAttribsARB = NULL; @@ -116,14 +116,174 @@ static void initWGLExtensions(_GLFWwindow* window) if (_glfwPlatformExtensionSupported("WGL_ARB_pixel_format")) { - window->wgl.ChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) - wglGetProcAddress("wglChoosePixelFormatARB"); + window->wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) + wglGetProcAddress("wglGetPixelFormatAttribivARB"); - if (window->wgl.ChoosePixelFormatARB) + if (window->wgl.GetPixelFormatAttribivARB) window->wgl.ARB_pixel_format = GL_TRUE; } } +// Returns the specified attribute of the specified pixel format +// NOTE: Do not call this unless we have found WGL_ARB_pixel_format +// +static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib) +{ + int value = 0; + + if (!window->wgl.GetPixelFormatAttribivARB(window->wgl.dc, + pixelFormat, + 0, 1, &attrib, &value)) + { + // NOTE: We should probably handle this error somehow + return 0; + } + + return value; +} + +// Return a list of available and usable framebuffer configs +// +static GLboolean choosePixelFormat(_GLFWwindow* window, + const _GLFWfbconfig* desired, + int* result) +{ + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + + if (window->wgl.ARB_pixel_format) + { + nativeCount = getPixelFormatAttrib(window, + 1, + WGL_NUMBER_PIXEL_FORMATS_ARB); + } + else + { + nativeCount = DescribePixelFormat(window->wgl.dc, + 1, + sizeof(PIXELFORMATDESCRIPTOR), + NULL); + } + + if (!nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "WGL: No pixel formats found"); + return GL_FALSE; + } + + usableConfigs = (_GLFWfbconfig*) calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const int n = i + 1; + _GLFWfbconfig* u = usableConfigs + usableCount; + + if (window->wgl.ARB_pixel_format) + { + // Get pixel format attributes through WGL_ARB_pixel_format + if (!getPixelFormatAttrib(window, n, WGL_SUPPORT_OPENGL_ARB) || + !getPixelFormatAttrib(window, n, WGL_DRAW_TO_WINDOW_ARB) || + !getPixelFormatAttrib(window, n, WGL_DOUBLE_BUFFER_ARB)) + { + continue; + } + + if (getPixelFormatAttrib(window, n, WGL_PIXEL_TYPE_ARB) != + WGL_TYPE_RGBA_ARB) + { + continue; + } + + if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) == + WGL_NO_ACCELERATION_ARB) + { + continue; + } + + u->redBits = getPixelFormatAttrib(window, n, WGL_RED_BITS_ARB); + u->greenBits = getPixelFormatAttrib(window, n, WGL_GREEN_BITS_ARB); + u->blueBits = getPixelFormatAttrib(window, n, WGL_BLUE_BITS_ARB); + u->alphaBits = getPixelFormatAttrib(window, n, WGL_ALPHA_BITS_ARB); + + u->depthBits = getPixelFormatAttrib(window, n, WGL_DEPTH_BITS_ARB); + u->stencilBits = getPixelFormatAttrib(window, n, WGL_STENCIL_BITS_ARB); + + u->accumRedBits = getPixelFormatAttrib(window, n, WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = getPixelFormatAttrib(window, n, WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = getPixelFormatAttrib(window, n, WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = getPixelFormatAttrib(window, n, WGL_ACCUM_ALPHA_BITS_ARB); + + u->auxBuffers = getPixelFormatAttrib(window, n, WGL_AUX_BUFFERS_ARB); + u->stereo = getPixelFormatAttrib(window, n, WGL_STEREO_ARB); + + if (window->wgl.ARB_multisample) + u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB); + + if (window->wgl.ARB_framebuffer_sRGB) + u->sRGB = getPixelFormatAttrib(window, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); + } + else + { + PIXELFORMATDESCRIPTOR pfd; + + // Get pixel format attributes through old-fashioned PFDs + + if (!DescribePixelFormat(window->wgl.dc, + n, + sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || + !(pfd.dwFlags & PFD_SUPPORT_OPENGL) || + !(pfd.dwFlags & PFD_DOUBLEBUFFER)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && + (pfd.dwFlags & PFD_GENERIC_FORMAT)) + { + continue; + } + + if (pfd.iPixelType != PFD_TYPE_RGBA) + continue; + + u->redBits = pfd.cRedBits; + u->greenBits = pfd.cGreenBits; + u->blueBits = pfd.cBlueBits; + u->alphaBits = pfd.cAlphaBits; + + u->depthBits = pfd.cDepthBits; + u->stencilBits = pfd.cStencilBits; + + u->accumRedBits = pfd.cAccumRedBits; + u->accumGreenBits = pfd.cAccumGreenBits; + u->accumBlueBits = pfd.cAccumBlueBits; + u->accumAlphaBits = pfd.cAccumAlphaBits; + + u->auxBuffers = pfd.cAuxBuffers; + u->stereo = (pfd.dwFlags & PFD_STEREO) ? GL_TRUE : GL_FALSE; + } + + u->wgl = n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = closest->wgl; + + free(usableConfigs); + + return GL_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -183,106 +343,11 @@ int _glfwCreateContext(_GLFWwindow* window, return GL_FALSE; } - if (window->wgl.ARB_pixel_format) + if (!choosePixelFormat(window, fbconfig, &pixelFormat)) { - int index = 0; - UINT count; - - setWGLattrib(WGL_SUPPORT_OPENGL_ARB, TRUE); - setWGLattrib(WGL_DRAW_TO_WINDOW_ARB, TRUE); - setWGLattrib(WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB); - setWGLattrib(WGL_DOUBLE_BUFFER_ARB, TRUE); - - if (fbconfig->redBits) - setWGLattrib(WGL_RED_BITS_ARB, fbconfig->redBits); - if (fbconfig->greenBits) - setWGLattrib(WGL_GREEN_BITS_ARB, fbconfig->greenBits); - if (fbconfig->blueBits) - setWGLattrib(WGL_BLUE_BITS_ARB, fbconfig->blueBits); - if (fbconfig->alphaBits) - setWGLattrib(WGL_ALPHA_BITS_ARB, fbconfig->alphaBits); - - if (fbconfig->depthBits) - setWGLattrib(WGL_DEPTH_BITS_ARB, fbconfig->depthBits); - if (fbconfig->stencilBits) - setWGLattrib(WGL_STENCIL_BITS_ARB, fbconfig->stencilBits); - - if (fbconfig->auxBuffers) - setWGLattrib(WGL_AUX_BUFFERS_ARB, fbconfig->auxBuffers); - - if (fbconfig->accumRedBits) - setWGLattrib(WGL_ACCUM_RED_BITS_ARB, fbconfig->accumRedBits); - if (fbconfig->accumGreenBits) - setWGLattrib(WGL_ACCUM_GREEN_BITS_ARB, fbconfig->accumGreenBits); - if (fbconfig->accumBlueBits) - setWGLattrib(WGL_ACCUM_BLUE_BITS_ARB, fbconfig->accumBlueBits); - if (fbconfig->accumAlphaBits) - setWGLattrib(WGL_ACCUM_BLUE_BITS_ARB, fbconfig->accumAlphaBits); - - if (fbconfig->stereo) - setWGLattrib(WGL_STEREO_ARB, TRUE); - - if (window->wgl.ARB_multisample) - { - if (fbconfig->samples) - { - setWGLattrib(WGL_SAMPLE_BUFFERS_ARB, 1); - setWGLattrib(WGL_SAMPLES_ARB, fbconfig->samples); - } - } - - if (window->wgl.ARB_framebuffer_sRGB) - { - if (fbconfig->sRGB) - setWGLattrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, TRUE); - } - - setWGLattrib(0, 0); - - if (!window->wgl.ChoosePixelFormatARB(window->wgl.dc, - attribs, - NULL, - 1, - &pixelFormat, - &count)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to find a suitable pixel format"); - return GL_FALSE; - } - } - else - { - ZeroMemory(&pfd, sizeof(pfd)); - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - - pfd.dwFlags |= PFD_DRAW_TO_WINDOW | - PFD_SUPPORT_OPENGL | - PFD_DOUBLEBUFFER; - - if (fbconfig->stereo) - pfd.dwFlags |= PFD_STEREO; - - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = fbconfig->redBits + - fbconfig->greenBits + - fbconfig->blueBits; - pfd.cAlphaBits = fbconfig->alphaBits; - pfd.cAccumBits = fbconfig->accumRedBits + - fbconfig->accumGreenBits + - fbconfig->accumBlueBits; - pfd.cDepthBits = fbconfig->depthBits; - pfd.cStencilBits = fbconfig->stencilBits; - pfd.cAuxBuffers = fbconfig->auxBuffers; - - pixelFormat = ChoosePixelFormat(window->wgl.dc, &pfd); - if (!pixelFormat) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to find a suitable pixel format"); - return GL_FALSE; - } + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to find a suitable pixel format"); + return GL_FALSE; } if (!DescribePixelFormat(window->wgl.dc, pixelFormat, sizeof(pfd), &pfd)) @@ -293,14 +358,6 @@ int _glfwCreateContext(_GLFWwindow* window, return GL_FALSE; } - if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && - (pfd.dwFlags & PFD_GENERIC_FORMAT)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to find an accelerated pixel format"); - return GL_FALSE; - } - if (!SetPixelFormat(window->wgl.dc, pixelFormat, &pfd)) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/wgl_platform.h b/src/wgl_platform.h index b5d481fd..1472b98a 100644 --- a/src/wgl_platform.h +++ b/src/wgl_platform.h @@ -37,6 +37,7 @@ #include "../deps/GL/wglext.h" +#define _GLFW_PLATFORM_FBCONFIG int wgl #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl #define _GLFW_PLATFORM_LIBRARY_OPENGL_STATE _GLFWlibraryWGL wgl @@ -56,7 +57,7 @@ typedef struct _GLFWcontextWGL // Platform specific extensions (context specific) PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNWGLCHOOSEPIXELFORMATARBPROC ChoosePixelFormatARB; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;