diff --git a/doc/compilation-speedup.dox b/doc/compilation-speedup.dox
index 688401977..53dbba44e 100644
--- a/doc/compilation-speedup.dox
+++ b/doc/compilation-speedup.dox
@@ -25,27 +25,27 @@
namespace Magnum {
/** @page compilation-speedup Speeding up compilation
-@brief Techniques for reducing compilation times.
+@brief Techniques for reducing compilation times used by Magnum itself and recommended for application code as well.
@tableofcontents
@m_footernavigation
@section compilation-forward-declarations Forward declarations instead of includes
-Essential thing when speeding up compilation is reducing number of @cpp #include @ce
-directives in both headers and source files. Magnum is strictly applying this
-policy in all header files, so all types which are not directly used in the
-header have only forward declarations.
+An essential thing when speeding up compilation is reducing number of
+@cpp #include @ce directives in both headers and source files. Magnum is
+strictly applying this policy in all header files, so all types which are not
+directly used in the header have only forward declarations.
For example, when including @ref Magnum.h, you get shortcut typedefs for
floating-point vectors and matrices like @ref Vector3 and @ref Matrix4, but to
actually use any of them, you have to include the respective header, e.g.
@ref Magnum/Math/Vector3.h.
-You are encouraged to use forward declarations also in your code. However, for
-some types it can be too cumbersome --- e.g. too many template parameters,
-typedefs etc. In this case a header with forward declarations is usually
-available, each namespace has its own:
+You are encouraged to use forward declarations in your code as well. However,
+for some types it can be too cumbersome --- e.g. too many template parameters,
+default template arguments, typedefs etc. Instead, forward declaration headers
+are available, with each namespace having its own:
- @ref Corrade/Corrade.h
- @ref Corrade/Containers/Containers.h
diff --git a/doc/opengl-wrapping.dox b/doc/opengl-wrapping.dox
index 2a3821728..d642ca725 100644
--- a/doc/opengl-wrapping.dox
+++ b/doc/opengl-wrapping.dox
@@ -31,9 +31,9 @@ namespace Magnum {
@m_footernavigation
The purpose of the @ref GL library is to simplify interaction with the OpenGL
-API using type-safe C++11 features, abstracting away extension and platform
-differences, tracking the state for optimum performance and selecting the best
-available code path for given system.
+API using type-safe C++11 features and RAII, abstracting away extension and
+platform differences, tracking the state for optimum performance and selecting
+the best available code path for given driver.
Magnum provides wrappers for most native OpenGL objects like buffers, textures,
meshes, queries, transform feedback objects, shaders etc., but makes it
@@ -42,49 +42,65 @@ libraries if the user wants to.
@section opengl-wrapping-instances OpenGL object wrapper instances
-By default, all underlying OpenGL objects are created in wrapper class
-constructor and deleted in wrapper class destructor. Constructing an object
-using default constructor requires active @ref GL::Context instance. All OpenGL
-objects are movable (but not copyable), although for performance reasons (and
-contrary to standard C++11 practice), the moved-from instance does *not* have
-any associated OpenGL object and is thus in *invalid state*. Using instance in
-moved-from state may result in OpenGL errors being generated, in some cases
-even application crashes.
-
-Besides the default behavior, it is possible to construct the object without
-creating the underlying OpenGL object using the @ref NoCreate tag.
-Constructing the object this way does not require any active context and its
-state is then equivalent to the moved-from state. It is useful in case you need
-to construct the object before creating context (such as class members) or if
-you know you would overwrite it later with another object:
-
-@snippet MagnumGL.cpp opengl-wrapping-nocreate
-
-
-
-@m_class{m-note m-warning}
-
-@par
- Note that calling anything on objects created this way is not defined (and
- not checked or guarded in any way) and may result in crashes. If you want delayed object creation with safety checks (however with some extra
- memory overhead), wrap the objects in an @ref Corrade::Containers::Optional.
+By default, all wrapper classes follow RAII -- underlying OpenGL objects are
+created in the class constructor and deleted in the wrapper class destructor.
+All OpenGL objects are movable (but not copyable) and moves are *destructive*
+--- the moved-from instance does *not* have any associated OpenGL object and
+is thus in an *invalid state*. Calling anything on instances in a moved-from
+state may thus result in OpenGL errors being generated, in some cases even
+application crashes.
If you need to preserve the underlying OpenGL object after destruction, you can
call @cpp release() @ce. It returns ID of the underlying object, the instance
-is then equivalent to moved-from state and you are responsible for proper
+is then equivalent to the moved-from state and you are responsible for proper
deletion of the returned OpenGL object (note that it is possible to just query
-ID of the underlying without releasing it using `id()`). It is also possible to
-do the opposite --- wrapping existing OpenGL object ID into Magnum object
-instance using @cpp wrap() @ce.
+ID of the underlying without releasing it using @cpp id() @ce). It is also
+possible to do the opposite --- wrapping an existing OpenGL object ID into a
+Magnum object instance using @cpp wrap() @ce:
@snippet MagnumGL.cpp opengl-wrapping-transfer
-The @cpp NoCreate @ce constructor, @cpp wrap() @ce and @cpp release() @ce
-functions are available for all OpenGL classes except @ref GL::Shader, where
-wrapping external instances makes less sense.
-
-Note that interaction with third-party OpenGL code as shown above usually needs
-special attention:
+The @cpp wrap() @ce and @cpp release() @ce functions are available for all
+OpenGL classes except for @ref GL::Shader, instances of which are rather
+short-lived and thus wrapping external instances makes less sense.
+
+@attention
+ Note that interaction with third-party OpenGL code as shown above usually
+ needs special attention due to the global nature of the OpenGL state
+ tracker. See @ref opengl-state-tracking below for more information.
+
+@subsection opengl-wrapping-instances-nocreate Delayed context creation and NoCreate constructors
+
+Constructing a Magnum GL object requires an active @ref GL::Context instance.
+By default, this instance is automatically created by
+@ref Platform::Sdl2Application "Platform::*Application" constructors, however
+if you use @ref platform-configuration-delayed "delayed context creation" or
+are directly interfacing with @ref platform-custom "custom platform toolkits",
+this is not the case. If OpenGL functionality gets called before the
+@ref GL::Context instance is created (or after it has been destroyed), you may
+end up with the following assertion:
+
+@code{.shell-session}
+GL::Context::current(): no current context
+@endcode
+
+In the common case of delayed context creation, this problem can be
+circumvented by constructing the OpenGL objects using the @ref NoCreate tag
+first and populating them with live instances once the context is ready. For
+example:
+
+@snippet MagnumGL-application.cpp opengl-wrapping-nocreate
+
+Please note that objects constructed using the @ref NoCreate tag are equivalent
+to the moved-from state, and thus again calling anything on these may result in
+OpenGL errors being generated or even application crashes --- you're
+responsible for correctly initializing the objects before use. If you are fine
+with trading some overhead for extra safety checks, wrap the objects in an
+@relativeref{Corrade,Containers::Optional} instead of using the @ref NoCreate
+constructor.
+
+The @ref NoCreate constructor is available for all OpenGL classes, including
+builtin shaders.
@section opengl-state-tracking State tracking and interaction with third-party code
diff --git a/doc/platform.dox b/doc/platform.dox
index bae1a6483..557c079ae 100644
--- a/doc/platform.dox
+++ b/doc/platform.dox
@@ -143,22 +143,49 @@ this:
@snippet MagnumPlatform.cpp configuration
-However, sometimes you would need to configure the application based on some
-configuration file or system introspection. In that case you can pass
-@ref NoCreate instead of @ref Platform::Sdl2Application::Configuration "Configuration"
-instance and then specify it later with @ref Platform::Sdl2Application::create() "create()":
+@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 the context creation in constructor or @ref Platform::Sdl2Application::create() "create()"
-fails, the application exits. However, it is also possible to negotiate the
-context using @ref Platform::Sdl2Application::tryCreate() "tryCreate()". 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:
+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
+
+
+@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
diff --git a/doc/snippets/CMakeLists.txt b/doc/snippets/CMakeLists.txt
index 9af7aca4b..f1c6299e6 100644
--- a/doc/snippets/CMakeLists.txt
+++ b/doc/snippets/CMakeLists.txt
@@ -203,7 +203,7 @@ if(WITH_SDL2APPLICATION AND TARGET_GL)
add_library(snippets-MagnumPlatform STATIC
MagnumPlatform.cpp
- MagnumGL-framebuffer.cpp)
+ MagnumGL-application.cpp)
target_link_libraries(snippets-MagnumPlatform PRIVATE MagnumSdl2Application)
set_target_properties(
diff --git a/doc/snippets/MagnumGL-framebuffer.cpp b/doc/snippets/MagnumGL-application.cpp
similarity index 80%
rename from doc/snippets/MagnumGL-framebuffer.cpp
rename to doc/snippets/MagnumGL-application.cpp
index 70b82db50..e17606270 100644
--- a/doc/snippets/MagnumGL-framebuffer.cpp
+++ b/doc/snippets/MagnumGL-application.cpp
@@ -26,11 +26,38 @@
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/DefaultFramebuffer.h"
#include "Magnum/GL/Framebuffer.h"
+#include "Magnum/GL/Mesh.h"
#include "Magnum/Platform/Sdl2Application.h"
#include "Magnum/Platform/GLContext.h"
+#include "Magnum/Shaders/PhongGL.h"
using namespace Magnum;
+#define DOXYGEN_IGNORE(...) __VA_ARGS__
+
+/* [opengl-wrapping-nocreate] */
+class MyApplication: public Platform::Application {
+ DOXYGEN_IGNORE(explicit MyApplication(const Arguments& arguments);)
+
+ private:
+ /* Placeholders without an underlying GL object */
+ GL::Mesh _mesh{NoCreate};
+ Shaders::PhongGL _shader{NoCreate};
+ DOXYGEN_IGNORE()
+};
+
+MyApplication::MyApplication(const Arguments& arguments):
+ Platform::Application{arguments, NoCreate}
+{
+ DOXYGEN_IGNORE()
+ create();
+
+ /* GL context is ready, now it's safe to populate the GL objects */
+ _mesh = GL::Mesh{};
+ _shader = Shaders::PhongGL{};
+}
+/* [opengl-wrapping-nocreate] */
+
struct A: Platform::Sdl2Application {
/* [DefaultFramebuffer-usage-viewport] */
void viewportEvent(ViewportEvent& event) override {
diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp
index e11b93612..5876a4c3d 100644
--- a/doc/snippets/MagnumGL.cpp
+++ b/doc/snippets/MagnumGL.cpp
@@ -120,23 +120,6 @@ carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256})
}
#endif
-{
-#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11
-#pragma GCC diagnostic push
-/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */
-#pragma GCC diagnostic ignored "-Wvexing-parse"
-#endif
-auto importSomeMesh() -> std::tuple;
-#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11
-#pragma GCC diagnostic pop
-#endif
-/* [opengl-wrapping-nocreate] */
-GL::Mesh mesh{NoCreate};
-GL::Buffer vertices{NoCreate}, indices{NoCreate};
-std::tie(mesh, vertices, indices) = importSomeMesh();
-/* [opengl-wrapping-nocreate] */
-}
-
{
struct Foo {
void setSomeBuffer(GLuint) {}
diff --git a/doc/snippets/MagnumPlatform.cpp b/doc/snippets/MagnumPlatform.cpp
index 7da163055..a1f312e97 100644
--- a/doc/snippets/MagnumPlatform.cpp
+++ b/doc/snippets/MagnumPlatform.cpp
@@ -89,9 +89,9 @@ struct MyApplication: Platform::Application {
/* [configuration] */
MyApplication::MyApplication(const Arguments& arguments):
- Platform::Application(arguments, Configuration{}
+ Platform::Application{arguments, Configuration{}
.setTitle("My Application")
- .setSize({800, 600}))
+ .setSize({800, 600})}
{
// ...
}
diff --git a/doc/troubleshooting.dox b/doc/troubleshooting.dox
index 909788932..cc618cc8b 100644
--- a/doc/troubleshooting.dox
+++ b/doc/troubleshooting.dox
@@ -25,29 +25,87 @@
namespace Magnum {
/** @page troubleshooting Troubleshooting
-@brief Various tricks to overcome common building and rendering issues.
+@brief Various tricks and solutions to common pitfalls.
+@tableofcontents
@m_footernavigation
@section troubleshooting-building Building issues
-If your project suddenly stops building after Magnum upgrade, check these
+If your project suddenly stops building after a Magnum upgrade, check these
things:
-- If the building fails on CMake step, be sure that you have up-to-date
+- If building fails on the CMake step, be sure that you have up-to-date
`FindCorrade.cmake`, `FindMagnum.cmake` and other CMake modules in your
- project (`FindSDL2.cmake`). They are contained in `modules/` directory of
- Magnum sources (and sources of other projects) also are installed into
- `share/cmake/Magnum`.
-- In some cases when the changes done to build system are too drastic,
- recreating the build dir or clearing CMake cache is needed, but this is
- a very rare occasion.
-- The library is constantly evolving, thus some API might get deprecated over
- time (and later removed). Either build the libraries with `BUILD_DEPRECATED`
- or switch to non-deprecated features. See @ref building for more
- information.
-
-@section troubleshooting-rendering Rendering issues
+ project (`FindSDL2.cmake`, ...). They are contained in `modules/` directory
+ of Magnum repositories and also get installed into `share/cmake/Magnum`.
+- In very rare cases the CMake build directory may not survive an upgrade. If
+ you get some particularly cursed errors, try recreating it from scratch.
+- The library is constantly evolving, meaning that APIs may get deprecated
+ and removed over time. If you upgrade to a version that deprecated a
+ particular API you use, the code will emit a deprecation warning,
+ suggesting a migration path. Except for rare cases, the deprecated APIs are
+ guaranteed to stay in the codebase for a year at least (or two subsequent
+ version releases, whichever is longer) and only then they get removed. To
+ make sure you're no longer using any deprecated functionality, it's
+ possible to disable the `BUILD_DEPRECATED` @ref building-features "CMake option"
+ when building Magnum, although for a smoother experience it's recommended
+ to flip this option back on when upgrading. A list of deprecated APIs is
+ maintained @ref changelog-latest-deprecated "in the changelog".
+- In some rare cases, it's impossible to provide a deprecated migration path
+ and an API gets changed in a backwards-incompatible manner, directly
+ leading to a compile error. Another possibility is that transitive header
+ dependencies got cleaned up for @ref compilation-forward-declarations "speeding up compilation" and you're now missing an @cpp #include @ce. The major
+ compatiblity breakages are always listed @ref changelog-latest-compatibility "in the changelog".
+- If you upgrade from a really old release, it's recommended to gradually
+ upgrade over tagged versions of Magnum and not jumping directly to latest
+ version. That way you're more likely to follow the deprecation path,
+ instead of directly ending up with long-deprecated APIs being gone with no
+ direct way to know what should be used instead. If in doubt, browse the
+ @ref changelog "old changelogs".
+
+@section troubleshooting-runtime Runtime issues
+
+Magnum makes heavy use of assertions to catch programmer errors (as opposed to
+runtime or data-dependent errors, which get handled in more graceful ways), and
+because past experience showed their usefulness, majority of assertions is
+@ref CORRADE_NO_ASSERT "by default kept even in release builds".
+
+If your application abruptly exits, it's important to know whether it was a
+regular *exit*, an *abort* or a *crash*, as each of these may point to a
+different problem.
+
+- Except for crashes, in which case the application usually won't even have a
+ chance to complain about anything, you should get a message on the standard
+ error output:
+ - On Unix systems the output can be seen when running from a console
+ - On Windows, if you don't see the console, switch the build back to a
+ console app @ref platform-windows-hiding-console "instead of a GUI app"
+ --- in case of CMake you can temporarily remove the `WIN32` part from
+ your @cmake add_executable() @ce call
+ - On Android @ref platforms-android-output-redirection "you can use logcat"
+ - On Emscripten the output is printed to the browser console
+- A regular exit may happen during startup due to an error in arguments
+ passed on the command line, or when a window / context creation fails. The
+ error output should mention what happened.
+- An abort is an assertion failure, with the error message telling you why.
+ It's an abort in order to trigger a debugger break (or the assertion dialog
+ on Windows) or create a core dump (on Unix systems) so you can see the
+ backtrace leading to the error.
+ - One of the more frequent assertion messages is
+ @cb{.shell-session} GL::Context::current(): no current context @ce.
+ This happens when you try to use OpenGL functionality when the OpenGL
+ context is not yet created (or no longer exists). See
+ @ref opengl-wrapping-instances-nocreate for more information.
+- A crash is usually a memory issue. To find the root cause, by far the
+ easiest is to hook up AddressSanitizer (`-fsanitize=address` on GCC, Clang
+ and [recent MSVC](https://docs.microsoft.com/en-us/cpp/sanitizers/asan)).
+ It can detect out-of-bounds accesses, use-after-free, leaks and other
+ issues and upon discovering a problem it prints a lengthy diagnostic about
+ what happened, where does the memory come from and what code touched it and
+ how.
+
+@section troubleshooting-opengl OpenGL issues
If you are experiencing the so-called "black screen of death", weird behavior
or crashes on GL calls, you might want to try these things:
@@ -99,7 +157,7 @@ or crashes on GL calls, you might want to try these things:
having similar ability to reset its state tracker), otherwise you may need
to save and restore GL state manually for that library to work.
-@section troubleshooting-debugging Debugging rendering
+@subsection troubleshooting-opengl-debugging Debugging rendering
- Use @ref GL::TimeQuery to find hot spots in the rendering code.
- @ref GL::DebugMessage::insert() "Mark relevant parts of code" to find them
diff --git a/src/Magnum/GL/Context.h b/src/Magnum/GL/Context.h
index 94d7a4337..fe01327be 100644
--- a/src/Magnum/GL/Context.h
+++ b/src/Magnum/GL/Context.h
@@ -204,12 +204,12 @@ instances for other OpenGL contexts, *first* you need to "unset" the current one
with @ref makeCurrent() and *then* create another instance, which will then
become implicitly active:
-@snippet MagnumGL-framebuffer.cpp Context-makeCurrent-nullptr
+@snippet MagnumGL-application.cpp Context-makeCurrent-nullptr
Once all needed instances are created, switch between them right after making
the underlying GL context current:
-@snippet MagnumGL-framebuffer.cpp Context-makeCurrent
+@snippet MagnumGL-application.cpp Context-makeCurrent
@section GL-Context-multithreading Thread safety
diff --git a/src/Magnum/GL/DefaultFramebuffer.h b/src/Magnum/GL/DefaultFramebuffer.h
index 3f01d0ff7..27390c828 100644
--- a/src/Magnum/GL/DefaultFramebuffer.h
+++ b/src/Magnum/GL/DefaultFramebuffer.h
@@ -49,7 +49,7 @@ classes, pass the new size in your
@ref Platform::Sdl2Application::viewportEvent() "viewportEvent()"
implementation, for example:
-@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-viewport
+@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-viewport
Next thing you probably want is to clear all used buffers before performing
any drawing. Again, in case you're using one of the
@@ -57,7 +57,7 @@ any drawing. Again, in case you're using one of the
@ref Platform::Sdl2Application::drawEvent() "drawEvent()" implementation, for
example:
-@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-clear
+@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-clear
See documentation of particular functions and @ref Framebuffer documentation
for more involved usage, usage of non-default or multiple framebuffers.