mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
15 KiB
393 lines
15 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 |
|
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. |
|
|
|
@code{.cpp} |
|
#include <Magnum/DefaultFramebuffer.h> |
|
#include <Magnum/Renderer.h> |
|
#include <Magnum/Math/Color.h> |
|
#include <Magnum/Platform/Sdl2Application.h> |
|
|
|
using namespace Magnum; |
|
|
|
class MyApplication: public Platform::Application { |
|
public: |
|
MyApplication(const Arguments& arguments); |
|
|
|
private: |
|
void drawEvent() override; |
|
}; |
|
|
|
MyApplication::MyApplication(const Arguments& arguments): Platform::Application(arguments) { |
|
// Set clear color to dark blue |
|
Renderer::setClearColor({0.0f, 0.0f, 0.4f}); |
|
} |
|
|
|
void MyApplication::drawEvent() { |
|
// Clear the window |
|
defaultFramebuffer.clear(FramebufferClear::Color); |
|
|
|
// The context is double-buffered, swap buffers |
|
swapBuffers(); |
|
} |
|
|
|
// main() function implementation |
|
MAGNUM_APPLICATION_MAIN(MyApplication) |
|
@endcode |
|
|
|
@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: |
|
|
|
@code{.cpp} |
|
class MyApplication: public Platform::Application { |
|
// ... |
|
|
|
private: |
|
void viewportEvent(const Vector2i& size) override; |
|
}; |
|
|
|
// ... |
|
|
|
void MyApplication::viewportEvent(const Vector2i& size) { |
|
defaultFramebuffer.setViewport({{}, size}); |
|
} |
|
@endcode |
|
|
|
@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::WindowlessGlxApplication, see link for bootstrap application |
|
below for fully portable example. |
|
|
|
You need to implement just @ref Platform::WindowlessGlxApplication::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_WINDOWLESSGLXAPPLICATION_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. |
|
|
|
@code{.cpp} |
|
#include <Magnum/Context.h> |
|
#include <Magnum/Platform/WindowlessGlxApplication.h> |
|
|
|
using namespace Magnum; |
|
|
|
class MyApplication: public Platform::WindowlessApplication { |
|
public: |
|
MyApplication(const Arguments& arguments); |
|
|
|
int exec() override; |
|
}; |
|
|
|
MyApplication::MyApplication(const Arguments& arguments): Platform::WindowlessApplication(arguments) {} |
|
|
|
int MyApplication::exec() { |
|
Debug() << "OpenGL version:" << Context::current()->versionString(); |
|
Debug() << "OpenGL renderer:" << Context::current()->rendererString(); |
|
|
|
// Exit with success |
|
return 0; |
|
} |
|
|
|
// main() function implementation |
|
MAGNUM_WINDOWLESSAPPLICATION_MAIN(MyApplication) |
|
@endcode |
|
|
|
@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: |
|
|
|
@code{.cpp} |
|
MyApplication::MyApplication(int& argc, char** argv): |
|
Platform::Application(argc, argv, Configuration() |
|
.setTitle("My Application") |
|
.setSize({800, 600})) |
|
{ |
|
// ... |
|
} |
|
@endcode |
|
|
|
However, sometimes you would need to configure the application based on some |
|
configuration file or system introspection. In that case you can pass `nullptr` |
|
instead of @ref Platform::Sdl2Application::Configuration "Configuration" |
|
instance and then specify it later with @ref Platform::Sdl2Application::createContext() "createContext()": |
|
|
|
@code{.cpp} |
|
MyApplication::MyApplication(int& argc, char** argv): Platform::Application(argc, argv, nullptr) { |
|
// ... |
|
|
|
createContext(Configuration() |
|
.setTitle("My Application") |
|
.setSize(size)); |
|
|
|
// ... |
|
} |
|
@endcode |
|
|
|
If the context creation in constructor or @ref Platform::Sdl2Application::createContext() "createContext()" |
|
fails, the application exits. However, it is also possible to negotiate the |
|
context using @ref Platform::Sdl2Application::tryCreateContext() "tryCreateContext()". |
|
The only difference is that this function returns `false` instead of exiting. |
|
You can for example try enabling MSAA and if the context creation fails, fall |
|
back to no-AA rendering: |
|
|
|
@code{.cpp} |
|
MyApplication::MyApplication(int& argc, char** argv): Platform::Application(argc, argv, nullptr) { |
|
// ... |
|
|
|
Configuration conf; |
|
conf.setTitle("My Application") |
|
.setSampleCount(16); |
|
|
|
if(!tryCreateContext(conf)) |
|
createContext(conf.setSampleCount(0)); |
|
|
|
// ... |
|
} |
|
@endcode |
|
|
|
@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::Context 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) repository. |
|
|
|
@code{.cpp} |
|
int main(int argc, char** argv) { |
|
// Create OpenGL context ... |
|
|
|
{ |
|
// Initialize Magnum |
|
Platform::Context context; |
|
|
|
// Open window, enter main loop ... |
|
|
|
// Magnum context gets destroyed |
|
} |
|
|
|
// Delete OpenGL context ... |
|
} |
|
@endcode |
|
|
|
@attention Currently Magnum is limited to single OpenGL context, which must be |
|
always set as current. |
|
|
|
On majority of platforms the @ref Platform::Context 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 generic `Magnum::Context` 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::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 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::Context 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 OpenGL context. |
|
|
|
@code{.cpp} |
|
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). See also |
|
@ref MAGNUM_BUILD_MULTITHREADED. |
|
|
|
@note Context creation is not thread safe on all platforms, that's why it still |
|
has to be done on the main thread. |
|
|
|
@code{.cpp} |
|
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 |
|
*/ |
|
}
|
|
|