From 6feda42f134a8e7aeb071ccaa44a1941967275da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 16 Jun 2016 19:52:15 +0200 Subject: [PATCH] Platform: reworked windowless apps to make threaded contexts possible. Each Windowless*Application has now a companion Windowless*Context that manages just the GL context creation and nothing else, with the ability to just create the context and not make it current, so it doesn't affect current thread state and can be moved to another thread and make current there. Other things that were done: * Using `NoCreateT` instead of `nullptr` for creating the application without creating GL context. * Properly handling failed creation of Magnum context instance -- if it errors out, also the GL context is destroyed to make it possible to create the context with a different configuration. * Reworked AMD and NVidia binary driver workaround, where core context created with specific version doesn't automatically choose the newest available (creating compatibility context on the other hand causes the version to get stuck on 2.1 on Mesa and OSX). * Added the above workaround also for WindowlessWglApplication to avoid driver issues in the future. * Reworked WindowlessWglApplication to not be so crazily entangled. It was a misunderstanding on my side about how WINAPI works. Much simpler now (and I hope still working :D). --- doc/platform.dox | 70 ++++- .../Platform/WindowlessCglApplication.cpp | 93 ++++--- .../Platform/WindowlessCglApplication.h | 142 ++++++++-- .../Platform/WindowlessEglApplication.cpp | 96 ++++--- .../Platform/WindowlessEglApplication.h | 205 ++++++++++---- .../Platform/WindowlessGlxApplication.cpp | 180 ++++++++----- .../Platform/WindowlessGlxApplication.h | 222 ++++++++++----- .../Platform/WindowlessIosApplication.h | 159 +++++++---- .../Platform/WindowlessIosApplication.mm | 71 +++-- .../Platform/WindowlessNaClApplication.cpp | 79 +++--- .../Platform/WindowlessNaClApplication.h | 131 +++++++-- .../Platform/WindowlessWglApplication.cpp | 218 ++++++++++----- .../Platform/WindowlessWglApplication.h | 254 +++++++++++------- .../WindowlessWindowsEglApplication.cpp | 121 +++++---- .../WindowlessWindowsEglApplication.h | 231 ++++++++++------ src/Magnum/Platform/magnum-info.cpp | 2 +- src/Magnum/Test/AbstractOpenGLTester.h | 28 +- src/Magnum/Text/fontconverter.cpp | 2 +- .../TextureTools/distancefieldconverter.cpp | 2 +- 19 files changed, 1546 insertions(+), 760 deletions(-) diff --git a/doc/platform.dox b/doc/platform.dox index bc58a29b4..fa02d29c0 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -284,8 +284,6 @@ int main(int argc, char** argv) { } // Delete OpenGL context ... - - return 0; } @endcode @@ -319,6 +317,74 @@ target_link_libraries(myapplication Magnum::Context) @endcode +@section platform-windowless-contexts Manually managing windowless contexts + +In case you need to manage windowless OpenGL contexts manually (for example +to use Magnum for data processing in a thread or when having more than one +OpenGL context), there is a possibility to directly use the context wrappers +from windowless applications. Each @ref WindowlessEglApplication "Windowless*Application" +is accompanied by a @ref WindowlessEglContext "Windowless*Context" class that +manages just GL context creation, making it current and destruction. Similarly +to using custom platform toolkits above, the workflow is to first create a GL +context instance, then making it current and finally instantiating the +@ref Platform::Context instance to initialize Magnum. + +Similarly as with the applications, to simplify the porting, the library +provides `Platform::WindowlessGLContext` typedef, but only if just one +windowless application header is included. + +@attention With this approach it is possible to switch between different GL + contexts, but make sure that Magnum is used only with its OpenGL context. + +@code +int main(int argc, char** argv) { + Platform::WindowlessGLContext glContext{{}}; + glContext.makeCurrent(); + Platform::Context context{argc, argv}; + + // Your GL code ... + + // Make another context current + eglMakeCurrent(); + + // Someone else's code ... + + // Make Magnum context current again + glContext.makeCurrent(); + + // Your GL code again ... + + // Magnum context gets destroyed + // Windowless GL context gets destroyed +} +@endcode + +The main purpose of windowless contexts is threaded OpenGL, used for example +for background data processing. The workflow is to create the windowless +context on the main thread, but make it current in the worker thread. This way +the main thread state isn't affected so it can have any other GL context +current (for example for the main application rendering). + +@note Context creation is not thread safe on all platforms, that's why it still + has to be done on the main thread. + +@code +int main() { + Platform::WindowlessGLContext glContext{{}}; + + std::thread worker{[&glContext]{ + glContext.makeCurrent(); + Platform::Context context{0, nullptr}; + + // Use Magnum here ... + }}; + + // Independent main application code here ... + + worker.join(); +} +@endcode + - Next page: @ref types */ } diff --git a/src/Magnum/Platform/WindowlessCglApplication.cpp b/src/Magnum/Platform/WindowlessCglApplication.cpp index 49140e99b..b43ae3455 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.cpp +++ b/src/Magnum/Platform/WindowlessCglApplication.cpp @@ -35,25 +35,7 @@ namespace Magnum { namespace Platform { -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments): WindowlessCglApplication{arguments, Configuration{}} {} -#endif - -WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessCglApplication{arguments, nullptr} { - createContext(configuration); -} - -WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} - -void WindowlessCglApplication::createContext() { createContext({}); } - -void WindowlessCglApplication::createContext(const Configuration& configuration) { - if(!tryCreateContext(configuration)) std::exit(1); -} - -bool WindowlessCglApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessCglApplication::tryCreateContext(): context already created", false); - +WindowlessCglContext::WindowlessCglContext(const Configuration&, Context*) { int formatCount; CGLPixelFormatAttribute attributes32[] = { kCGLPFAAccelerated, @@ -62,7 +44,7 @@ bool WindowlessCglApplication::tryCreateContext(const Configuration&) { CGLPixelFormatAttribute(0) }; if(CGLChoosePixelFormat(attributes32, &_pixelFormat, &formatCount) != kCGLNoError) { - Error() << "Platform::WindowlessCglApplication::tryCreateContext(): cannot choose pixel format for GL 3.2, falling back to 3.0"; + Warning() << "Platform::WindowlessCglContext: cannot choose pixel format for GL 3.2, falling back to 3.0"; CGLPixelFormatAttribute attributes30[] = { kCGLPFAAccelerated, @@ -71,7 +53,7 @@ bool WindowlessCglApplication::tryCreateContext(const Configuration&) { CGLPixelFormatAttribute(0) }; if(CGLChoosePixelFormat(attributes30, &_pixelFormat, &formatCount) != kCGLNoError) { - Error() << "Platform::WindowlessCglApplication::tryCreateContext(): cannot choose pixel format for GL 3.0, falling back to 2.1"; + Warning() << "Platform::WindowlessCglContext: cannot choose pixel format for GL 3.0, falling back to 2.1"; CGLPixelFormatAttribute attributes21[] = { kCGLPFAAccelerated, @@ -80,31 +62,68 @@ bool WindowlessCglApplication::tryCreateContext(const Configuration&) { CGLPixelFormatAttribute(0) }; if(CGLChoosePixelFormat(attributes21, &_pixelFormat, &formatCount) != kCGLNoError) { - Error() << "Platform::WindowlessCglApplication::tryCreateContext(): cannot choose pixel format"; - return false; + Error() << "Platform::WindowlessCglContext: cannot choose pixel format"; + return; } } } - if(CGLCreateContext(_pixelFormat, nullptr, &_glContext) != kCGLNoError) { - Error() << "Platform::WindowlessCglApplication::tryCreateContext(): cannot create context"; - return false; - } + if(CGLCreateContext(_pixelFormat, nullptr, &_context) != kCGLNoError) + Error() << "Platform::WindowlessCglContext: cannot create context"; +} - if(CGLSetCurrentContext(_glContext) != kCGLNoError) { - Error() << "Platform::WindowlessCglApplication::tryCreateContext(): cannot make context current"; - return false; - } +WindowlessCglContext::WindowlessCglContext(WindowlessCglContext&& other): _pixelFormat{other._pixelFormat}, _context{other._context} { + other._pixelFormat = {}; + other._context = {}; +} - /* Return true if the initialization succeeds */ - return _context->tryCreate(); +WindowlessCglContext::~WindowlessCglContext() { + if(_context) CGLDestroyContext(_context); + if(_pixelFormat) CGLDestroyPixelFormat(_pixelFormat); } -WindowlessCglApplication::~WindowlessCglApplication() { - _context.reset(); +WindowlessCglContext& WindowlessCglContext::operator=(WindowlessCglContext&& other) { + using std::swap; + swap(other._pixelFormat, _pixelFormat); + swap(other._context, _context); + return *this; +} + +bool WindowlessCglContext::makeCurrent() { + if(CGLSetCurrentContext(_context) == kCGLNoError) + return true; + + Error() << "Platform::WindowlessCglContext::makeCurrent(): cannot make context current"; + return false; +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments): WindowlessCglApplication{arguments, Configuration{}} {} +#endif + +WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessCglApplication{arguments, NoCreate} { + createContext(configuration); +} + +WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +WindowlessCglApplication::~WindowlessCglApplication() = default; + +void WindowlessCglApplication::createContext() { createContext({}); } + +void WindowlessCglApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessCglApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessCglApplication::tryCreateContext(): context already created", false); + + WindowlessCglContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) + return false; - CGLDestroyContext(_glContext); - CGLDestroyPixelFormat(_pixelFormat); + _glContext = std::move(glContext); + return true; } }} diff --git a/src/Magnum/Platform/WindowlessCglApplication.h b/src/Magnum/Platform/WindowlessCglApplication.h index 693fe8ee8..9acb76868 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.h +++ b/src/Magnum/Platform/WindowlessCglApplication.h @@ -28,12 +28,13 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessCglApplication, macro @ref MAGNUM_WINDOWLESSCGLAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessCglApplication, @ref Magnum::Platform::WindowlessCglContext, macro @ref MAGNUM_WINDOWLESSCGLAPPLICATION_MAIN() */ #include #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include #include #include @@ -44,13 +45,99 @@ namespace Magnum { namespace Platform { /** -@brief Windowless CGL application +@brief Windowless CGL context + +GL context used in @ref WindowlessCglApplication. Does not have any default +framebuffer. It is built if `WITH_WINDOWLESSCGLAPPLICATION` is enabled in +CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessCglContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * If version is not specified in @p configuration, it first tries to + * create core context (OpenGL 3.2+), if that fails, tries OpenGL 3.0+ + * and as a last attempt falls back to compatibility OpenGL 2.1 + * context. + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessCglContext(const Configuration& configuration, Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessCglContext(NoCreateT) {} -Application for offscreen rendering using pure CGL. Does not have any default -framebuffer. + /** @brief Copying is not allowed */ + WindowlessCglContext(const WindowlessCglContext&) = delete; + + /** @brief Move constructor */ + WindowlessCglContext(WindowlessCglContext&& other); + + /** @brief Copying is not allowed */ + WindowlessCglContext& operator=(const WindowlessCglContext&) = delete; + + /** @brief Move assignment */ + WindowlessCglContext& operator=(WindowlessCglContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessCglContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); -This application library is available on desktop OpenGL on OS X. It -is built if `WITH_WINDOWLESSCGLAPPLICATION` is enabled in CMake. + private: + CGLPixelFormatObj _pixelFormat{}; + CGLContextObj _context{}; +}; + +/** +@brief Configuration + +@see @ref WindowlessCglContext(), + @ref WindowlessCglApplication::WindowlessCglApplication(), + @ref WindowlessCglApplication::createContext(), + @ref WindowlessCglApplication::tryCreateContext() +*/ +class WindowlessCglContext::Configuration { + public: + constexpr /*implicit*/ Configuration() {} +}; + +/** +@brief Windowless CGL application + +Application for offscreen rendering using @ref WindowlessCglContext. This +application library is available on desktop OpenGL on OS X. It is built if +`WITH_WINDOWLESSCGLAPPLICATION` is enabled in CMake. ## Bootstrap application @@ -104,7 +191,13 @@ class WindowlessCglApplication { char** argv; /**< @brief Argument values */ }; - class Configuration; + /** + * @brief Configuration + * + * @see @ref WindowlessCglApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessCglContext::Configuration Configuration; /** * @brief Default constructor @@ -115,6 +208,7 @@ class WindowlessCglApplication { * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessCglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -125,13 +219,21 @@ class WindowlessCglApplication { #endif /** - * @brief Constructor + * @brief Construct without creating the context * @param arguments Application arguments * * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessCglApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessCglApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessCglApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessCglApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessCglApplication(const Arguments&, NoCreateT) instead") explicit WindowlessCglApplication(const Arguments& arguments, std::nullptr_t): WindowlessCglApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessCglApplication(const WindowlessCglApplication&) = delete; @@ -166,11 +268,7 @@ class WindowlessCglApplication { * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. - * - * On desktop GL, if version is not specified in @p configuration, the - * application first tries to create core context (OpenGL 3.2+), if - * that fails, tries OpenGL 3.0+ and as a last attempt falls back to - * compatibility OpenGL 2.1 context. + * @see @ref WindowlessCglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -189,23 +287,10 @@ class WindowlessCglApplication { bool tryCreateContext(const Configuration& configuration); private: - CGLContextObj _glContext; - CGLPixelFormatObj _pixelFormat; - + WindowlessCglContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessCglApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessCglApplication::Configuration { - public: - constexpr /*implicit*/ Configuration() {} -}; - /** @hideinitializer @brief Entry point for windowless CGL application @param className Class name @@ -232,6 +317,7 @@ aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessCglApplication WindowlessApplication; +typedef WindowlessCglContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSCGLAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp index 61d61ce22..ca63f7a5e 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -35,30 +35,12 @@ namespace Magnum { namespace Platform { -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments): WindowlessEglApplication{arguments, Configuration{}} {} -#endif - -WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessEglApplication{arguments, nullptr} { - createContext(configuration); -} - -WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} - -void WindowlessEglApplication::createContext() { createContext({}); } - -void WindowlessEglApplication::createContext(const Configuration& configuration) { - if(!tryCreateContext(configuration)) std::exit(1); -} - -bool WindowlessEglApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessEglApplication::tryCreateContext(): context already created", false); - +WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, Context*) { /* Initialize */ _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if(!eglInitialize(_display, nullptr, nullptr)) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); - return false; + return; } const EGLenum api = @@ -70,7 +52,7 @@ bool WindowlessEglApplication::tryCreateContext(const Configuration& configurati ; if(!eglBindAPI(api)) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot bind EGL API:" << Implementation::eglErrorString(eglGetError()); - return false; + return; } /* Choose EGL config */ @@ -87,15 +69,16 @@ bool WindowlessEglApplication::tryCreateContext(const Configuration& configurati #endif EGL_NONE }; + EGLConfig config; EGLint configCount; - if(!eglChooseConfig(_display, attribs, &_config, 1, &configCount)) { + if(!eglChooseConfig(_display, attribs, &config, 1, &configCount)) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get EGL visual config:" << Implementation::eglErrorString(eglGetError()); - return false; + return; } if(!configCount) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): no matching EGL visual config available"; - return false; + return; } const EGLint attributes[] = { @@ -113,25 +96,64 @@ bool WindowlessEglApplication::tryCreateContext(const Configuration& configurati EGL_NONE }; - if(!(_glContext = eglCreateContext(_display, _config, EGL_NO_CONTEXT, attributes))) { + if(!(_context = eglCreateContext(_display, config, EGL_NO_CONTEXT, attributes))) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot create EGL context:" << Implementation::eglErrorString(eglGetError()); - return false; - } - if(!eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, _glContext)) { - Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot make context current:" << Implementation::eglErrorString(eglGetError()); - return false; + return; } +} - /* Return true if the initialization succeeds */ - return _context->tryCreate(); +WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other): _display{other._display}, _context{other._context} { + other._display = {}; + other._context = {}; } -WindowlessEglApplication::~WindowlessEglApplication() { - _context.reset(); +WindowlessEglContext::~WindowlessEglContext() { + if(_context) eglDestroyContext(_display, _context); + if(_display) eglTerminate(_display); +} - eglDestroyContext(_display, _glContext); - eglDestroySurface(_display, EGL_NO_SURFACE); - eglTerminate(_display); +WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext && other) { + using std::swap; + swap(other._display, _display); + swap(other._context, _context); + return *this; } +bool WindowlessEglContext::makeCurrent() { + if(eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, _context)) + return true; + + Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot make context current:" << Implementation::eglErrorString(eglGetError()); + return false; +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments): WindowlessEglApplication{arguments, Configuration{}} {} +#endif + +WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessEglApplication{arguments, NoCreate} { + createContext(configuration); +} + +WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +void WindowlessEglApplication::createContext() { createContext({}); } + +void WindowlessEglApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessEglApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessEglApplication::tryCreateContext(): context already created", false); + + WindowlessEglContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) + return false; + + _glContext = std::move(glContext); + return true; +} + +WindowlessEglApplication::~WindowlessEglApplication() = default; + }} diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index b262ef3f7..c656154b0 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessEglApplication, macro @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessEglApplication, @ref Magnum::Platform::WindowlessEglContext, macro @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() */ #include @@ -41,17 +41,138 @@ #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" namespace Magnum { namespace Platform { +/** +@brief Windowless EGL context + +GL context using EGL without any windowing system, used in +@ref WindowlessEglApplication. Does not have any default framebuffer. It is +built if `WITH_WINDOWLESSEGLAPPLICATION` is enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessEglContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessEglContext(const Configuration& configuration, const Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessEglContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessEglContext(const WindowlessEglContext&) = delete; + + /** @brief Move constructor */ + WindowlessEglContext(WindowlessEglContext&& other); + + /** @brief Copying is not allowed */ + WindowlessEglContext& operator=(const WindowlessEglContext&) = delete; + + /** @brief Move assignment */ + WindowlessEglContext& operator=(WindowlessEglContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessEglContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + EGLDisplay _display{}; + EGLContext _context{}; +}; + +/** +@brief Configuration + +@see @ref WindowlessEglContext(), + @ref WindowlessEglApplication::WindowlessEglApplication(), + @ref WindowlessEglApplication::createContext(), + @ref WindowlessEglApplication::tryCreateContext() +*/ +class WindowlessEglContext::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + */ + enum class Flag: int { + Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR /**< Create debug context */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + + constexpr /*implicit*/ Configuration() {} + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. See also @ref Context::flags(). + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + private: + Flags _flags; +}; + /** @brief Windowless EGL application -Application for offscreen rendering using EGL without any windowing system. -Does not have any default framebuffer. Supported mainly on OpenGL ES drivers, -for desktop OpenGL the only driver that supports this configuration is -NVidia >= 355. See other `Windowless*Application` classes for an alternative. +Application for offscreen rendering using @ref WindowlessEglContext. Supported +mainly on OpenGL ES drivers, for desktop OpenGL the only driver that supports +this configuration is NVidia >= 355. See other `Windowless*Application` classes +for an alternative. It is built if `WITH_WINDOWLESSEGLAPPLICATION` is enabled in CMake. @@ -104,7 +225,13 @@ class WindowlessEglApplication { char** argv; /**< @brief Argument values */ }; - class Configuration; + /** + * @brief Configuration + * + * @see @ref WindowlessEglApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessEglContext::Configuration Configuration; /** * @brief Default constructor @@ -115,6 +242,7 @@ class WindowlessEglApplication { * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessEglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -131,7 +259,15 @@ class WindowlessEglApplication { * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessEglApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessEglApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessEglApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessEglApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessEglApplication(const Arguments&, NoCreateT) instead") explicit WindowlessEglApplication(const Arguments& arguments, std::nullptr_t): WindowlessEglApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessEglApplication(const WindowlessEglApplication&) = delete; @@ -166,6 +302,7 @@ class WindowlessEglApplication { * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessEglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -184,61 +321,10 @@ class WindowlessEglApplication { bool tryCreateContext(const Configuration& configuration); private: - EGLDisplay _display; - EGLConfig _config; - EGLContext _glContext; - + WindowlessEglContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessEglApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessEglApplication::Configuration { - public: - /** - * @brief Context flag - * - * @see @ref Flags, @ref setFlags(), @ref Context::Flag - */ - enum class Flag: int { - Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR /**< Create debug context */ - }; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref Context::Flags - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - typedef Containers::EnumSet Flags; - #else - typedef Containers::EnumSet Flags; - #endif - - constexpr /*implicit*/ Configuration() {} - - /** @brief Context flags */ - Flags flags() const { return _flags; } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is no flag. See also @ref Context::flags(). - */ - Configuration& setFlags(Flags flags) { - _flags = flags; - return *this; - } - - private: - Flags _flags; -}; - /** @hideinitializer @brief Entry point for windowless EGL application @param className Class name @@ -258,6 +344,7 @@ windowless application header is included this macro is also aliased to #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessEglApplication WindowlessApplication; +typedef WindowlessEglContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessGlxApplication.cpp b/src/Magnum/Platform/WindowlessGlxApplication.cpp index 0e1379eee..fcb693f7e 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.cpp +++ b/src/Magnum/Platform/WindowlessGlxApplication.cpp @@ -37,32 +37,15 @@ namespace { enum { None = 0L }; } namespace Magnum { namespace Platform { -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments): WindowlessGlxApplication{arguments, Configuration{}} {} -#endif - -WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration): WindowlessGlxApplication{arguments, nullptr} { - createContext(configuration); -} - -WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} - -void WindowlessGlxApplication::createContext() { createContext({}); } - -void WindowlessGlxApplication::createContext(const Configuration& configuration) { - if(!tryCreateContext(configuration)) std::exit(1); -} - -bool WindowlessGlxApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessGlxApplication::tryCreateContext(): context already created", false); +WindowlessGlxContext::WindowlessGlxContext(const WindowlessGlxContext::Configuration& configuration, Context* const magnumContext) { _display = XOpenDisplay(nullptr); /* Check version */ int major, minor; glXQueryVersion(_display, &major, &minor); if(major == 1 && minor < 4) { - Error() << "Platform::WindowlessGlxApplication::tryCreateContext(): GLX version 1.4 or greater is required"; - return false; + Error() << "Platform::WindowlessGlxContext: GLX version 1.4 or greater is required"; + return; } /* Choose config */ @@ -70,8 +53,8 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration& configurati constexpr static const int fbAttributes[] = { None }; GLXFBConfig* configs = glXChooseFBConfig(_display, DefaultScreen(_display), fbAttributes, &configCount); if(!configCount) { - Error() << "Platform::WindowlessGlxApplication::tryCreateContext(): no supported framebuffer configuration found"; - return false; + Error() << "Platform::WindowlessGlxContext: no supported framebuffer configuration found"; + return; } /* Create pbuffer */ @@ -82,6 +65,10 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration& configurati }; _pbuffer = glXCreatePbuffer(_display, configs[0], pbufferAttributes); + /* Get pointer to proper context creation function */ + const PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast(glXGetProcAddress(reinterpret_cast("glXCreateContextAttribsARB"))); + + /* Optimistically choose core context first */ const GLint contextAttributes[] = { #ifdef MAGNUM_TARGET_GLES #ifdef MAGNUM_TARGET_GLES3 @@ -95,8 +82,6 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration& configurati GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, GLX_CONTEXT_FLAGS_ARB, GLint(configuration.flags()), #else - /* Similarly to what's done in Sdl2Application, try to request core - context first */ GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 1, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, @@ -104,73 +89,122 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration& configurati #endif 0 }; - - const PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast(glXGetProcAddress(reinterpret_cast("glXCreateContextAttribsARB"))); - _glContext = glXCreateContextAttribsARB(_display, configs[0], nullptr, True, contextAttributes); + _context = glXCreateContextAttribsARB(_display, configs[0], nullptr, True, contextAttributes); #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. 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"; - constexpr static const char amdVendorString[] = "ATI Technologies Inc."; - const char* vendorString; - #endif - if(!_glContext - #ifndef CORRADE_TARGET_APPLE - /* We need to make the context current first, sorry about the UGLY code - and HOPEFULLY THERE WON'T BE MORE WORKAROUNDS */ - || (glXMakeContextCurrent(_display, _pbuffer, _pbuffer, _glContext) && - (vendorString = reinterpret_cast(glGetString(GL_VENDOR)), - (std::strncmp(vendorString, nvidiaVendorString, sizeof(nvidiaVendorString)) == 0 || - std::strncmp(vendorString, amdVendorString, sizeof(amdVendorString)) == 0) && - !_context->isDriverWorkaroundDisabled("amd-nv-no-forward-compatible-core-context"))) - #endif - ) { - /* Don't print any warning when doing the NV workaround, because the - bug will be there probably forever */ - if(!_glContext) Warning() - << "Platform::WindowlessGlxApplication::tryCreateContext(): cannot create core context, falling back to compatibility context"; - else { - glXMakeCurrent(_display, None, nullptr); - glXDestroyContext(_display, _glContext); - } + /* Fall back to (forward compatible) GL 2.1 if core context creation fails */ + if(!_context) { + Warning() << "Platform::WindowlessGlxContext: cannot create core context, falling back to compatibility context"; const GLint fallbackContextAttributes[] = { GLX_CONTEXT_FLAGS_ARB, GLint(configuration.flags()), 0 }; + _context = glXCreateContextAttribsARB(_display, configs[0], nullptr, True, fallbackContextAttributes); + + /* Fall back to (forward compatible) GL 2.1 if we are on binary NVidia/AMD + drivers on Linux/Windows. Instead of creating forward-compatible context + with highest available version, they force the version to the one + specified, which is completely useless behavior. */ + } else { + /* We need to make the context current to read out vendor string, so + save the previous values so we can safely revert back without + messing up the state */ + GLXDrawable currentDrawable = glXGetCurrentDrawable(); + GLXDrawable currentReadDrawable = glXGetCurrentReadDrawable(); + GLXContext currentContext = glXGetCurrentContext(); + if(!glXMakeContextCurrent(_display, _pbuffer, _pbuffer, _context)) { + Error() << "Platform::WindowlessGlxContext: cannot make context current"; + return; + } - _glContext = glXCreateContextAttribsARB(_display, configs[0], nullptr, True, fallbackContextAttributes); + /* The workaround check is the last so it doesn't appear in workaround + list on unrelated drivers */ + constexpr static const char nvidiaVendorString[] = "NVIDIA Corporation"; + constexpr static const char amdVendorString[] = "ATI Technologies Inc."; + const char* const vendorString = reinterpret_cast(glGetString(GL_VENDOR)); + if((std::strncmp(vendorString, nvidiaVendorString, sizeof(nvidiaVendorString)) == 0 || + std::strncmp(vendorString, amdVendorString, sizeof(amdVendorString)) == 0) && + (!magnumContext || !magnumContext->isDriverWorkaroundDisabled("amd-nv-no-forward-compatible-core-context"))) + { + /* Destroy the core context and create a compatibility one */ + glXDestroyContext(_display, _context); + const GLint fallbackContextAttributes[] = { + GLX_CONTEXT_FLAGS_ARB, GLint(configuration.flags()), + 0 + }; + _context = glXCreateContextAttribsARB(_display, configs[0], nullptr, True, fallbackContextAttributes); + } + + /* Revert back the old context */ + if(!glXMakeContextCurrent(_display, currentDrawable, currentReadDrawable, currentContext)) { + Error() << "Platform::WindowlessGlxContext: cannot make the previous context current"; + return; + } } #endif XFree(configs); - if(!_glContext) { - Error() << "Platform::WindowlessGlxApplication::tryCreateContext(): cannot create context"; - return false; - } + if(!_context) + Error() << "Platform::WindowlessGlxContext: cannot create context"; +} - /* Set OpenGL context as current */ - if(!glXMakeContextCurrent(_display, _pbuffer, _pbuffer, _glContext)) { - Error() << "Platform::WindowlessGlxApplication::tryCreateContext(): cannot make context current"; - return false; - } +WindowlessGlxContext::WindowlessGlxContext(WindowlessGlxContext&& other): _display{other._display}, _pbuffer{other._pbuffer}, _context{other._context} { + other._display = {}; + other._context = {}; + other._pbuffer = {}; +} + +WindowlessGlxContext::~WindowlessGlxContext() { + if(_context) glXDestroyContext(_display, _context); + if(_pbuffer) glXDestroyPbuffer(_display, _pbuffer); + if(_display) XCloseDisplay(_display); +} - /* Return true if the initialization succeeds */ - return _context->tryCreate(); +WindowlessGlxContext& WindowlessGlxContext::operator=(WindowlessGlxContext&& other) { + using std::swap; + swap(other._display, _display); + swap(other._pbuffer, _pbuffer); + swap(other._context, _context); + return *this; } -WindowlessGlxApplication::~WindowlessGlxApplication() { - _context.reset(); +bool WindowlessGlxContext::makeCurrent() { + if(glXMakeContextCurrent(_display, _pbuffer, _pbuffer, _context)) + return true; - glXMakeCurrent(_display, None, nullptr); - glXDestroyPbuffer(_display, _pbuffer); - glXDestroyContext(_display, _glContext); + Error() << "Platform::WindowlessGlxContext::makeCurrent(): cannot make context current"; + return false; } +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments): WindowlessGlxApplication{arguments, Configuration{}} {} +#endif + +WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration): WindowlessGlxApplication{arguments, NoCreate} { + createContext(configuration); +} + +WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +void WindowlessGlxApplication::createContext() { createContext({}); } + +void WindowlessGlxApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessGlxApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessGlxApplication::tryCreateContext(): context already created", false); + + WindowlessGlxContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) + return false; + + _glContext = std::move(glContext); + return true; +} + +WindowlessGlxApplication::~WindowlessGlxApplication() = default; + }} diff --git a/src/Magnum/Platform/WindowlessGlxApplication.h b/src/Magnum/Platform/WindowlessGlxApplication.h index 0e7325957..5821aae9d 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.h +++ b/src/Magnum/Platform/WindowlessGlxApplication.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessGlxApplication, macro @ref MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessGlxApplication, @ref Magnum::Platform::WindowlessGlxContext, macro @ref MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN() */ #include @@ -43,16 +43,147 @@ #undef Status #include "Magnum/Magnum.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" namespace Magnum { namespace Platform { /** -@brief Windowless GLX application +@brief Windowless GLX context + +GL context using pure X11 and GLX, used in @ref WindowlessGlxApplication. Does +not have any default framebuffer. It is built if `WITH_WINDOWLESSGLXAPPLICATION` +is enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessGlxContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * On desktop GL, if version is not specified in @p configuration, the + * application first tries to create core context (OpenGL 3.1+) and if + * that fails, falls back to compatibility OpenGL 2.1 context. However, + * on binary AMD and NVidia drivers, creating core context does not use + * the largest available version. If the application detects such case + * (and given workaround is not disabled in optionally passed + * @ref context instance), the core context is destroyed and + * compatibility OpenGL 2.1 context is created instead to make the + * driver use the latest available version. + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessGlxContext(const Configuration& configuration, Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessGlxContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessGlxContext(const WindowlessGlxContext&) = delete; + + /** @brief Move constructor */ + WindowlessGlxContext(WindowlessGlxContext&& other); + + /** @brief Copying is not allowed */ + WindowlessGlxContext& operator=(const WindowlessGlxContext&) = delete; + + /** @brief Move assignment */ + WindowlessGlxContext& operator=(WindowlessGlxContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessGlxContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + Display* _display{}; + GLXPbuffer _pbuffer{}; + GLXContext _context{}; +}; + +/** +@brief Configuration + +@see @ref WindowlessGlxContext(), + @ref WindowlessGlxApplication::WindowlessGlxApplication(), + @ref WindowlessGlxApplication::createContext(), + @ref WindowlessGlxApplication::tryCreateContext() +*/ +class WindowlessGlxContext::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + */ + enum class Flag: int { + Debug = GLX_CONTEXT_DEBUG_BIT_ARB /**< Create debug context */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + + constexpr /*implicit*/ Configuration() {} + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. See also @ref Context::flags(). + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + private: + Flags _flags; +}; -Application for offscreen rendering using pure X11 and GLX. +/** +@brief Windowless GLX application -This application library is available on desktop OpenGL and +Application for offscreen rendering using @ref WindowlessGlxContext. This +application library is available on desktop OpenGL and @ref MAGNUM_TARGET_DESKTOP_GLES "OpenGL ES emulation on desktop" on Linux. It depends on **X11** library and is built if `WITH_WINDOWLESSGLXAPPLICATION` is enabled in CMake. @@ -107,7 +238,13 @@ class WindowlessGlxApplication { char** argv; /**< @brief Argument values */ }; - class Configuration; + /** + * @brief Configuration + * + * @see @ref WindowlessGlxApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessGlxContext::Configuration Configuration; /** * @brief Default constructor @@ -118,6 +255,7 @@ class WindowlessGlxApplication { * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessGlxContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -134,7 +272,15 @@ class WindowlessGlxApplication { * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessGlxApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessGlxApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessGlxApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessGlxApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessGlxApplication(const Arguments&, NoCreateT) instead") explicit WindowlessGlxApplication(const Arguments& arguments, std::nullptr_t): WindowlessGlxApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessGlxApplication(const WindowlessGlxApplication&) = delete; @@ -169,15 +315,7 @@ class WindowlessGlxApplication { * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. - * - * On desktop GL, if version is not specified in @p configuration, the - * application first tries to create core context (OpenGL 3.1+) and if - * that fails, falls back to compatibility OpenGL 2.1 context. However, - * on binary AMD and NVidia drivers, creating core context does not use - * the largest available version. If the application detects such case, - * the core context is destroyed and compatibility OpenGL 2.1 context - * is created instead to make the driver use the latest available - * version. + * @see @ref WindowlessGlxContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -196,61 +334,10 @@ class WindowlessGlxApplication { bool tryCreateContext(const Configuration& configuration); private: - Display* _display; - GLXContext _glContext; - GLXPbuffer _pbuffer; - + WindowlessGlxContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessGlxApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessGlxApplication::Configuration { - public: - /** - * @brief Context flag - * - * @see @ref Flags, @ref setFlags(), @ref Context::Flag - */ - enum class Flag: int { - Debug = GLX_CONTEXT_DEBUG_BIT_ARB /**< Create debug context */ - }; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref Context::Flags - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - typedef Containers::EnumSet Flags; - #else - typedef Containers::EnumSet Flags; - #endif - - constexpr /*implicit*/ Configuration() {} - - /** @brief Context flags */ - Flags flags() const { return _flags; } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is no flag. See also @ref Context::flags(). - */ - Configuration& setFlags(Flags flags) { - _flags = flags; - return *this; - } - - private: - Flags _flags; -}; - /** @hideinitializer @brief Entry point for windowless GLX application @param className Class name @@ -277,6 +364,7 @@ aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessGlxApplication WindowlessApplication; +typedef WindowlessGlxContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessIosApplication.h b/src/Magnum/Platform/WindowlessIosApplication.h index 459afc39a..b870c5ab4 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.h +++ b/src/Magnum/Platform/WindowlessIosApplication.h @@ -3,7 +3,7 @@ /* This file is part of Magnum. - Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessIosApplication, macro @ref MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessIosApplication, @ref Magnum::Platform::WindowlessIosContext, macro @ref MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN() */ #include @@ -34,6 +34,7 @@ #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" #ifdef __OBJC__ @@ -44,12 +45,94 @@ struct EAGLContext; namespace Magnum { namespace Platform { +/** +@brief Windowless iOS context + +GL context using EAGL on iOS, used in @ref WindowlessIosApplication. Does not +have any default framebuffer. It is built if `WITH_WINDOWLESSIOSAPPLICATION` is +enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessIosContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessIosContext(const Configuration& configuration, Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessIosContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessIosContext(const WindowlessIosContext&) = delete; + + /** @brief Move constructor */ + WindowlessIosContext(WindowlessIosContext&& other); + + /** @brief Copying is not allowed */ + WindowlessIosContext& operator=(const WindowlessIosContext&) = delete; + + /** @brief Move assignment */ + WindowlessIosContext& operator=(WindowlessIosContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessIosContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + EAGLContext* _context{}; +}; + +/** +@brief Configuration + +@see @ref WindowlessIosContext(), + @ref WindowlessIosApplication::WindowlessCglApplication(), + @ref WindowlessIosApplication::createContext(), + @ref WindowlessIosApplication::tryCreateContext() +*/ +class WindowlessIosContext::Configuration { + public: + constexpr /*implicit*/ Configuration() {} +}; + /** @brief Windowless iOS application -Application for offscreen rendering using EAGL on iOS. Does not have any -default framebuffer. It is built if `WITH_WINDOWLESSIOSAPPLICATION` is enabled -in CMake. +Application for offscreen rendering using @ref WindowlessIosContext. Does not +have any default framebuffer. It is built if `WITH_WINDOWLESSIOSAPPLICATION` is +enabled in CMake. ## Bootstrap application @@ -100,7 +183,13 @@ class WindowlessIosApplication { char** argv; /**< @brief Argument values */ }; - class Configuration; + /** + * @brief Configuration + * + * @see @ref WindowlessIosApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessIosContext::Configuration Configuration; /** * @brief Default constructor @@ -111,6 +200,7 @@ class WindowlessIosApplication { * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessIosContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -127,7 +217,15 @@ class WindowlessIosApplication { * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessIosApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessIosApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessIosApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessIosApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessIosApplication(const Arguments&, NoCreateT) instead") explicit WindowlessIosApplication(const Arguments& arguments, std::nullptr_t): WindowlessIosApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessIosApplication(const WindowlessIosApplication&) = delete; @@ -162,6 +260,7 @@ class WindowlessIosApplication { * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessIosContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -180,53 +279,10 @@ class WindowlessIosApplication { bool tryCreateContext(const Configuration& configuration); private: - EAGLContext* _glContext; - + WindowlessIosContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessIosApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessIosApplication::Configuration { - public: - /** - * @brief Context flag - * - * @see @ref Flags, @ref setFlags(), @ref Context::Flag - */ - enum class Flag: int {}; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref Context::Flags - */ - typedef Containers::EnumSet Flags; - - constexpr /*implicit*/ Configuration() {} - - /** @brief Context flags */ - Flags flags() const { return _flags; } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is no flag. See also @ref Context::flags(). - */ - Configuration& setFlags(Flags flags) { - _flags = flags; - return *this; - } - - private: - Flags _flags; -}; - /** @hideinitializer @brief Entry point for windowless EGL application @param className Class name @@ -246,6 +302,7 @@ windowless application header is included this macro is also aliased to #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessIosApplication WindowlessApplication; +typedef WindowlessIosContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessIosApplication.mm b/src/Magnum/Platform/WindowlessIosApplication.mm index 921f5d261..172e9273d 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.mm +++ b/src/Magnum/Platform/WindowlessIosApplication.mm @@ -1,7 +1,7 @@ /* This file is part of Magnum. - Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a @@ -39,15 +39,50 @@ namespace Magnum { namespace Platform { +WindowlessIosContext::WindowlessIosContext(const Configuration&, Context*) { + if(!(_context = [[EAGLContext alloc] + #ifdef MAGNUM_TARGET_GLES2 + initWithAPI:kEAGLRenderingAPIOpenGLES2 + #else + initWithAPI:kEAGLRenderingAPIOpenGLES3 + #endif + ])) + { + Error() << "Platform::WindowlessIosContext(): cannot create EAGL context"; + } +} + +WindowlessIosContext::WindowlessIosContext(WindowlessIosContext&& other): _context{other._context} { + other._context = {}; +} + +WindowlessIosContext::~WindowlessIosContext() { + if(_context) [_context dealloc]; +} + +WindowlessIosContext& WindowlessIosContext::operator=(WindowlessIosContext&& other) { + using std::swap; + swap(other._context, _context); + return *this; +} + +bool WindowlessIosContext::makeCurrent() { + if([EAGLContext setCurrentContext:_context]) + return true; + + Error() << "Platform::WindowlessIosContext::makeCurrent(): cannot make context current"; + return false; +} + #ifndef DOXYGEN_GENERATING_OUTPUT WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments): WindowlessIosApplication{arguments, Configuration{}} {} #endif -WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration): WindowlessIosApplication{arguments, nullptr} { +WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration): WindowlessIosApplication{arguments, NoCreate} { createContext(configuration); } -WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} +WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} void WindowlessIosApplication::createContext() { createContext({}); } @@ -55,35 +90,17 @@ void WindowlessIosApplication::createContext(const Configuration& configuration) if(!tryCreateContext(configuration)) std::exit(1); } -bool WindowlessIosApplication::tryCreateContext(const Configuration&) { +bool WindowlessIosApplication::tryCreateContext(const Configuration& configuration) { CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessIosApplication::tryCreateContext(): context already created", false); - /* Initialize */ - if(!(_glContext = [[EAGLContext alloc] - #ifdef MAGNUM_TARGET_GLES2 - initWithAPI:kEAGLRenderingAPIOpenGLES2 - #else - initWithAPI:kEAGLRenderingAPIOpenGLES3 - #endif - ])) - { - Error() << "Platform::WindowlessIosApplication::tryCreateContext(): cannot create EAGL context"; - return false; - } - - if(![EAGLContext setCurrentContext:_glContext]) { - Error() << "Platform::WindowlessIosApplication::tryCreateContext(): cannot make context current"; + WindowlessIosContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) return false; - } - /* Return true if the initialization succeeds */ - return _context->tryCreate(); + _glContext = std::move(glContext); + return true; } -WindowlessIosApplication::~WindowlessIosApplication() { - _context.reset(); - - [_glContext dealloc]; -} +WindowlessIosApplication::~WindowlessIosApplication() = default; }} diff --git a/src/Magnum/Platform/WindowlessNaClApplication.cpp b/src/Magnum/Platform/WindowlessNaClApplication.cpp index fd4194d33..22f0cdbce 100644 --- a/src/Magnum/Platform/WindowlessNaClApplication.cpp +++ b/src/Magnum/Platform/WindowlessNaClApplication.cpp @@ -34,6 +34,40 @@ namespace Magnum { namespace Platform { +WindowlessNaClContext::WindowlessNaClContext(pp::Instance& instance, const Configuration&, Context*) { + const std::int32_t attributes[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, + PP_GRAPHICS3DATTRIB_WIDTH, 1, + PP_GRAPHICS3DATTRIB_HEIGHT, 1, + PP_GRAPHICS3DATTRIB_NONE + }; + + std::unique_ptr context{new pp::Graphics3D{*instance, attributes}}; + if(context->is_null()) + Error() << "Platform::WindowlessNaClContext: cannot create context"; + + if(!BindGraphics(*context)) + Error() << "Platform::WindowlessNaClContext: cannot bind graphics"; + + /* All went well, save the context */ + _context = std::move(context); +} + +WindowlessNaClContext::WindowlessNaClContext(WindowlessNaClContext&&) = default; + +WindowlessNaClContext::~WindowlessEglContext() = default; + +WindowlessNaClContext& WindowlessNaClContext::operator=(WindowlessNaClContext&&) = default; + +bool WindowlessNaClContext::makeCurrent() { + if(!_context) return false; + + glSetCurrentContextPPAPI(_context->pp_resource()); + return true; +} + struct WindowlessNaClApplication::ConsoleDebugOutput { explicit ConsoleDebugOutput(pp::Instance* instance); @@ -55,13 +89,11 @@ WindowlessNaClApplication::WindowlessNaClApplication(const Arguments& arguments) #endif WindowlessNaClApplication::WindowlessNaClApplication(const Arguments& arguments, const Configuration& configuration): -WindowlessNaClApplication{arguments, nullptr} { +WindowlessNaClApplication{arguments, NoCreate} { createContext(configuration); } -WindowlessNaClApplication::WindowlessNaClApplication(const Arguments& arguments, std::nullptr_t): Instance(arguments), Graphics3DClient(this), graphics(nullptr), c(nullptr) { - debugOutput = new ConsoleDebugOutput(this); -} +WindowlessNaClApplication::WindowlessNaClApplication(const Arguments& arguments, NoCreate): Instance(arguments), Graphics3DClient(this), _glContext{NoCreate}, _debugOutput{new ConsoleDebugOutput{this}} {} void WindowlessNaClApplication::createContext() { createContext({}); } @@ -69,43 +101,18 @@ void WindowlessNaClApplication::createContext(const Configuration& configuration if(!tryCreateContext(configuration)) std::exit(1); } -bool WindowlessNaClApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(!c, "Platform::WindowlessNaClApplication::tryCreateContext(): context already created", false); - - const std::int32_t attributes[] = { - PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, - PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, - PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, - PP_GRAPHICS3DATTRIB_WIDTH, 1, - PP_GRAPHICS3DATTRIB_HEIGHT, 1, - PP_GRAPHICS3DATTRIB_NONE - }; +bool WindowlessNaClApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessNaClApplication::tryCreateContext(): context already created", false); - graphics = new pp::Graphics3D(this, attributes); - if(graphics->is_null()) { - Error() << "Platform::WindowlessNaClApplication::tryCreateContext(): cannot create context"; - delete graphics; - graphics = nullptr; - return false; - } - if(!BindGraphics(*graphics)) { - Error() << "Platform::WindowlessNaClApplication::tryCreateContext(): cannot bind graphics"; - delete graphics; - graphics = nullptr; + WindowlessNaClContext glContext{*this, configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) return false; - } - glSetCurrentContextPPAPI(graphics->pp_resource()); - - /* Return true if the initialization succeeds */ - return c = Platform::Context::tryCreate().release(); + _glContext = std::move(glContext); + return true; } -WindowlessNaClApplication::~WindowlessNaClApplication() { - delete c; - delete graphics; - delete debugOutput; -} +WindowlessNaClApplication::~WindowlessNaClApplication() = default; bool WindowlessNaClApplication::Init(uint32_t , const char* , const char*) { return exec() == 0; diff --git a/src/Magnum/Platform/WindowlessNaClApplication.h b/src/Magnum/Platform/WindowlessNaClApplication.h index b130f475a..fba8b8e62 100644 --- a/src/Magnum/Platform/WindowlessNaClApplication.h +++ b/src/Magnum/Platform/WindowlessNaClApplication.h @@ -26,9 +26,10 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessNaClApplication, macro @ref MAGNUM_WINDOWLESSNACLAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessNaClApplication, @ref Magnum::Platform::WindowlessNaClContext, macro @ref MAGNUM_WINDOWLESSNACLAPPLICATION_MAIN() */ +#include #include #include @@ -41,6 +42,7 @@ #include #include "Magnum/Magnum.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" namespace pp { @@ -50,13 +52,94 @@ namespace pp { namespace Magnum { namespace Platform { +/** +@brief Windowless NaCl context + +GL context running in [Google Chrome Native Client](https://developers.google.com/native-client/), +used in @ref WindowlessNaClApplication. Does not have any default framebuffer. +It is built if `WITH_WINDOWLESSNACLAPPLICATION` is enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessNaClContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param instance Pepper instance handle + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessNaClContext(pp::Instance& instance, const Configuration& configuration, const Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessNaClContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessNaClContext(const WindowlessNaClContext&) = delete; + + /** @brief Move constructor */ + WindowlessNaClContext(WindowlessNaClContext&&); + + /** @brief Copying is not allowed */ + WindowlessNaClContext& operator=(const WindowlessNaClContext&) = delete; + + /** @brief Move assignment */ + WindowlessNaClContext& operator=(WindowlessNaClContext&&); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessNaClContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + std::unique_ptr _context; +}; + +/** +@brief Configuration + +@see @ref WindowlessNaClContext(), + @ref WindowlessNaClApplication::WindowlessCglApplication(), + @ref WindowlessNaClApplication::createContext(), + @ref WindowlessNaClApplication::tryCreateContext() +*/ +class WindowlessNaClContext::Configuration { + public: + constexpr /*implicit*/ Configuration() {} +}; + /** @nosubgrouping @brief Windowless NaCl application -Application for offscreen rendering running in -[Google Chrome Native Client](https://developers.google.com/native-client/). - -This application library is available only in @ref CORRADE_TARGET_NACL "Native Client", +Application for offscreen rendering using @ref WindowlessNaClContext. This +application library is available only in @ref CORRADE_TARGET_NACL "Native Client", see respective sections in @ref building-corrade-cross-nacl "Corrade's" and @ref building-cross-nacl "Magnum's" building documentation. It is built if `WITH_WINDOWLESSNACLAPPLICATION` is enabled in CMake. @@ -111,7 +194,13 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien /** @brief Application arguments */ typedef PP_Instance Arguments; - class Configuration; + /** + * @brief Configuration + * + * @see @ref WindowlessNaClApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessNaClContext::Configuration Configuration; /** * @brief Default constructor @@ -122,6 +211,7 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessNaClContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessNaClApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -138,7 +228,15 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessNaClApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessNaClApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessNaClApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessNaClApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessNaClApplication(const Arguments&, NoCreateT) instead") explicit WindowlessNaClApplication(const Arguments& arguments, std::nullptr_t): WindowlessNaClApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessNaClApplication(const WindowlessNaClApplication&) = delete; @@ -178,6 +276,7 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessNaClContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -202,20 +301,9 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien bool Init(std::uint32_t, const char*, const char*) override; - pp::Graphics3D* graphics; - Platform::Context* c; - ConsoleDebugOutput* debugOutput; -}; - -/** -@brief Configuration - -@see @ref WindowlessNaClApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessNaClApplication::Configuration { - public: - constexpr /*implicit*/ Configuration() {} + WindowlessNaClContext _glContext; + std::unique_ptr _context; + std::unique_ptr _debugOutput; }; namespace Implementation { @@ -256,6 +344,7 @@ application header is included this macro is also aliased to #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessNaClApplication WindowlessApplication; +typedef WindowlessNaClContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSNACLAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp index 72597d12d..75845496e 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -25,19 +25,31 @@ #include "WindowlessWglApplication.h" +#include #include #include #include "Magnum/Version.h" #include "Magnum/Platform/Context.h" +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Define stuff that we need because I can't be bothered with creating a new + header just for a few defines */ +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#endif + namespace Magnum { namespace Platform { -#ifndef DOXYGEN_GENERATING_OUTPUT -int WindowlessWglApplication::create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)) { +WindowlessWglContext::WindowlessWglContext(const Configuration& configuration, Context* const magnumContext) { + /* Create the window */ const WNDCLASS wc{ 0, - windowProcedure, + DefWindowProc, 0, 0, GetModuleHandle(nullptr), @@ -47,37 +59,10 @@ int WindowlessWglApplication::create(LRESULT(CALLBACK windowProcedure)(HWND, UIN nullptr, L"Magnum Windowless Application" }; - if(!RegisterClass(&wc)) return 1; - - CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application", + if(!RegisterClass(&wc)) return; + _window = CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application", WS_OVERLAPPEDWINDOW, 0, 0, 32, 32, 0, 0, wc.hInstance, 0); - /* Hammer the return code out of the messaging thingy */ - MSG msg; - do {} while(GetMessageW(&msg, nullptr, 0, 0) != 0); - return msg.wParam; -} -#endif - -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments): WindowlessWglApplication{arguments, Configuration{}} {} -#endif - -WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWglApplication{arguments, nullptr} { - createContext(configuration); -} - -WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window), _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} - -void WindowlessWglApplication::createContext() { createContext({}); } - -void WindowlessWglApplication::createContext(const Configuration& configuration) { - if(!tryCreateContext(configuration)) std::exit(1); -} - -bool WindowlessWglApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWglApplication::tryCreateContext(): context already created", false); - /* Get device context */ _deviceContext = GetDC(_window); @@ -104,49 +89,160 @@ bool WindowlessWglApplication::tryCreateContext(const Configuration& configurati const int pixelFormat = ChoosePixelFormat(_deviceContext, &pfd); SetPixelFormat(_deviceContext, pixelFormat, &pfd); - const int attributes[] = { - WGL_CONTEXT_FLAGS_ARB, int(configuration.flags()), - 0 - }; - /* Create temporary context so we are able to get the pointer to - wglCreateContextAttribsARB() */ - HGLRC temporaryContext = wglCreateContext(_deviceContext); + wglCreateContextAttribsARB(). To avoid messing up the app state we need + to save the old active context and then restore it later. */ + const HGLRC currentContext = wglGetCurrentContext(); + const HGLRC temporaryContext = wglCreateContext(_deviceContext); if(!wglMakeCurrent(_deviceContext, temporaryContext)) { - Error() << "Platform::WindowlessWglApplication::tryCreateContext(): cannot make temporary context current:" << GetLastError(); - return false; + Error() << "Platform::WindowlessWglContext: cannot make temporary context current:" << GetLastError(); + wglDeleteContext(temporaryContext); + return; } - /* Get pointer to proper context creation function and create real context - with it */ + /* Get pointer to proper context creation function */ typedef HGLRC(WINAPI*PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*); const PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = reinterpret_cast( wglGetProcAddress(reinterpret_cast("wglCreateContextAttribsARB"))); - _renderingContext = wglCreateContextAttribsARB(_deviceContext, nullptr, attributes); - /* Delete the temporary context */ - wglMakeCurrent(_deviceContext, nullptr); - wglDeleteContext(temporaryContext); - - if(!_renderingContext) { - Error() << "Platform::WindowlessWglApplication::tryCreateContext(): cannot create context:" << GetLastError(); - return false; + /* Optimistically choose core context first */ + const GLint contextAttributes[] = { + #ifdef MAGNUM_TARGET_GLES + #ifdef MAGNUM_TARGET_GLES3 + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + #elif defined(MAGNUM_TARGET_GLES2) + WGL_CONTEXT_MAJOR_VERSION_ARB, 2, + #else + #error unsupported OpenGL ES version + #endif + WGL_CONTEXT_MINOR_VERSION_ARB, 0, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES2_PROFILE_BIT_EXT, + WGL_CONTEXT_FLAGS_ARB, GLint(configuration.flags()), + #else + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 1, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB|GLint(configuration.flags()), + #endif + 0 + }; + _context = wglCreateContextAttribsARB(_deviceContext, nullptr, contextAttributes); + + #ifndef MAGNUM_TARGET_GLES + /* Fall back to (forward compatible) GL 2.1 if core context creation fails */ + if(!_context) { + Warning() << "Platform::WindowlessWglContext: cannot create core context, falling back to compatibility context:" << GetLastError(); + + const int fallbackContextAttributes[] = { + WGL_CONTEXT_FLAGS_ARB, int(configuration.flags()), + 0 + }; + _context = wglCreateContextAttribsARB(_deviceContext, nullptr, fallbackContextAttributes); + + /* Fall back to (forward compatible) GL 2.1 if we are on binary NVidia/AMD + drivers on Linux/Windows. Instead of creating forward-compatible context + with highest available version, they force the version to the one + specified, which is completely useless behavior. */ + } else { + /* We need to make the context current to read out vendor string */ + if(!wglMakeCurrent(_deviceContext, _context)) { + Error() << "Platform::WindowlessWglContext: cannot make context current:" << GetLastError(); + + /* Everything failed, at least try to delete the dangling contexts + and revert to the previous context to regain some sanity */ + wglMakeCurrent(_deviceContext, currentContext); + wglDeleteContext(temporaryContext); + return; + } + + /* The workaround check is the last so it doesn't appear in workaround + list on unrelated drivers */ + constexpr static const char nvidiaVendorString[] = "NVIDIA Corporation"; + constexpr static const char amdVendorString[] = "ATI Technologies Inc."; + const char* const vendorString = reinterpret_cast(glGetString(GL_VENDOR)); + if((std::strncmp(vendorString, nvidiaVendorString, sizeof(nvidiaVendorString)) == 0 || + std::strncmp(vendorString, amdVendorString, sizeof(amdVendorString)) == 0) && + (!magnumContext || !magnumContext->isDriverWorkaroundDisabled("amd-nv-no-forward-compatible-core-context"))) + { + /* Destroy the core context and create a compatibility one */ + wglDeleteContext(_context); + const int fallbackContextAttributes[] = { + WGL_CONTEXT_FLAGS_ARB, int(configuration.flags()), + 0 + }; + _context = wglCreateContextAttribsARB(_deviceContext, nullptr, fallbackContextAttributes); + } } + #endif - /* Set OpenGL context as current */ - if(!wglMakeCurrent(_deviceContext, _renderingContext)) { - Error() << "Platform::WindowlessWglApplication::tryCreateContext(): cannot make context current:" << GetLastError(); - return false; + /* Make the previous context active and delete the temporary context */ + if(!wglMakeCurrent(_deviceContext, currentContext)) { + Error() << "Platform::WindowlessWglContext: cannot make the previous context current:" << GetLastError(); + + /* Everything is fucked up, but try to delete the temporary context + anyway */ + wglDeleteContext(temporaryContext); + return; } + wglDeleteContext(temporaryContext); + + if(!_context) + Error() << "Platform::WindowlessWglContext: cannot create context:" << GetLastError(); +} + +WindowlessWglContext::WindowlessWglContext(WindowlessWglContext&& other): _window{other._window}, _deviceContext{other._deviceContext}, _context{other._context} { + other._window = {}; + other._deviceContext = {}; + other._context = {}; +} + +WindowlessWglContext::~WindowlessWglContext() { + if(_context) wglDeleteContext(_context); + if(_window) DestroyWindow(_window); +} + +WindowlessWglContext& WindowlessWglContext::operator=(WindowlessWglContext&& other) { + using std::swap; + swap(other._window, _window); + swap(other._deviceContext, _deviceContext); + swap(other._context, _context); + return *this; +} + +bool WindowlessWglContext::makeCurrent() { + if(wglMakeCurrent(_deviceContext, _context)) + return true; + + Error() << "Platform::WindowlessWglContext::makeCurrent(): cannot make context current:" << GetLastError(); + return false; +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments): WindowlessWglApplication{arguments, Configuration{}} {} +#endif + +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWglApplication{arguments, NoCreate} { + createContext(configuration); +} + +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +void WindowlessWglApplication::createContext() { createContext({}); } - /* Return true if the initialization succeeds */ - return _context->tryCreate(); +void WindowlessWglApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); } -WindowlessWglApplication::~WindowlessWglApplication() { - _context.reset(); +bool WindowlessWglApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWglApplication::tryCreateContext(): context already created", false); + + WindowlessWglContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) + return false; - wglMakeCurrent(_deviceContext, nullptr); - wglDeleteContext(_renderingContext); + _glContext = std::move(glContext); + return true; } +WindowlessWglApplication::~WindowlessWglApplication() = default; + }} diff --git a/src/Magnum/Platform/WindowlessWglApplication.h b/src/Magnum/Platform/WindowlessWglApplication.h index 0b5b4f6d4..2f91b2558 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.h +++ b/src/Magnum/Platform/WindowlessWglApplication.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessWglApplication, macro @ref MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessWglApplication, @ref Magnum::Platform::WindowlessWglContext, macro @ref MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN() */ #include @@ -39,24 +39,153 @@ #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" #ifndef DOXYGEN_GENERATING_OUTPUT /* Define stuff that we need because I can't be bothered with creating a new - header just for two defines */ -#define WGL_CONTEXT_FLAGS_ARB 0x2094 + header just for a few defines */ #define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 #endif namespace Magnum { namespace Platform { /** -@brief Windowless WGL application +@brief Windowless WGL context + +GL context using pure WINAPI, used in @ref WindowlessWglApplication. It is +built if `WITH_WINDOWLESSWGLAPPLICATION` is enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessWglContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * On desktop GL, if version is not specified in @p configuration, the + * application first tries to create core context (OpenGL 3.1+) and if + * that fails, falls back to compatibility OpenGL 2.1 context. However, + * on binary AMD and NVidia drivers, creating core context does not use + * the largest available version. If the application detects such case + * (and given workaround is not disabled in optionally passed + * @ref context instance), the core context is destroyed and + * compatibility OpenGL 2.1 context is created instead to make the + * driver use the latest available version. + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessWglContext(const Configuration& configuration, Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessWglContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessWglContext(const WindowlessWglContext&) = delete; + + /** @brief Move constructor */ + WindowlessWglContext(WindowlessWglContext&& other); + + /** @brief Copying is not allowed */ + WindowlessWglContext& operator=(const WindowlessWglContext&) = delete; + + /** @brief Move assignment */ + WindowlessWglContext& operator=(WindowlessWglContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessWglContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + HWND _window{}; + HDC _deviceContext{}; + HGLRC _context{}; +}; -Application for offscreen rendering using pure WINAPI. +/** +@brief Configuration -This application library is available on desktop OpenGL on Windows. It is built -if `WITH_WINDOWLESSWGLAPPLICATION` is enabled in CMake. +@see @ref WindowlessWglContext(), + @ref WindowlessWglApplication::WindowlessWglApplication(), + @ref WindowlessWglApplication::createContext(), + @ref WindowlessWglApplication::tryCreateContext() +*/ +class WindowlessWglContext::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + */ + enum class Flag: int { + Debug = WGL_CONTEXT_DEBUG_BIT_ARB /**< Create debug context */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + + constexpr /*implicit*/ Configuration() {} + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. See also @ref Context::flags(). + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + private: + Flags _flags; +}; + +/** +@brief Windowless WGL application + +Application for offscreen rendering using @ref WindowlessWglContext. This +application library is available on desktop OpenGL on Windows. It is built if +`WITH_WINDOWLESSWGLAPPLICATION` is enabled in CMake. ## Bootstrap application @@ -102,20 +231,19 @@ class WindowlessWglApplication { /** @brief Application arguments */ struct Arguments { /** @brief Constructor */ - /*implicit*/ constexpr Arguments(int& argc, char** argv, HWND window) noexcept: argc{argc}, argv{argv}, window{window} {} + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} int& argc; /**< @brief Argument count */ char** argv; /**< @brief Argument values */ - #ifndef DOXYGEN_GENERATING_OUTPUT - HWND window; - #endif }; - class Configuration; - - #ifndef DOXYGEN_GENERATING_OUTPUT - static int create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)); - #endif + /** + * @brief Configuration + * + * @see @ref WindowlessWglApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessWglContext::Configuration Configuration; /** * @brief Default constructor @@ -126,6 +254,7 @@ class WindowlessWglApplication { * See @ref Configuration for more information. The program exits if * the context cannot be created, see @ref tryCreateContext() for an * alternative. + * @see @ref WindowlessWglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); @@ -142,7 +271,15 @@ class WindowlessWglApplication { * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessWglApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessWglApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessWglApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessWglApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessWglApplication(const Arguments&, NoCreateT) instead") explicit WindowlessWglApplication(const Arguments& arguments, std::nullptr_t): WindowlessWglApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessWglApplication(const WindowlessWglApplication&) = delete; @@ -177,15 +314,7 @@ class WindowlessWglApplication { * constructor itself. Error message is printed and the program exits * if the context cannot be created, see @ref tryCreateContext() for an * alternative. - * - * On desktop GL, if version is not specified in @p configuration, the - * application first tries to create core context (OpenGL 3.1+) and if - * that fails, falls back to compatibility OpenGL 2.1 context. However, - * on binary AMD and NVidia drivers, creating core context does not use - * the largest available version. If the application detects such case, - * the core context is destroyed and compatibility OpenGL 2.1 context - * is created instead to make the driver use the latest available - * version. + * @see @ref WindowlessWglContext */ #ifdef DOXYGEN_GENERATING_OUTPUT void createContext(const Configuration& configuration = Configuration()); @@ -204,61 +333,10 @@ class WindowlessWglApplication { bool tryCreateContext(const Configuration& configuration); private: - HWND _window; - HDC _deviceContext; - HGLRC _renderingContext; - + WindowlessWglContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessWglApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessWglApplication::Configuration { - public: - /** - * @brief Context flag - * - * @see @ref Flags, @ref setFlags(), @ref Context::Flag - */ - enum class Flag: int { - Debug = WGL_CONTEXT_DEBUG_BIT_ARB /**< Create debug context */ - }; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref Context::Flags - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - typedef Containers::EnumSet Flags; - #else - typedef Containers::EnumSet Flags; - #endif - - constexpr /*implicit*/ Configuration() {} - - /** @brief Context flags */ - Flags flags() const { return _flags; } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is no flag. See also @ref Context::flags(). - */ - Configuration& setFlags(Flags flags) { - _flags = flags; - return *this; - } - - private: - Flags _flags; -}; - /** @hideinitializer @brief Entry point for windowless WGL application @param className Class name @@ -270,29 +348,15 @@ windowless application header is included this macro is also aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. */ #define MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN(className) \ - int globalArgc; char** globalArgv; \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { \ - switch(message) { \ - case WM_CREATE: \ - { \ - className app({globalArgc, globalArgv, hWnd}); \ - PostQuitMessage(app.exec()); \ - } \ - break; \ - default: return DefWindowProc(hWnd, message, wParam, lParam); \ - } \ - return 0; \ - } \ int main(int argc, char** argv) { \ - globalArgc = argc; \ - globalArgv = argv; \ - return Magnum::Platform::WindowlessWglApplication::create(windowProcedure); \ + className app({argc, argv}); \ + return app.exec(); \ } #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessWglApplication WindowlessApplication; +typedef WindowlessWglContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp index 432274a8f..cf4c944e3 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp @@ -35,11 +35,11 @@ namespace Magnum { namespace Platform { -#ifndef DOXYGEN_GENERATING_OUTPUT -int WindowlessWindowsEglApplication::create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)) { +WindowlessWindowsEglContext::WindowlessWindowsEglContext(const Configuration& configuration, Context*) { + /* Create the window */ const WNDCLASS wc{ 0, - windowProcedure, + DefWindowProc, 0, 0, GetModuleHandle(nullptr), @@ -49,39 +49,12 @@ int WindowlessWindowsEglApplication::create(LRESULT(CALLBACK windowProcedure)(HW nullptr, L"Magnum Windowless Application" }; - if(!RegisterClass(&wc)) return 1; - - CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application", + if(!RegisterClass(&wc)) return; + const HWND window = CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application", WS_OVERLAPPEDWINDOW, 0, 0, 32, 32, 0, 0, wc.hInstance, 0); - /* Hammer the return code out of the messaging thingy */ - MSG msg; - do {} while(GetMessageW(&msg, nullptr, 0, 0) != 0); - return msg.wParam; -} -#endif - -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments): WindowlessWindowsEglApplication{arguments, Configuration{}} {} -#endif - -WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWindowsEglApplication{arguments, nullptr} { - createContext(configuration); -} - -WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window), _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} - -void WindowlessWindowsEglApplication::createContext() { createContext({}); } - -void WindowlessWindowsEglApplication::createContext(const Configuration& configuration) { - if(!tryCreateContext(configuration)) std::exit(1); -} - -bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWindowsEglApplication::tryCreateContext(): context already created", false); - /* Initialize */ - _display = eglGetDisplay(GetDC(_window)); + _display = eglGetDisplay(GetDC(window)); if(!eglInitialize(_display, nullptr, nullptr)) { Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); return false; @@ -117,7 +90,8 @@ bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration& conf EGL_NONE }; EGLint configCount; - if(!eglChooseConfig(_display, attribs, &_config, 1, &configCount)) { + EGLConfig config; + if(!eglChooseConfig(_display, attribs, &config, 1, &configCount)) { Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot get EGL visual config:" << Implementation::eglErrorString(eglGetError()); return false; } @@ -142,30 +116,73 @@ bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration& conf EGL_NONE }; - if(!(_glContext = eglCreateContext(_display, _config, EGL_NO_CONTEXT, attributes))) { - Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot create EGL context:" << Implementation::eglErrorString(eglGetError()); - return false; - } - if(!(_surface = eglCreateWindowSurface(_display, _config, _window, nullptr))) { - Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot create window surface:" << Implementation::eglErrorString(eglGetError()); - return false; + if(!(_context = eglCreateContext(_display, config, EGL_NO_CONTEXT, attributes))) { + Error() << "Platform::WindowlessWindowsEglContext: cannot create EGL context:" << Implementation::eglErrorString(eglGetError()); + return; } - if(!eglMakeCurrent(_display, _surface, _surface, _glContext)) { - Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot make context current:" << Implementation::eglErrorString(eglGetError()); - return false; - } + if(!(_surface = eglCreateWindowSurface(_display, config, _window, nullptr))) + Error() << "Platform::WindowlessWindowsEglContext: cannot create window surface:" << Implementation::eglErrorString(eglGetError()); +} + +WindowlessWindowsEglContext::WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other): _window{other._window}, _display{other._display}, _surface{other._surface}, _context{other._context} { + other._window = {}; + other._display = {}; + other._surface = {}; + other._context = {}; +} + +WindowlessWindowsEglContext::~WindowlessWindowsEglContext() { + if(_context) eglDestroyContext(_display, _context); + if(_surface) eglDestroySurface(_display, _surface); + if(_display) eglTerminate(_display); + if(_window) DestroyWindow(_window); +} + +WindowlessWindowsEglContext& WindowlessWindowsEglContext::operator=(WindowlessWindowsEglContext&& other) { + using std::swap; + swap(other._window, _window); + swap(other._display, _display); + swap(other._surface, _surface); + swap(other._context, _context); + return *this; +} - /* Return true if the initialization succeeds */ - return _context->tryCreate(); +bool WindowlessWindowsEglContext::makeCurrent() { + if(eglMakeCurrent(_display, _surface, _surface, _context)) + return true; + + Error() << "Platform::WindowlessWindowsEglContext::makeCurrent(): cannot make context current:" << GetLastError(); + return false; } -WindowlessWindowsEglApplication::~WindowlessWindowsEglApplication() { - _context.reset(); +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments): WindowlessWindowsEglApplication{arguments, Configuration{}} {} +#endif - eglDestroyContext(_display, _glContext); - eglDestroySurface(_display, _surface); - eglTerminate(_display); +WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWindowsEglApplication{arguments, NoCreate} { + createContext(configuration); } +WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t): _glContext{NoCreate}, _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +void WindowlessWindowsEglApplication::createContext() { createContext({}); } + +void WindowlessWindowsEglApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWindowsEglApplication::tryCreateContext(): context already created", false); + + WindowlessWindowsEglContext glContext{configuration, _context.get()}; + if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) + return false; + + _glContext = std::move(glContext); + return true; +} + +WindowlessWindowsEglApplication::~WindowlessWindowsEglApplication() = default; + }} diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.h b/src/Magnum/Platform/WindowlessWindowsEglApplication.h index 87c6e663f..07a028458 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.h +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Platform::WindowlessWindowsEglApplication, macro @ref MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN() + * @brief Class @ref Magnum::Platform::WindowlessWindowsEglApplication, @ref Magnum::Platform::WindowlessWindowsEglContext, macro @ref MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN() */ #include @@ -41,15 +41,136 @@ #include "Magnum/Magnum.h" #include "Magnum/OpenGL.h" +#include "Magnum/Tags.h" #include "Magnum/Platform/Platform.h" namespace Magnum { namespace Platform { /** -@brief Windowless Windows/EGL application +@brief Windowless Windows/EGL context + +GL context using pure WINAPI and EGL, used in @ref WindowlessWindowsEglApplication. +It is built if `WITH_WINDOWLESSWINDOWSEGLAPPLICATION` is enabled in CMake. + +Meant to be used when there is a need to manage (multiple) GL contexts +manually. See @ref platform-windowless-contexts for more information. If no +other application header is included, this class is also aliased to +`Platform::WindowlessGLContext`. +*/ +class WindowlessWindowsEglContext { + public: + class Configuration; + + /** + * @brief Constructor + * @param configuration Context configuration + * @param context Optional Magnum context instance constructed + * using @ref NoCreate to manage driver workarounds + * + * Once the context is created, make it current using @ref makeCurrent() + * and create @ref Platform::Context instance to be able to use Magnum. + * @see @ref isCreated() + */ + explicit WindowlessWindowsEglContext(const Configuration& configuration, Context* context = nullptr); + + /** + * @brief Construct without creating the context + * + * Move a instance with created context over to make it usable. + */ + explicit WindowlessWindowsEglContext(NoCreateT) {} + + /** @brief Copying is not allowed */ + WindowlessWindowsEglContext(const WindowlessWindowsEglContext&) = delete; + + /** @brief Move constructor */ + WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other); -Application for offscreen rendering using pure WINAPI and EGL. + /** @brief Copying is not allowed */ + WindowlessWindowsEglContext& operator=(const WindowlessWindowsEglContext&) = delete; + + /** @brief Move assignment */ + WindowlessWindowsEglContext& operator=(WindowlessWindowsEglContext&& other); + + /** + * @brief Destructor + * + * Destroys the context, if any. + */ + ~WindowlessWindowsEglContext(); + + /** @brief Whether the context is created */ + bool isCreated() const { return _context; } + + /** + * @brief Make the context current + * + * Prints error message and returns `false` on failure, otherwise + * returns `true`. + */ + bool makeCurrent(); + + private: + HWND _window{}; + EGLDisplay _display{}; + EGLSurface _surface{}; + EGLContext _context{}; +}; + +/** +@brief Configuration +@see @ref WindowlessWindowsEglContext(), + @ref WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(), + @ref WindowlessWindowsEglApplication::createContext(), + @ref WindowlessWindowsEglApplication::tryCreateContext() +*/ +class WindowlessWindowsEglContext::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + */ + enum class Flag: int { + Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR /**< Create debug context */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + + constexpr /*implicit*/ Configuration() {} + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. See also @ref Context::flags(). + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + private: + Flags _flags; +}; + +/** +@brief Windowless Windows/EGL application + +Application for offscreen rendering using @ref WindowlessWindowsEglContext. This application library is available on OpenGL ES (also ANGLE) on Windows. It is built if `WITH_WINDOWLESSWINDOWSEGLAPPLICATION` is enabled in CMake. @@ -97,20 +218,19 @@ class WindowlessWindowsEglApplication { /** @brief Application arguments */ struct Arguments { /** @brief Constructor */ - /*implicit*/ constexpr Arguments(int& argc, char** argv, HWND window) noexcept: argc{argc}, argv{argv}, window{window} {} + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} int& argc; /**< @brief Argument count */ char** argv; /**< @brief Argument values */ - #ifndef DOXYGEN_GENERATING_OUTPUT - HWND window; - #endif }; - class Configuration; - - #ifndef DOXYGEN_GENERATING_OUTPUT - static int create(LRESULT(CALLBACK windowProcedure)(HWND, UINT, WPARAM, LPARAM)); - #endif + /** + * @brief Configuration + * + * @see @ref WindowlessWindowsEglApplication(), @ref createContext(), + * @ref tryCreateContext() + */ + typedef WindowlessWindowsEglContext::Configuration Configuration; /** * @brief Default constructor @@ -137,7 +257,15 @@ class WindowlessWindowsEglApplication { * Unlike above, the context is not created and must be created later * with @ref createContext() or @ref tryCreateContext(). */ - explicit WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t); + explicit WindowlessWindowsEglApplication(const Arguments& arguments, NoCreateT); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief WindowlessWindowsEglApplication(const Arguments&, NoCreateT) + * @deprecated Use @ref WindowlessWindowsEglApplication(const Arguments&, NoCreateT) instead. + */ + CORRADE_DEPRECATED("use WindowlessWindowsEglApplication(const Arguments&, NoCreateT) instead") explicit WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t): WindowlessWindowsEglApplication{arguments, NoCreate} {} + #endif /** @brief Copying is not allowed */ WindowlessWindowsEglApplication(const WindowlessWindowsEglApplication&) = delete; @@ -190,63 +318,10 @@ class WindowlessWindowsEglApplication { bool tryCreateContext(const Configuration& configuration); private: - HWND _window; - EGLDisplay _display; - EGLConfig _config; - EGLSurface _surface; - EGLContext _glContext; - + WindowlessWindowsEglContext _glContext; std::unique_ptr _context; }; -/** -@brief Configuration - -@see @ref WindowlessWindowsEglApplication(), @ref createContext(), - @ref tryCreateContext() -*/ -class WindowlessWindowsEglApplication::Configuration { - public: - /** - * @brief Context flag - * - * @see @ref Flags, @ref setFlags(), @ref Context::Flag - */ - enum class Flag: int { - Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR /**< Create debug context */ - }; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref Context::Flags - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - typedef Containers::EnumSet Flags; - #else - typedef Containers::EnumSet Flags; - #endif - - constexpr /*implicit*/ Configuration() {} - - /** @brief Context flags */ - Flags flags() const { return _flags; } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is no flag. See also @ref Context::flags(). - */ - Configuration& setFlags(Flags flags) { - _flags = flags; - return *this; - } - - private: - Flags _flags; -}; - /** @hideinitializer @brief Entry point for windowless Windows/EGL application @param className Class name @@ -257,30 +332,16 @@ code, see @ref portability-applications for more information. When no other windowless application header is included this macro is also aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. */ -#define MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(className) \ - int globalArgc; char** globalArgv; \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { \ - switch(message) { \ - case WM_CREATE: \ - { \ - className app({globalArgc, globalArgv, hWnd}); \ - PostQuitMessage(app.exec()); \ - } \ - break; \ - default: return DefWindowProc(hWnd, message, wParam, lParam); \ - } \ - return 0; \ - } \ +#define MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(className) \ int main(int argc, char** argv) { \ - globalArgc = argc; \ - globalArgv = argv; \ - return Magnum::Platform::WindowlessWindowsEglApplication::create(windowProcedure); \ + className app({argc, argv}); \ + return app.exec(); \ } #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN typedef WindowlessWindowsEglApplication WindowlessApplication; +typedef WindowlessWindowsEglContext WindowlessGLContext; #define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(className) #else #undef MAGNUM_WINDOWLESSAPPLICATION_MAIN diff --git a/src/Magnum/Platform/magnum-info.cpp b/src/Magnum/Platform/magnum-info.cpp index 68cf49f56..0c38fa460 100644 --- a/src/Magnum/Platform/magnum-info.cpp +++ b/src/Magnum/Platform/magnum-info.cpp @@ -141,7 +141,7 @@ class MagnumInfo: public Platform::WindowlessApplication { int exec() override { return 0; } }; -MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplication(arguments, nullptr) { +MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} { Utility::Arguments args; args.addBooleanOption('s', "short").setHelp("short", "display just essential info and exit") .addBooleanOption("extension-strings").setHelp("extension-strings", "list all extension strings provided by the driver (implies --short)") diff --git a/src/Magnum/Test/AbstractOpenGLTester.h b/src/Magnum/Test/AbstractOpenGLTester.h index 9e2450d57..e159cd1ae 100644 --- a/src/Magnum/Test/AbstractOpenGLTester.h +++ b/src/Magnum/Test/AbstractOpenGLTester.h @@ -61,7 +61,7 @@ class AbstractOpenGLTester: public TestSuite::Tester { private: struct WindowlessApplication: Platform::WindowlessApplication { - explicit WindowlessApplication(const Arguments& arguments): Platform::WindowlessApplication{arguments, nullptr} {} + explicit WindowlessApplication(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} {} int exec() override final { return 0; } using Platform::WindowlessApplication::tryCreateContext; @@ -94,31 +94,7 @@ std::optional AbstractOpenGLTester:: #define MAGNUM_VERIFY_NO_ERROR() CORRADE_COMPARE(Magnum::Renderer::error(), Magnum::Renderer::Error::NoError) -#ifdef CORRADE_TARGET_WINDOWS -#define MAGNUM_GL_TEST_MAIN(Class) \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); \ - LRESULT CALLBACK windowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { \ - int ret = 0; \ - switch(message) { \ - case WM_CREATE: \ - { \ - Magnum::Test::AbstractOpenGLTester::_windowlessApplicationArguments->window = hWnd; \ - Class t; \ - t.registerTest(__FILE__, #Class); \ - PostQuitMessage(ret = t.exec( \ - Magnum::Test::AbstractOpenGLTester::_windowlessApplicationArguments->argc, \ - Magnum::Test::AbstractOpenGLTester::_windowlessApplicationArguments->argv)); \ - } \ - break; \ - default: return DefWindowProc(hWnd, message, wParam, lParam); \ - } \ - return ret; \ - } \ - int main(int argc, char** argv) { \ - Magnum::Test::AbstractOpenGLTester::_windowlessApplicationArguments.emplace(argc, argv, nullptr); \ - return Magnum::Platform::WindowlessApplication::create(windowProcedure); \ - } -#elif defined(CORRADE_TESTSUITE_TARGET_XCTEST) +#ifdef CORRADE_TESTSUITE_TARGET_XCTEST #define MAGNUM_GL_TEST_MAIN(Class) \ int CORRADE_VISIBILITY_EXPORT corradeTestMain(int argc, char** argv) { \ Magnum::Test::AbstractOpenGLTester::_windowlessApplicationArguments.emplace(argc, argv); \ diff --git a/src/Magnum/Text/fontconverter.cpp b/src/Magnum/Text/fontconverter.cpp index 78a90dbf4..29f8bbc50 100644 --- a/src/Magnum/Text/fontconverter.cpp +++ b/src/Magnum/Text/fontconverter.cpp @@ -101,7 +101,7 @@ class FontConverter: public Platform::WindowlessApplication { Utility::Arguments args; }; -FontConverter::FontConverter(const Arguments& arguments): Platform::WindowlessApplication(arguments, nullptr) { +FontConverter::FontConverter(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} { args.addArgument("input").setHelp("input", "input font") .addArgument("output").setHelp("output", "output filename prefix") .addNamedArgument("font").setHelp("font", "font plugin") diff --git a/src/Magnum/TextureTools/distancefieldconverter.cpp b/src/Magnum/TextureTools/distancefieldconverter.cpp index bded817b8..c06831f36 100644 --- a/src/Magnum/TextureTools/distancefieldconverter.cpp +++ b/src/Magnum/TextureTools/distancefieldconverter.cpp @@ -102,7 +102,7 @@ class DistanceFieldConverter: public Platform::WindowlessApplication { Utility::Arguments args; }; -DistanceFieldConverter::DistanceFieldConverter(const Arguments& arguments): Platform::WindowlessApplication(arguments, nullptr) { +DistanceFieldConverter::DistanceFieldConverter(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} { args.addArgument("input").setHelp("input", "input image") .addArgument("output").setHelp("output", "output image") .addOption("importer", "AnyImageImporter").setHelp("importer", "image importer plugin")