From 277828ea444484abd4138fb7c9fd16f123438416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 11 Oct 2015 01:23:43 +0200 Subject: [PATCH] Ability to do GL context creation fallbacks even from Context constructor. In some cases the GL context creation might success without error, but the created version is one that we don't want (e.g. software GDI rasterizer on Windows). Previously the Context class constructor just exited the application and it was impossible to react on that from the application side (for example reducing some context feature requirements). Now there is Platform::Context::tryCreate(), which returns either created instance or `nullptr` if the instance creation failed with some error. That is now used in all Platform::*Application implementations. --- src/Magnum/Context.cpp | 26 ++++++++++++++----- src/Magnum/Context.h | 4 +++ src/Magnum/Platform/AbstractXApplication.cpp | 4 +-- src/Magnum/Platform/AndroidApplication.cpp | 4 +-- src/Magnum/Platform/Context.h | 24 ++++++++++++++++- src/Magnum/Platform/GlutApplication.cpp | 4 +-- src/Magnum/Platform/NaClApplication.cpp | 4 +-- src/Magnum/Platform/Sdl2Application.cpp | 4 +-- .../Platform/WindowlessCglApplication.cpp | 4 +-- .../Platform/WindowlessGlxApplication.cpp | 4 +-- .../Platform/WindowlessNaClApplication.cpp | 4 +-- .../Platform/WindowlessWglApplication.cpp | 4 +-- .../WindowlessWindowsEglApplication.cpp | 4 +-- 13 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/Magnum/Context.cpp b/src/Magnum/Context.cpp index a82b8ae7c..7818d1cad 100644 --- a/src/Magnum/Context.cpp +++ b/src/Magnum/Context.cpp @@ -402,10 +402,19 @@ const std::vector& Extension::extensions(Version version) { Context* Context::_current = nullptr; -Context::Context(void functionLoader()) { +Context::Context(NoCreateT, void functionLoader()) { /* Load GL function pointers */ if(functionLoader) functionLoader(); + /* Nothing else, waiting for create() to be called */ +} + +Context::Context(void functionLoader()): Context{NoCreateT{}, functionLoader} { + /* Hard exit if the context cannot be created */ + if(!create()) std::exit(1); +} + +bool Context::create() { GLint majorVersion, minorVersion; /* Get version on ES 3.0+/WebGL 2.0+ */ @@ -421,7 +430,7 @@ Context::Context(void functionLoader()) { const std::string version = versionString(); if(version.find("WebGL 2") == std::string::npos) { Error() << "Context: unsupported version string:" << version; - std::exit(65); + return false; } majorVersion = 3; minorVersion = 0; @@ -442,7 +451,7 @@ Context::Context(void functionLoader()) { { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(versionNumberError == Renderer::Error::InvalidEnum, - "Context: cannot retrieve OpenGL version:" << versionNumberError, ); + "Context: cannot retrieve OpenGL version:" << versionNumberError, false); #endif /* Allow ES2 context on driver that reports ES3 as supported */ @@ -467,7 +476,7 @@ Context::Context(void functionLoader()) { #endif } else { Error() << "Context: unsupported version string:" << version; - std::exit(65); + return false; } } #endif @@ -479,7 +488,7 @@ Context::Context(void functionLoader()) { #ifndef CORRADE_NO_ASSERT const auto error = Renderer::error(); CORRADE_ASSERT(error == Renderer::Error::NoError, - "Context: cannot retrieve OpenGL version:" << error, ); + "Context: cannot retrieve OpenGL version:" << error, false); #endif /* Check that the version is supported (now it probably is, but be sure) */ @@ -496,7 +505,7 @@ Context::Context(void functionLoader()) { #else Error() << "Context: unsupported OpenGL ES version" << std::make_pair(majorVersion, minorVersion); #endif - std::exit(66); + return false; } /* Context flags are supported since GL 3.0 */ @@ -572,7 +581,7 @@ Context::Context(void functionLoader()) { setupDriverWorkarounds(); /* Set this context as current */ - CORRADE_ASSERT(!_current, "Context: Another context currently active", ); + CORRADE_ASSERT(!_current, "Context: Another context currently active", false); _current = this; /* Print some info and initialize state tracker (which also prints some @@ -585,6 +594,9 @@ Context::Context(void functionLoader()) { /** @todo Get rid of these */ DefaultFramebuffer::initializeContextBasedFunctionality(*this); Renderer::initializeContextBasedFunctionality(); + + /* Everything okay */ + return true; } Context::Context(Context&& other): _version{std::move(other._version)}, diff --git a/src/Magnum/Context.h b/src/Magnum/Context.h index 239d34f9e..d0391a7e7 100644 --- a/src/Magnum/Context.h +++ b/src/Magnum/Context.h @@ -37,6 +37,7 @@ #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include "Magnum/visibility.h" #include "MagnumExternal/Optional/optional.hpp" @@ -464,8 +465,11 @@ class MAGNUM_EXPORT Context { private: static Context* _current; + explicit Context(NoCreateT, void functionLoader()); explicit Context(void functionLoader()); + bool create(); + /* Defined in Implementation/driverSpecific.cpp */ MAGNUM_LOCAL void setupDriverWorkarounds(); diff --git a/src/Magnum/Platform/AbstractXApplication.cpp b/src/Magnum/Platform/AbstractXApplication.cpp index f926abc5f..d284b48ff 100644 --- a/src/Magnum/Platform/AbstractXApplication.cpp +++ b/src/Magnum/Platform/AbstractXApplication.cpp @@ -95,8 +95,8 @@ bool AbstractXApplication::tryCreateContext(const Configuration& configuration) /* Set OpenGL context as current */ _contextHandler->makeCurrent(); - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } AbstractXApplication::~AbstractXApplication() { diff --git a/src/Magnum/Platform/AndroidApplication.cpp b/src/Magnum/Platform/AndroidApplication.cpp index c61d2c379..bc5b5577e 100644 --- a/src/Magnum/Platform/AndroidApplication.cpp +++ b/src/Magnum/Platform/AndroidApplication.cpp @@ -136,8 +136,8 @@ bool AndroidApplication::tryCreateContext(const Configuration& configuration) { /* Make the context current */ CORRADE_INTERNAL_ASSERT_OUTPUT(eglMakeCurrent(_display, _surface, _surface, _glContext)); - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } void AndroidApplication::swapBuffers() { diff --git a/src/Magnum/Platform/Context.h b/src/Magnum/Platform/Context.h index 32c39c7dd..bfac9d7c6 100644 --- a/src/Magnum/Platform/Context.h +++ b/src/Magnum/Platform/Context.h @@ -41,12 +41,26 @@ information. */ class Context: public Magnum::Context { public: + /** + * @brief Try to create the context + * + * Unline @ref Context(), this function returns `nullptr` on error + * instead of application exit and thus is usable for context creation + * fallbacks. + */ + static std::unique_ptr tryCreate() { + std::unique_ptr c{new Context{NoCreate}}; + return c->create() ? std::move(c) : nullptr; + } + /** * @brief Constructor * * Does initial setup, loads OpenGL function pointers using * platform-specific API, detects available features and enables them - * throughout the engine. + * throughout the engine. If detected version is unsupported or any + * other error occurs, a message is printed to output and the + * application exits. See @ref tryCreate() for an alternative. * @see @fn_gl{Get} with @def_gl{MAJOR_VERSION}, @def_gl{MINOR_VERSION}, * @def_gl{CONTEXT_FLAGS}, @def_gl{NUM_EXTENSIONS}, * @fn_gl{GetString} with @def_gl{EXTENSIONS} @@ -57,6 +71,14 @@ class Context: public Magnum::Context { #else Magnum::Context{nullptr} {} #endif + + private: + explicit Context(NoCreateT): + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) + Magnum::Context{NoCreate, flextGLInit} {} + #else + Magnum::Context{NoCreate, nullptr} {} + #endif }; }} diff --git a/src/Magnum/Platform/GlutApplication.cpp b/src/Magnum/Platform/GlutApplication.cpp index f3a49ae70..6a65c4b4d 100644 --- a/src/Magnum/Platform/GlutApplication.cpp +++ b/src/Magnum/Platform/GlutApplication.cpp @@ -96,8 +96,8 @@ bool GlutApplication::tryCreateContext(const Configuration& configuration) { glutMotionFunc(staticMouseMoveEvent); glutDisplayFunc(staticDrawEvent); - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } GlutApplication::~GlutApplication() = default; diff --git a/src/Magnum/Platform/NaClApplication.cpp b/src/Magnum/Platform/NaClApplication.cpp index 4ad50972f..8cc096abc 100644 --- a/src/Magnum/Platform/NaClApplication.cpp +++ b/src/Magnum/Platform/NaClApplication.cpp @@ -112,8 +112,8 @@ bool NaClApplication::tryCreateContext(const Configuration& configuration) { RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE|PP_INPUTEVENT_CLASS_WHEEL); RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } NaClApplication::~NaClApplication() = default; diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 4ca56e583..8cf2ce093 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -226,8 +226,8 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) { _glContext = SDL_SetVideoMode(configuration.size().x(), configuration.size().y(), 24, SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF); #endif - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } void Sdl2Application::swapBuffers() { diff --git a/src/Magnum/Platform/WindowlessCglApplication.cpp b/src/Magnum/Platform/WindowlessCglApplication.cpp index b111c1a01..c24cccd43 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.cpp +++ b/src/Magnum/Platform/WindowlessCglApplication.cpp @@ -96,8 +96,8 @@ bool WindowlessCglApplication::tryCreateContext(const Configuration&) { return false; } - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } WindowlessCglApplication::~WindowlessCglApplication() { diff --git a/src/Magnum/Platform/WindowlessGlxApplication.cpp b/src/Magnum/Platform/WindowlessGlxApplication.cpp index 731e70fa7..0155e91d2 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.cpp +++ b/src/Magnum/Platform/WindowlessGlxApplication.cpp @@ -145,8 +145,8 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration&) { return false; } - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } WindowlessGlxApplication::~WindowlessGlxApplication() { diff --git a/src/Magnum/Platform/WindowlessNaClApplication.cpp b/src/Magnum/Platform/WindowlessNaClApplication.cpp index b7dff7a45..9e34589c6 100644 --- a/src/Magnum/Platform/WindowlessNaClApplication.cpp +++ b/src/Magnum/Platform/WindowlessNaClApplication.cpp @@ -97,8 +97,8 @@ bool WindowlessNaClApplication::tryCreateContext(const Configuration&) { glSetCurrentContextPPAPI(graphics->pp_resource()); - c = new Platform::Context; - return true; + /* Return true if the initialization succeeds */ + return c = Platform::Context::tryCreate().release(); } WindowlessNaClApplication::~WindowlessNaClApplication() { diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp index f4bad5cbf..a00983bfb 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -116,8 +116,8 @@ bool WindowlessWglApplication::tryCreateContext(const Configuration&) { return false; } - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } WindowlessWglApplication::~WindowlessWglApplication() { diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp index 16807f8c5..30ccf4ee8 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp @@ -155,8 +155,8 @@ bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration&) { return false; } - _context.reset(new Platform::Context); - return true; + /* Return true if the initialization succeeds */ + return !!(_context = Platform::Context::tryCreate()); } WindowlessWindowsEglApplication::~WindowlessWindowsEglApplication() {