Browse Source

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).
pull/158/head
Vladimír Vondruš 10 years ago
parent
commit
6feda42f13
  1. 70
      doc/platform.dox
  2. 93
      src/Magnum/Platform/WindowlessCglApplication.cpp
  3. 142
      src/Magnum/Platform/WindowlessCglApplication.h
  4. 96
      src/Magnum/Platform/WindowlessEglApplication.cpp
  5. 205
      src/Magnum/Platform/WindowlessEglApplication.h
  6. 180
      src/Magnum/Platform/WindowlessGlxApplication.cpp
  7. 222
      src/Magnum/Platform/WindowlessGlxApplication.h
  8. 159
      src/Magnum/Platform/WindowlessIosApplication.h
  9. 71
      src/Magnum/Platform/WindowlessIosApplication.mm
  10. 79
      src/Magnum/Platform/WindowlessNaClApplication.cpp
  11. 131
      src/Magnum/Platform/WindowlessNaClApplication.h
  12. 218
      src/Magnum/Platform/WindowlessWglApplication.cpp
  13. 254
      src/Magnum/Platform/WindowlessWglApplication.h
  14. 121
      src/Magnum/Platform/WindowlessWindowsEglApplication.cpp
  15. 231
      src/Magnum/Platform/WindowlessWindowsEglApplication.h
  16. 2
      src/Magnum/Platform/magnum-info.cpp
  17. 28
      src/Magnum/Test/AbstractOpenGLTester.h
  18. 2
      src/Magnum/Text/fontconverter.cpp
  19. 2
      src/Magnum/TextureTools/distancefieldconverter.cpp

70
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
*/
}

93
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;
}
}}

142
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 <memory>
#include "Magnum/OpenGL.h"
#include "Magnum/Tags.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLTypes.h>
#include <OpenGL/CGLCurrent.h>
@ -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<Platform::Context> _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

96
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;
}}

205
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 <memory>
@ -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<Flag, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR> Flags;
#else
typedef Containers::EnumSet<Flag> 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<Platform::Context> _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<Flag, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR> Flags;
#else
typedef Containers::EnumSet<Flag> 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

180
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<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("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<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(reinterpret_cast<const GLubyte*>("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<const char*>(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<const char*>(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;
}}

222
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 <memory>
@ -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<Flag, GLX_CONTEXT_DEBUG_BIT_ARB> Flags;
#else
typedef Containers::EnumSet<Flag> 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<Platform::Context> _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<Flag, GLX_CONTEXT_DEBUG_BIT_ARB> Flags;
#else
typedef Containers::EnumSet<Flag> 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

159
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š <mosra@centrum.cz>
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 <memory>
@ -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<Platform::Context> _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<Flag> 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

71
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š <mosra@centrum.cz>
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;
}}

79
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<pp::Graphics3D> 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;

131
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 <memory>
#include <string>
#include <Corrade/Containers/EnumSet.h>
@ -41,6 +42,7 @@
#include <ppapi/gles2/gl2ext_ppapi.h>
#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<pp::Graphics3D> _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<Platform::Context> _context;
std::unique_ptr<ConsoleDebugOutput> _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

218
src/Magnum/Platform/WindowlessWglApplication.cpp

@ -25,19 +25,31 @@
#include "WindowlessWglApplication.h"
#include <cstring>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#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<PFNWGLCREATECONTEXTATTRIBSARBPROC>( wglGetProcAddress(reinterpret_cast<LPCSTR>("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<const char*>(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;
}}

254
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 <memory>
@ -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<Flag, WGL_CONTEXT_DEBUG_BIT_ARB> Flags;
#else
typedef Containers::EnumSet<Flag> 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<Platform::Context> _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<Flag, WGL_CONTEXT_DEBUG_BIT_ARB> Flags;
#else
typedef Containers::EnumSet<Flag> 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

121
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;
}}

231
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 <memory>
@ -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<Flag, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR> Flags;
#else
typedef Containers::EnumSet<Flag> 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<Platform::Context> _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<Flag, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR> Flags;
#else
typedef Containers::EnumSet<Flag> 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

2
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)")

28
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<Platform::WindowlessApplication::Arguments> 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); \

2
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")

2
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")

Loading…
Cancel
Save