|
|
|
|
/*
|
|
|
|
|
This file is part of Magnum.
|
|
|
|
|
|
|
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
|
|
|
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
|
|
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
to deal in the Software without restriction, including without limitation
|
|
|
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
|
|
|
in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace Magnum {
|
|
|
|
|
/** @page platform Platform support
|
|
|
|
|
@brief Integration into windowing toolkits and creation of windowless contexts.
|
|
|
|
|
|
|
|
|
|
@tableofcontents
|
|
|
|
|
@m_footernavigation
|
|
|
|
|
|
|
|
|
|
@ref Platform namespace contains classes integrating Magnum engine into
|
|
|
|
|
various toolkits, both windowed and windowless. Each class has slightly
|
|
|
|
|
different dependencies and platform requirements, see documentation of
|
|
|
|
|
@ref Platform namespace and particular `*Application` classes for more
|
|
|
|
|
information about building and usage with CMake.
|
|
|
|
|
|
|
|
|
|
All the classes have common API to achieve static polymorphism, so basically
|
|
|
|
|
you can use different toolkits on different platforms and the only thing you
|
|
|
|
|
need to change is the class name, everything else is the same. Basic usage is
|
|
|
|
|
to subclass the chosen `*Application` class and implement required methods.
|
|
|
|
|
|
|
|
|
|
@section platform-windowed Windowed applications
|
|
|
|
|
|
|
|
|
|
Windowed applications provide a window and keyboard and mouse handling. The
|
|
|
|
|
de-facto standard and most widely used toolkit is SDL2, which is implemented in
|
|
|
|
|
@ref Platform::Sdl2Application. As said above, the usage is similar for all
|
|
|
|
|
toolkits, you must provide one-argument constructor and implement at least
|
|
|
|
|
@ref Platform::Sdl2Application::drawEvent() "drawEvent()" function. The class
|
|
|
|
|
can be then used directly in @cpp main() @ce, but for convenience and
|
|
|
|
|
portability it's better to use @ref MAGNUM_SDL2APPLICATION_MAIN() macro.
|
|
|
|
|
|
|
|
|
|
To simplify the porting, the library provides @cpp Platform::Application @ce
|
|
|
|
|
typedef and @cpp MAGNUM_APPLICATION_MAIN() @ce macro (but only if only one
|
|
|
|
|
application header is included, to avoid ambiguity). Changing the code to use
|
|
|
|
|
different toolkit is then matter of replacing only the @cpp #include @ce
|
|
|
|
|
statement (and changing one line in CMake build script, as you see later).
|
|
|
|
|
|
|
|
|
|
Barebone application implementation which will just clear the window to dark
|
|
|
|
|
blue color is shown in the following code listing.
|
|
|
|
|
|
|
|
|
|
@note Fully contained base application along with CMake setup is available in
|
|
|
|
|
`base` branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap)
|
|
|
|
|
repository.
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform.cpp windowed
|
|
|
|
|
|
|
|
|
|
@subsection platform-windowed-viewport Responding to viewport size changes
|
|
|
|
|
|
|
|
|
|
By default the application doesn't respond to window size changes in any way,
|
|
|
|
|
as the window has fixed size in most cases. To respond to size change for
|
|
|
|
|
example by resizing the default framebuffer, you need to reimplement
|
|
|
|
|
@ref Platform::Sdl2Application::viewportEvent() "viewportEvent()" function and
|
|
|
|
|
pass the new size to the framebuffer:
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform.cpp size
|
|
|
|
|
|
|
|
|
|
@section platform-windowless Windowless applications
|
|
|
|
|
|
|
|
|
|
Windowless applications provide just a context for ofscreen rendering or
|
|
|
|
|
performing tasks on GPU. There is not yet any platform-independent toolkit
|
|
|
|
|
which could handle this in portable way, thus you have to use platform-specific
|
|
|
|
|
ones. Magnum provides windowless applications for X11-based Unix, macOS and
|
|
|
|
|
Windows. To make things simple, as an example we will use only
|
|
|
|
|
@ref Platform::WindowlessEglApplication, see link for bootstrap application
|
|
|
|
|
below for fully portable example.
|
|
|
|
|
|
|
|
|
|
You need to implement just @ref Platform::WindowlessEglApplication::exec() "exec()"
|
|
|
|
|
function. The class can be then used directly in @cpp main() @ce, but again,
|
|
|
|
|
for convenience and portability it's better to use
|
|
|
|
|
@ref MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN() macro.
|
|
|
|
|
|
|
|
|
|
Similarly as with windowed applications, to simplify the porting, the library
|
|
|
|
|
provides @cpp Platform::WindowlessApplication @ce typedef and
|
|
|
|
|
@cpp MAGNUM_WINDOWLESSAPPLICATION_MAIN() @ce macro, but only if just one
|
|
|
|
|
windowless application header is included. Changing the code to use different
|
|
|
|
|
toolkit is then matter of replacing only the @cpp #include @ce statement.
|
|
|
|
|
Aliases for windowless applications are separated from aliases for windowed
|
|
|
|
|
applications, because projects commonly contain both graphics application and
|
|
|
|
|
command-line tools (for data preparation etc.).
|
|
|
|
|
|
|
|
|
|
Barebone application which will just print out current OpenGL version and
|
|
|
|
|
renderer string and exits is in the following code listing.
|
|
|
|
|
|
|
|
|
|
@note Fully contained windowless application using @ref Platform::WindowlessCglApplication
|
|
|
|
|
on macOS, @ref Platform::WindowlessGlxApplication on Unix and
|
|
|
|
|
@ref Platform::WindowlessWglApplication on Windows along with CMake setup
|
|
|
|
|
is available in `windowless` branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap)
|
|
|
|
|
repository.
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform-windowless.cpp windowless
|
|
|
|
|
|
|
|
|
|
@section platform-compilation Compilation with CMake
|
|
|
|
|
|
|
|
|
|
Barebone compilation consists just of finding Magnum library with, for example,
|
|
|
|
|
`Sdl2Application` component, compilation of the executable and linking
|
|
|
|
|
`Magnum::Magnum` and `Magnum::Sdl2Application` to it.
|
|
|
|
|
|
|
|
|
|
Again, to simplify porting, you can also use generic `Magnum::Application`
|
|
|
|
|
aliases (or `Magnum::WindowlessApplication` for windowless applications), but
|
|
|
|
|
only if only one application (windowless application) component is requested to
|
|
|
|
|
avoid ambiguity. Changing the build script to use different toolkit is then
|
|
|
|
|
matter of replacing only the requested `*Application` component (and one
|
|
|
|
|
@cpp #include @ce line in the actual code, as said above).
|
|
|
|
|
|
|
|
|
|
@code{.cmake}
|
|
|
|
|
find_package(Magnum REQUIRED Sdl2Application)
|
|
|
|
|
|
|
|
|
|
add_executable(myapplication MyApplication.cpp)
|
|
|
|
|
target_link_libraries(myapplication
|
|
|
|
|
Magnum::Magnum
|
|
|
|
|
Magnum::Application)
|
|
|
|
|
@endcode
|
|
|
|
|
|
|
|
|
|
@section platform-configuration Specifying configuration
|
|
|
|
|
|
|
|
|
|
By default the application is created with some reasonable defaults (e.g.
|
|
|
|
|
window size 800x600 pixels). If you want something else, you can pass
|
|
|
|
|
@ref Platform::Sdl2Application::Configuration "Configuration" instance to
|
|
|
|
|
application constructor. Using method chaining it can be done conveniently like
|
|
|
|
|
this:
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform.cpp configuration
|
|
|
|
|
|
|
|
|
|
@subsection platform-configuration-delayed Delayed context creation
|
|
|
|
|
|
|
|
|
|
Sometimes you may want to set up the application based on a configuration file
|
|
|
|
|
or system introspection, which can't really be done inside the base class
|
|
|
|
|
initializer. You can specify @ref NoCreate in the constructor instead and pass
|
|
|
|
|
the @relativeref{Platform::Sdl2Application,Configuration} later to a
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} function:
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform.cpp createcontext
|
|
|
|
|
|
|
|
|
|
If context creation in the constructor or in
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} fails, the application prints
|
|
|
|
|
an error message to standard output and exits. While that frees you from having
|
|
|
|
|
to do explicit error handling, sometimes a more graceful behavior may be
|
|
|
|
|
desirable --- with @relativeref{Platform::Sdl2Application,tryCreate()} the
|
|
|
|
|
function returns @cpp false @ce instead of exiting and it's up to you whether
|
|
|
|
|
you abort the launch or retry with different configuration. You can for example
|
|
|
|
|
try enabling MSAA first, and if the context creation fails, fall back to no-AA
|
|
|
|
|
rendering:
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform.cpp trycreatecontext
|
|
|
|
|
|
|
|
|
|
<b></b>
|
|
|
|
|
|
|
|
|
|
@m_class{m-block m-warning}
|
|
|
|
|
|
|
|
|
|
@par Implications for GL objects as class members
|
|
|
|
|
Delaying GL context creation to
|
|
|
|
|
@relativeref{Platform::Sdl2Application,create()} /
|
|
|
|
|
@relativeref{Platform::Sdl2Application,tryCreate()} means that, at the
|
|
|
|
|
point when class members get constructed, the context isn't available yet.
|
|
|
|
|
If you have GL objects such as @ref GL::Mesh or @ref Shaders::PhongGL as
|
|
|
|
|
class members, the application may hit the following assert during startup:
|
|
|
|
|
@par
|
|
|
|
|
@code{.shell-session}
|
|
|
|
|
GL::Context::current(): no current context
|
|
|
|
|
@endcode
|
|
|
|
|
@par
|
|
|
|
|
The solution is to construct the GL objects with @ref NoCreate constructors
|
|
|
|
|
as well and populate them with live instances only after the context has
|
|
|
|
|
been created. See @ref opengl-wrapping-instances-nocreate for more
|
|
|
|
|
information.
|
|
|
|
|
|
|
|
|
|
@section platform-custom Using custom platform toolkits
|
|
|
|
|
|
|
|
|
|
In case you want to use some not-yet-supported toolkit or you don't want to use
|
|
|
|
|
the application wrappers in @ref Platform namespace, you can initialize Magnum
|
|
|
|
|
manually. First create OpenGL context and then create instance of
|
|
|
|
|
@ref Platform::GLContext class, which will take care of proper initialization
|
|
|
|
|
and feature detection. The instance must be alive for whole application
|
|
|
|
|
lifetime. Example @cpp main() @ce function with manual initialization is in the
|
|
|
|
|
following code listing.
|
|
|
|
|
|
|
|
|
|
@note Fully contained application using with manual Magnum initialization on
|
|
|
|
|
top of Qt toolkit is available in `base-qt` branch of
|
|
|
|
|
[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap/tree/base-qt)
|
|
|
|
|
repository. Similar project, but for wxWidgets and gtkmm are in the
|
|
|
|
|
[base-wxwidgets](https://github.com/mosra/magnum-bootstrap/tree/base-wxwidgets)
|
|
|
|
|
and [base-gtkmm](https://github.com/mosra/magnum-bootstrap/tree/base-gtkmm)
|
|
|
|
|
branches.
|
|
|
|
|
@note
|
|
|
|
|
There's also an example showing @ref examples-triangle-plain-glfw "usage of plain GLFW to render a basic triangle".
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform-custom.cpp custom
|
|
|
|
|
|
|
|
|
|
@attention The @ref Platform::GLContext instance is bound to a single OpenGL
|
|
|
|
|
context, which must be always set as current when calling any Magnum APIs
|
|
|
|
|
touching OpenGL state.
|
|
|
|
|
|
|
|
|
|
On majority of platforms the @ref Platform::GLContext class does GL function
|
|
|
|
|
pointer loading using platform-specific APIs. In that case you also need to
|
|
|
|
|
find particular `*Context` library, add its include dir and then link to it.
|
|
|
|
|
These platform-specific libraries are available:
|
|
|
|
|
|
|
|
|
|
- `CglContext` --- CGL context (macOS)
|
|
|
|
|
- `EglContext` --- EGL context (everywhere except Emscripten)
|
|
|
|
|
- `GlxContext` --- GLX context (X11-based Unix)
|
|
|
|
|
- `WglContext` --- WGL context (Windows)
|
|
|
|
|
|
|
|
|
|
Systems not listed here (such as Emscripten) don't need any `Context` library,
|
|
|
|
|
because dynamic function pointer loading is not available on these.
|
|
|
|
|
|
|
|
|
|
For example, when you create the OpenGL context using GLX, you need to find
|
|
|
|
|
`GlxContext` component, and link to `Magnum::GlxContext` target. Similarly to
|
|
|
|
|
application libraries, you can also use the generic `Magnum::GLContext` target,
|
|
|
|
|
providing you requested only one `*Context` component in the
|
|
|
|
|
@cmake find_package() @ce call. Complete example:
|
|
|
|
|
|
|
|
|
|
@code{.cmake}
|
|
|
|
|
find_package(Magnum REQUIRED GlxContext)
|
|
|
|
|
|
|
|
|
|
add_executable(myapplication MyCustomApplication.cpp)
|
|
|
|
|
target_link_libraries(myapplication
|
|
|
|
|
Magnum::Magnum
|
|
|
|
|
Magnum::GLContext)
|
|
|
|
|
@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 Platform::WindowlessEglApplication "Platform::Windowless*Application"
|
|
|
|
|
is accompanied by a @ref Platform::WindowlessEglContext "Platform::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::GLContext instance to initialize Magnum.
|
|
|
|
|
|
|
|
|
|
Similarly as with the applications, to simplify the porting, the library
|
|
|
|
|
provides @cpp Platform::WindowlessGLContext @ce 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 own OpenGL
|
|
|
|
|
context.
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform-windowless-custom.cpp custom
|
|
|
|
|
|
|
|
|
|
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). See
|
|
|
|
|
@ref GL-Context-multithreading and @ref CORRADE_BUILD_MULTITHREADED for more
|
|
|
|
|
information.
|
|
|
|
|
|
|
|
|
|
@note Context creation is not thread safe on all platforms, that's why it still
|
|
|
|
|
has to be done on the main thread.
|
|
|
|
|
|
|
|
|
|
@snippet MagnumPlatform-windowless-thread.cpp thread
|
|
|
|
|
*/
|
|
|
|
|
}
|