Browse Source

doc: add docs for GL context asserts and other troubleshooting.

pull/529/head
Vladimír Vondruš 5 years ago
parent
commit
2bd933d3ef
  1. 18
      doc/compilation-speedup.dox
  2. 94
      doc/opengl-wrapping.dox
  3. 47
      doc/platform.dox
  4. 2
      doc/snippets/CMakeLists.txt
  5. 27
      doc/snippets/MagnumGL-application.cpp
  6. 17
      doc/snippets/MagnumGL.cpp
  7. 4
      doc/snippets/MagnumPlatform.cpp
  8. 90
      doc/troubleshooting.dox
  9. 4
      src/Magnum/GL/Context.h
  10. 4
      src/Magnum/GL/DefaultFramebuffer.h

18
doc/compilation-speedup.dox

@ -25,27 +25,27 @@
namespace Magnum { namespace Magnum {
/** @page compilation-speedup Speeding up compilation /** @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 @tableofcontents
@m_footernavigation @m_footernavigation
@section compilation-forward-declarations Forward declarations instead of includes @section compilation-forward-declarations Forward declarations instead of includes
Essential thing when speeding up compilation is reducing number of @cpp #include @ce An essential thing when speeding up compilation is reducing number of
directives in both headers and source files. Magnum is strictly applying this @cpp #include @ce directives in both headers and source files. Magnum is
policy in all header files, so all types which are not directly used in the strictly applying this policy in all header files, so all types which are not
header have only forward declarations. directly used in the header have only forward declarations.
For example, when including @ref Magnum.h, you get shortcut typedefs for 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 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. actually use any of them, you have to include the respective header, e.g.
@ref Magnum/Math/Vector3.h. @ref Magnum/Math/Vector3.h.
You are encouraged to use forward declarations also in your code. However, for You are encouraged to use forward declarations in your code as well. However,
some types it can be too cumbersome --- e.g. too many template parameters, 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 default template arguments, typedefs etc. Instead, forward declaration headers
available, each namespace has its own: are available, with each namespace having its own:
- @ref Corrade/Corrade.h - @ref Corrade/Corrade.h
- @ref Corrade/Containers/Containers.h - @ref Corrade/Containers/Containers.h

94
doc/opengl-wrapping.dox

@ -31,9 +31,9 @@ namespace Magnum {
@m_footernavigation @m_footernavigation
The purpose of the @ref GL library is to simplify interaction with the OpenGL 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 API using type-safe C++11 features and RAII, abstracting away extension and
differences, tracking the state for optimum performance and selecting the best platform differences, tracking the state for optimum performance and selecting
available code path for given system. the best available code path for given driver.
Magnum provides wrappers for most native OpenGL objects like buffers, textures, Magnum provides wrappers for most native OpenGL objects like buffers, textures,
meshes, queries, transform feedback objects, shaders etc., but makes it 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 @section opengl-wrapping-instances OpenGL object wrapper instances
By default, all underlying OpenGL objects are created in wrapper class By default, all wrapper classes follow RAII -- underlying OpenGL objects are
constructor and deleted in wrapper class destructor. Constructing an object created in the class constructor and deleted in the wrapper class destructor.
using default constructor requires active @ref GL::Context instance. All OpenGL All OpenGL objects are movable (but not copyable) and moves are *destructive*
objects are movable (but not copyable), although for performance reasons (and --- the moved-from instance does *not* have any associated OpenGL object and
contrary to standard C++11 practice), the moved-from instance does *not* have is thus in an *invalid state*. Calling anything on instances in a moved-from
any associated OpenGL object and is thus in *invalid state*. Using instance in state may thus result in OpenGL errors being generated, in some cases even
moved-from state may result in OpenGL errors being generated, in some cases application crashes.
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
<b></b>
@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.
If you need to preserve the underlying OpenGL object after destruction, you can 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 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 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 ID of the underlying without releasing it using @cpp id() @ce). It is also
do the opposite --- wrapping existing OpenGL object ID into Magnum object possible to do the opposite --- wrapping an existing OpenGL object ID into a
instance using @cpp wrap() @ce. Magnum object instance using @cpp wrap() @ce:
@snippet MagnumGL.cpp opengl-wrapping-transfer @snippet MagnumGL.cpp opengl-wrapping-transfer
The @cpp NoCreate @ce constructor, @cpp wrap() @ce and @cpp release() @ce The @cpp wrap() @ce and @cpp release() @ce functions are available for all
functions are available for all OpenGL classes except @ref GL::Shader, where OpenGL classes except for @ref GL::Shader, instances of which are rather
wrapping external instances makes less sense. short-lived and thus wrapping external instances makes less sense.
Note that interaction with third-party OpenGL code as shown above usually needs @attention
special 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 @section opengl-state-tracking State tracking and interaction with third-party code

47
doc/platform.dox

@ -143,22 +143,49 @@ this:
@snippet MagnumPlatform.cpp configuration @snippet MagnumPlatform.cpp configuration
However, sometimes you would need to configure the application based on some @subsection platform-configuration-delayed Delayed context creation
configuration file or system introspection. In that case you can pass
@ref NoCreate instead of @ref Platform::Sdl2Application::Configuration "Configuration" Sometimes you may want to set up the application based on a configuration file
instance and then specify it later with @ref Platform::Sdl2Application::create() "create()": 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 @snippet MagnumPlatform.cpp createcontext
If the context creation in constructor or @ref Platform::Sdl2Application::create() "create()" If context creation in the constructor or in
fails, the application exits. However, it is also possible to negotiate the @relativeref{Platform::Sdl2Application,create()} fails, the application prints
context using @ref Platform::Sdl2Application::tryCreate() "tryCreate()". The an error message to standard output and exits. While that frees you from having
only difference is that this function returns `false` instead of exiting. You to do explicit error handling, sometimes a more graceful behavior may be
can for example try enabling MSAA and if the context creation fails, fall back desirable --- with @relativeref{Platform::Sdl2Application,tryCreate()} the
to no-AA rendering: 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 @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 @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 In case you want to use some not-yet-supported toolkit or you don't want to use

2
doc/snippets/CMakeLists.txt

@ -203,7 +203,7 @@ if(WITH_SDL2APPLICATION AND TARGET_GL)
add_library(snippets-MagnumPlatform STATIC add_library(snippets-MagnumPlatform STATIC
MagnumPlatform.cpp MagnumPlatform.cpp
MagnumGL-framebuffer.cpp) MagnumGL-application.cpp)
target_link_libraries(snippets-MagnumPlatform PRIVATE MagnumSdl2Application) target_link_libraries(snippets-MagnumPlatform PRIVATE MagnumSdl2Application)
set_target_properties( set_target_properties(

27
doc/snippets/MagnumGL-framebuffer.cpp → doc/snippets/MagnumGL-application.cpp

@ -26,11 +26,38 @@
#include "Magnum/GL/Buffer.h" #include "Magnum/GL/Buffer.h"
#include "Magnum/GL/DefaultFramebuffer.h" #include "Magnum/GL/DefaultFramebuffer.h"
#include "Magnum/GL/Framebuffer.h" #include "Magnum/GL/Framebuffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/Platform/Sdl2Application.h" #include "Magnum/Platform/Sdl2Application.h"
#include "Magnum/Platform/GLContext.h" #include "Magnum/Platform/GLContext.h"
#include "Magnum/Shaders/PhongGL.h"
using namespace Magnum; 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 { struct A: Platform::Sdl2Application {
/* [DefaultFramebuffer-usage-viewport] */ /* [DefaultFramebuffer-usage-viewport] */
void viewportEvent(ViewportEvent& event) override { void viewportEvent(ViewportEvent& event) override {

17
doc/snippets/MagnumGL.cpp

@ -120,23 +120,6 @@ carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256})
} }
#endif #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<GL::Mesh, GL::Buffer, GL::Buffer>;
#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 { struct Foo {
void setSomeBuffer(GLuint) {} void setSomeBuffer(GLuint) {}

4
doc/snippets/MagnumPlatform.cpp

@ -89,9 +89,9 @@ struct MyApplication: Platform::Application {
/* [configuration] */ /* [configuration] */
MyApplication::MyApplication(const Arguments& arguments): MyApplication::MyApplication(const Arguments& arguments):
Platform::Application(arguments, Configuration{} Platform::Application{arguments, Configuration{}
.setTitle("My Application") .setTitle("My Application")
.setSize({800, 600})) .setSize({800, 600})}
{ {
// ... // ...
} }

90
doc/troubleshooting.dox

@ -25,29 +25,87 @@
namespace Magnum { namespace Magnum {
/** @page troubleshooting Troubleshooting /** @page troubleshooting Troubleshooting
@brief Various tricks to overcome common building and rendering issues. @brief Various tricks and solutions to common pitfalls.
@tableofcontents
@m_footernavigation @m_footernavigation
@section troubleshooting-building Building issues @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: 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 `FindCorrade.cmake`, `FindMagnum.cmake` and other CMake modules in your
project (`FindSDL2.cmake`). They are contained in `modules/` directory of project (`FindSDL2.cmake`, ...). They are contained in `modules/` directory
Magnum sources (and sources of other projects) also are installed into of Magnum repositories and also get installed into `share/cmake/Magnum`.
`share/cmake/Magnum`. - In very rare cases the CMake build directory may not survive an upgrade. If
- In some cases when the changes done to build system are too drastic, you get some particularly cursed errors, try recreating it from scratch.
recreating the build dir or clearing CMake cache is needed, but this is - The library is constantly evolving, meaning that APIs may get deprecated
a very rare occasion. and removed over time. If you upgrade to a version that deprecated a
- The library is constantly evolving, thus some API might get deprecated over particular API you use, the code will emit a deprecation warning,
time (and later removed). Either build the libraries with `BUILD_DEPRECATED` suggesting a migration path. Except for rare cases, the deprecated APIs are
or switch to non-deprecated features. See @ref building for more guaranteed to stay in the codebase for a year at least (or two subsequent
information. version releases, whichever is longer) and only then they get removed. To
make sure you're no longer using any deprecated functionality, it's
@section troubleshooting-rendering Rendering issues 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 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: 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 having similar ability to reset its state tracker), otherwise you may need
to save and restore GL state manually for that library to work. 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. - Use @ref GL::TimeQuery to find hot spots in the rendering code.
- @ref GL::DebugMessage::insert() "Mark relevant parts of code" to find them - @ref GL::DebugMessage::insert() "Mark relevant parts of code" to find them

4
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 with @ref makeCurrent() and *then* create another instance, which will then
become implicitly active: 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 Once all needed instances are created, switch between them right after making
the underlying GL context current: the underlying GL context current:
@snippet MagnumGL-framebuffer.cpp Context-makeCurrent @snippet MagnumGL-application.cpp Context-makeCurrent
@section GL-Context-multithreading Thread safety @section GL-Context-multithreading Thread safety

4
src/Magnum/GL/DefaultFramebuffer.h

@ -49,7 +49,7 @@ classes, pass the new size in your
@ref Platform::Sdl2Application::viewportEvent() "viewportEvent()" @ref Platform::Sdl2Application::viewportEvent() "viewportEvent()"
implementation, for example: 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 Next thing you probably want is to clear all used buffers before performing
any drawing. Again, in case you're using one of the 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 @ref Platform::Sdl2Application::drawEvent() "drawEvent()" implementation, for
example: example:
@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-clear @snippet MagnumGL-application.cpp DefaultFramebuffer-usage-clear
See documentation of particular functions and @ref Framebuffer documentation See documentation of particular functions and @ref Framebuffer documentation
for more involved usage, usage of non-default or multiple framebuffers. for more involved usage, usage of non-default or multiple framebuffers.

Loading…
Cancel
Save