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;