diff --git a/CMakeLists.txt b/CMakeLists.txt index 593c31144..a88557da1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,9 +83,11 @@ cmake_dependent_option(WITH_SHADERS "Build Shaders library" ON "NOT WITH_DEBUGTO cmake_dependent_option(WITH_TEXT "Build Text library" ON "NOT WITH_MAGNUMFONT;NOT WITH_MAGNUMFONTCONVERTER" ON) cmake_dependent_option(WITH_TEXTURETOOLS "Build TextureTools library" ON "NOT WITH_TEXT;NOT WITH_DISTANCEFIELDCONVERTER" ON) -# EGL context, available everywhere except on platforms which don't support extension loading +# EGL context and windowless EGL application, available everywhere except on +# platforms which don't support extension loading if(NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET_NACL) option(WITH_EGLCONTEXT "Build EglContext library" OFF) + option(WITH_WINDOWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF) endif() # NaCl-specific application libraries diff --git a/doc/building.dox b/doc/building.dox index 70a2c70e1..7890e2968 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -207,6 +207,7 @@ platform best: - `WITH_SDL2APPLICATION` - @ref Platform::Sdl2Application "Sdl2Application" - `WITH_XEGLAPPLICATION` - @ref Platform::XEglApplication "XEglApplication" - `WITH_WINDOWLESSCGLAPPLICATION` - @ref Platform::WindowlessCglApplication "WindowlessCglApplication" +- `WITH_WINDOWLESSEGLAPPLICATION` - @ref Platform::WindowlessEglApplication "WindowlessEglApplication" - `WITH_WINDOWLESSGLXAPPLICATION` - @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" - `WITH_WINDOWLESSNACLAPPLICATION` - @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" - `WITH_WINDOWLESSWGLAPPLICATION` - @ref Platform::WindowlessWglApplication "WindowlessWglApplication" diff --git a/doc/cmake.dox b/doc/cmake.dox index 32fae0679..73892ee0f 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -100,6 +100,7 @@ Platform namespace is split into more components: - `Sdl2Application` -- @ref Platform::Sdl2Application "Sdl2Application" - `XEglApplication` -- @ref Platform::XEglApplication "XEglApplication" - `WindowlessCglApplication` -- @ref Platform::WindowlessCglApplication "WindowlessCglApplication" +- `WindowlessEglApplication` -- @ref Platform::WindowlessEglApplication "WindowlessEglApplication" - `WindowlessGlxApplication` -- @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" - `WindowlessNaClApplication` -- @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" - `WindowlessWglApplication` -- @ref Platform::WindowlessWglApplication "WindowlessWglApplication" diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 0a8eea56a..11aa63e0e 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -53,6 +53,7 @@ # Sdl2Application - SDL2 application # XEglApplication - X/EGL application # WindowlessCglApplication - Windowless CGL application +# WindowlessEglApplication - Windowless EGL application # WindowlessGlxApplication - Windowless GLX application # WindowlessNaClApplication - Windowless NaCl application # WindowlessWglApplication - Windowless WGL application @@ -334,7 +335,7 @@ endif() # Component distinction (listing them explicitly to avoid mistakes with finding # components from other repositories) -set(_MAGNUM_LIBRARY_COMPONENTS "^(Audio|DebugTools|MeshTools|Primitives|SceneGraph|Shaders|Shapes|Text|TextureTools|GlutApplication|GlxApplication|NaClApplication|Sdl2Application|XEglApplication|WindowlessCglApplication|WindowlessGlxApplication|WindowlessNaClApplication|WindowlessWglApplication|WindowlessWindowsEglApplication|CglContext|EglContext|GlxContext|WglContext)$") +set(_MAGNUM_LIBRARY_COMPONENTS "^(Audio|DebugTools|MeshTools|Primitives|SceneGraph|Shaders|Shapes|Text|TextureTools|GlutApplication|GlxApplication|NaClApplication|Sdl2Application|XEglApplication|WindowlessCglApplication|WindowlessEglApplication|WindowlessGlxApplication|WindowlessNaClApplication|WindowlessWglApplication|WindowlessWindowsEglApplication|CglContext|EglContext|GlxContext|WglContext)$") set(_MAGNUM_PLUGIN_COMPONENTS "^(MagnumFont|MagnumFontConverter|ObjImporter|TgaImageConverter|TgaImporter|WavAudioImporter)$") set(_MAGNUM_EXECUTABLE_COMPONENTS "^(distancefieldconverter|fontconverter|info)$") @@ -490,6 +491,14 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) # Windowless CGL application has no additional dependencies + + # Windowless EGL application dependencies + elseif(_component STREQUAL WindowlessEglApplication) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL) + endif() + # Windowless WGL application has no additional dependencies # Windowless Windows/EGL application dependencies diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index 2fc4b0f5c..520eefeed 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -295,6 +295,38 @@ if(WITH_WINDOWLESSGLXAPPLICATION OR NEED_ABSTRACTXAPPLICATION) endif() endif() +# Windowless EGL application +if(WITH_WINDOWLESSEGLAPPLICATION) + set(NEED_EGLCONTEXT 1) + + set(MagnumWindowlessEglApplication_SRCS + WindowlessEglApplication.cpp + Implementation/Egl.cpp + $) + set(MagnumWindowlessEglApplication_HEADERS + WindowlessEglApplication.h) + set(MagnumWindowlessEglApplication_PRIVATE_HEADERS + Implementation/Egl.h) + + add_library(MagnumWindowlessEglApplication STATIC + ${MagnumWindowlessEglApplication_SRCS} + ${MagnumWindowlessEglApplication_HEADERS} + ${MagnumWindowlessEglApplication_PRIVATE_HEADERS}) + set_target_properties(MagnumWindowlessEglApplication PROPERTIES DEBUG_POSTFIX "-d") + target_link_libraries(MagnumWindowlessEglApplication Magnum EGL::EGL) + # Assuming that PIC is not needed because the Application lib is always + # linked to the executable and not to any intermediate shared lib + + install(FILES ${MagnumWindowlessEglApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumWindowlessEglApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + + # Magnum WindowlessEglApplication target alias for superprojects + add_library(Magnum::WindowlessEglApplication ALIAS MagnumWindowlessEglApplication) +endif() + # Windowless GLX application if(WITH_WINDOWLESSGLXAPPLICATION) set(NEED_GLXCONTEXT 1) diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp new file mode 100644 index 000000000..61d61ce22 --- /dev/null +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -0,0 +1,137 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "WindowlessEglApplication.h" + +#include +#include + +#include "Magnum/Version.h" +#include "Magnum/Platform/Context.h" + +#include "Implementation/Egl.h" + +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); + + /* Initialize */ + _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(!eglInitialize(_display, nullptr, nullptr)) { + Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); + return false; + } + + const EGLenum api = + #ifndef MAGNUM_TARGET_GLES + EGL_OPENGL_API + #else + EGL_OPENGL_ES_API + #endif + ; + if(!eglBindAPI(api)) { + Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot bind EGL API:" << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Choose EGL config */ + static const EGLint attribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + #ifndef MAGNUM_TARGET_GLES + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #elif defined(MAGNUM_TARGET_GLES3) + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, + #elif defined(MAGNUM_TARGET_GLES2) + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #else + #error unsupported OpenGL edition + #endif + EGL_NONE + }; + EGLint configCount; + if(!eglChooseConfig(_display, attribs, &_config, 1, &configCount)) { + Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get EGL visual config:" << Implementation::eglErrorString(eglGetError()); + return false; + } + + if(!configCount) { + Error() << "Platform::WindowlessEglApplication::tryCreateContext(): no matching EGL visual config available"; + return false; + } + + const EGLint attributes[] = { + #ifdef MAGNUM_TARGET_GLES + EGL_CONTEXT_CLIENT_VERSION, + #ifdef MAGNUM_TARGET_GLES3 + 3, + #elif defined(MAGNUM_TARGET_GLES2) + 2, + #else + #error unsupported OpenGL ES version + #endif + #endif + EGL_CONTEXT_FLAGS_KHR, EGLint(configuration.flags()), + EGL_NONE + }; + + if(!(_glContext = 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 true if the initialization succeeds */ + return _context->tryCreate(); +} + +WindowlessEglApplication::~WindowlessEglApplication() { + _context.reset(); + + eglDestroyContext(_display, _glContext); + eglDestroySurface(_display, EGL_NO_SURFACE); + eglTerminate(_display); +} + +}} diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h new file mode 100644 index 000000000..034e66b0d --- /dev/null +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -0,0 +1,242 @@ +#ifndef Magnum_Platform_WindowlessEglApplication_h +#define Magnum_Platform_WindowlessEglApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Platform::WindowlessEglApplication, macro @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() + */ + +#include +#include +#include +#include +/* undef Xlib nonsense to avoid conflicts */ +#undef Always +#undef Complex +#undef None +#undef Status + +#include "Magnum/Magnum.h" +#include "Magnum/OpenGL.h" +#include "Magnum/Platform/Platform.h" + +namespace Magnum { namespace Platform { + +/** +@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. + +It is built if `WITH_WINDOWLESSEGLAPPLICATION` is enabled in CMake. + +## Bootstrap application + +Fully contained windowless application using @ref WindowlessEglApplication +along with CMake setup is available in `windowless` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/windowless.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/windowless.zip) +file. After extracting the downloaded archive you can build and run the +application with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +See @ref cmake for more information. + +## General usage + +In CMake you need to request `WindowlessEglApplication` component and link to +`Magnum::WindowlessEglApplication` target. If no other windowless application +is requested, you can also use generic `Magnum::WindowlessApplication` alias to +simplify porting. Again, see @ref building and @ref cmake for more information. + +Place your code into @ref exec(). The subclass can be then used in main +function using @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() macro. See +@ref platform for more information. +@code +class MyApplication: public Platform::WindowlessEglApplication { + // implement required methods... +}; +MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::WindowlessApplication` and the macro is aliased to +`MAGNUM_WINDOWLESSAPPLICATION_MAIN()` to simplify porting. +*/ +class WindowlessEglApplication { + public: + /** @brief Application arguments */ + struct Arguments { + /** @brief Constructor */ + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv}{} + + int& argc; /**< @brief Argument count */ + char** argv; /**< @brief Argument values */ + }; + + class Configuration; + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration); + explicit WindowlessEglApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit WindowlessEglApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + WindowlessEglApplication(const WindowlessEglApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessEglApplication(WindowlessEglApplication&&) = delete; + + /** @brief Copying is not allowed */ + WindowlessEglApplication& operator=(const WindowlessEglApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessEglApplication& operator=(WindowlessEglApplication&&) = delete; + + /** + * @brief Execute application + * @return Value for returning from `main()` + * + * See @ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() for usage + * information. + */ + virtual int exec() = 0; + + protected: + /* Nobody will need to have (and delete) WindowlessEglApplication*, + thus this is faster than public pure virtual destructor */ + ~WindowlessEglApplication(); + + /** @copydoc Sdl2Application::createContext() */ + #ifdef DOXYGEN_GENERATING_OUTPUT + void createContext(const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + void createContext(const Configuration& configuration); + void createContext(); + #endif + + /** @copydoc Sdl2Application::tryCreateContext() */ + bool tryCreateContext(const Configuration& configuration); + + private: + EGLDisplay _display; + EGLConfig _config; + EGLContext _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 + +See @ref Magnum::Platform::WindowlessEglApplication "Platform::WindowlessEglApplication" +for usage information. This macro abstracts out platform-specific entry point +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_WINDOWLESSEGLAPPLICATION_MAIN(className) \ + int main(int argc, char** argv) { \ + className app({argc, argv}); \ + return app.exec(); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN +typedef WindowlessEglApplication WindowlessApplication; +#define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN(className) +#else +#undef MAGNUM_WINDOWLESSAPPLICATION_MAIN +#endif +#endif + +}} + +#endif