diff --git a/doc/changelog.dox b/doc/changelog.dox index 86b5f7296..06b366683 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -40,6 +40,12 @@ See also: @subsection changelog-latest-changes Changes and improvements +- @ref Platform::GlfwApplication now behaves the same as + @ref Platform::Sdl2Application when creating an OpenGL context: first it + attempts to create a forward-compatible core OpenGL 3.2+ context and if + that fails, falls back to 2.1+ compatibility context. The + @cpp "no-forward-compatible-core-context" @ce workaround is implemented as + well, see @ref opengl-workarounds for more information. - @ref Platform::GlfwApplication no longer stores a needless global window pointer diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index 417f5ae65..b563209b7 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -26,6 +26,7 @@ #include "GlfwApplication.h" +#include #include #include #include @@ -169,7 +170,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf CORRADE_IGNORE_DEPRECATED_POP #endif - CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, "Platform::GlfwApplication::tryCreate(): context already created", false); + CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, "Platform::GlfwApplication::tryCreate(): window with OpenGL context already created", false); /* Window flags */ GLFWmonitor* monitor = nullptr; /* Needed for setting fullscreen */ @@ -212,13 +213,83 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf #else glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); #endif + + /* Request usable version otherwise */ + } else { + #ifndef MAGNUM_TARGET_GLES + /* First try to create core context. This is needed mainly on macOS and + Mesa, as support for recent OpenGL versions isn't implemented in + compatibility contexts (which are the default). Unlike SDL2, GLFW + requires at least version 3.2 to be able to request a core profile. */ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); + #else + /* For ES the major context version is compile-time constant */ + #ifdef MAGNUM_TARGET_GLES3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + #elif defined(MAGNUM_TARGET_GLES2) + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + #else + #error unsupported OpenGL ES version + #endif + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #endif } - /* Set context flags */ - _window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr); + /* Create window. Hide it by default so we don't have distracting window + blinking in case we have to destroy it again right away. If the creation + succeeds, make the context current so we can query GL_VENDOR below. */ + glfwWindowHint(GLFW_VISIBLE, false); + if((_window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr))) + glfwMakeContextCurrent(_window); + + #ifndef MAGNUM_TARGET_GLES + /* Fall back to (forward compatible) GL 2.1, if version is not + user-specified and either core context creation fails or we are on + binary NVidia/AMD drivers on Linux/Windows or Intel Windows drivers. + Instead of creating forward-compatible context with highest available + version, they force the version to the one specified, which is + completely useless behavior. */ + #ifndef CORRADE_TARGET_APPLE + constexpr static const char nvidiaVendorString[] = "NVIDIA Corporation"; + #ifdef CORRADE_TARGET_WINDOWS + constexpr static const char intelVendorString[] = "Intel"; + #endif + constexpr static const char amdVendorString[] = "ATI Technologies Inc."; + const char* vendorString; + #endif + if(glConfiguration.version() == GL::Version::None && (!_window + #ifndef CORRADE_TARGET_APPLE + /* Sorry about the UGLY code, HOPEFULLY THERE WON'T BE MORE WORKAROUNDS */ + || (vendorString = reinterpret_cast(glGetString(GL_VENDOR)), + (std::strncmp(vendorString, nvidiaVendorString, sizeof(nvidiaVendorString)) == 0 || + #ifdef CORRADE_TARGET_WINDOWS + std::strncmp(vendorString, intelVendorString, sizeof(intelVendorString)) == 0 || + #endif + std::strncmp(vendorString, amdVendorString, sizeof(amdVendorString)) == 0) + && !_context->isDriverWorkaroundDisabled("no-forward-compatible-core-context")) + #endif + )) { + /* Don't print any warning when doing the workaround, because the bug + will be there probably forever */ + if(!_window) Warning{} + << "Platform::GlfwApplication::tryCreate(): cannot create a window with core OpenGL context, falling back to compatibility context"; + else glfwDestroyWindow(_window); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, false); + + _window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr); + } + #endif + if(!_window) { - Error() << "Platform::GlfwApplication::tryCreate(): cannot create context"; - glfwTerminate(); + Error() << "Platform::GlfwApplication::tryCreate(): cannot create a window with OpenGL context"; return false; } @@ -237,10 +308,21 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf glfwSetScrollCallback(_window, staticMouseScrollEvent); glfwSetCharCallback(_window, staticTextInputEvent); + /* Make the final context current */ glfwMakeContextCurrent(_window); + /* Destroy everything when the Magnum context creation fails */ + if(!_context->tryCreate()) { + glfwDestroyWindow(_window); + _window = nullptr; + } + + /* Show the window once we are sure that everything is okay */ + if(!(configuration.windowFlags() & Configuration::WindowFlag::Hidden)) + glfwShowWindow(_window); + /* Return true if the initialization succeeds */ - return _context->tryCreate(); + return true; } #endif diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index e96eeb9fa..c7bb0cf90 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -244,6 +244,10 @@ class GlfwApplication { * printed and the program exits if the context cannot be created, see * @ref tryCreate() for an alternative. * + * On desktop GL, if version is not specified in @p glConfiguration, + * the application first tries to create core context (OpenGL 3.2+) and + * if that fails, falls back to compatibility OpenGL 2.1 context. + * * @note This function is available only if Magnum is compiled with * @ref MAGNUM_TARGET_GL enabled (done by default). See * @ref building-features for more information.