gstreamer/markdown/additional/design/opengl.md
2020-03-10 13:34:24 +00:00

241 lines
10 KiB
Markdown

# OpenGL
OpenGL is a venerable, cross-platform 3D graphics API available for use
on Linux, Windows, MacOS, iOS and Android and is usually backed by
specialized hardware (a GPU) to accelarate rendering. There are however,
also CPU-based software implementations available for ensuring correctness and
OpenGL use without a GPU.
## Limits imposed by OpenGL
### OpenGL and Threads
A major design decision of OpenGL has an OpenGL context only being available
(or current) for use in a single thread. This is directly at odds with
GStreamer's multi-threaded design and requires guidance and/or restrictions on
how GStreamer and the application will interact with OpenGL. There are two
main models for using OpenGL from multiple threads.
The first involves creating an OpenGL context for each thread that will use
OpenGL in such a way that all the OpenGL contexts can share OpenGL resources.
While this does ensure that each thread can execute and use OpenGL resources
concurrently, it has a high resources cost for two main reasons.
1. OpenGL contexts are expensive to create and maintain. e.g. a single OpenGL
context can use multiple MB of memory for storing all the required state.
2. Transfering between different OpenGL contexts requires synchronisation.
This incurs a penalty at runtime performing the required synchronisation
as well as an increase in code complexity.
The second involves making the OpenGL context current whenever it is
needed. On some platforms this is possible to perform lazily as the context
will be uncurrented in the previous thread automatically. Regrettably this
behaviour is not guaranteed for all platforms and means that transfering an
OpenGL context between threads requires explicitly uncurrenting from the
previous thread. The other issue with this is performance related where on
some platforms, making an OpenGL current could could consume a large amount of
CPU time.
GStreamer takes a different approach to this by marshalling all the OpenGL
functionality to a dedicated OpenGL thread that is created and destroyed with
the `GstGLContext`. This removes the synchronisation requirements between
multiple GStreamer OpenGL contexts, removes the need for multiple OpenGL
contexts inside GStreamer reducing the memory requirements, and avoids the
possibly expensive currenting and uncurrenting of the OpenGL context multiple
times per frame. The marshalling is performed in the `GstGLWindow`
implementation by `gst_gl_window_send_message()` /
`gst_gl_window_send_message_async()` and is required to be re-entrant by
ensuring that marshalling from the OpenGL thread must execute the marshalled
operation immediately. The only downside of marshalling everything to a
dedicated thread has is that OpenGL in GStreamer requires two
thread switches for calling a block of OpenGL commands from outside the
OpenGL thread. Given the possibly high cost of the other approaches
this is an acceptable trade-off.
### OpenGL Function Pointers
Another design choice of OpenGL is that (at least on the windows platform)
OpenGL functions can be OpenGL context specific. This means that retrieving a
function pointer from one OpenGL context has no guarentee of doing the correct
thing with any other OpenGL context. Each `GstGLContext` has a `GstGLFuncs`
structure that contains a list of OpenGL functions known to libgstgl that is
populated by a call to `gst_gl_context_fill_info()`.
## libgstgl Library
This library provides the necessary infrastructure for integrating OpenGL usage
in GStreamer between elements in the pipeline and an application that may use
OpenGL. The library can be split into 3 main parts:
- Platform-specific wrappers (`GstGLDisplay`, `GstGLWindow`, `GstGLContext`)
- GStreamer subclasses
- Element base clases (`GstGLBaseFilter`, `GstGLFilter`, `GstGLBaseMixer`, etc)
- Data/Memory (`GstGLBuffer`, `GstGLMemory`, `GstGLBufferPool`, etc)
- Synchronisation (`GstGLSyncMeta`)
- OpenGL helpers (`GstGLShader`, `GstGLFramebuffer`, etc)
### Platform Specifics
Due to the nature of OpenGL, the use of OpenGL requires a connection to a
windowing system (X11, Wayland, Cocoa, GDI+, etc). These windowing systems
are entirely platform specific and are wrapped in the `GstGLDisplay` generic
interface. Specific functionality exposed by a windowing system may be exposed
by platform-specific `GstGLDisplay` objects. e.g. `GstGLDisplayWayland` or
`GstGLDisplayX11`.
Another platform specific part of the puzzle is the `GstGLWindow` object which
encapsulates the surface that is being rendered into by OpenGL. There are
specific windowing system implementations for each platform that are all exposed
through the generic `GstGLWindow` interface. An application generally does not
need to interact with the window used by an OpenGL context with GStreamer.
The last platform specific piece is the `GstGLContext` which represents a
OpenGL context. A `GstGLContext` has two main forms, wrapped (created with
`gst_gl_context_new_wrapped()`) vs not. Some `GstGLContext` API behaves
differently with a wrapped `GstGLContext`.
#### Wrapped `GstGLContext`s
A wrapped `GstGLContext` is simply a container to an OpenGL context handle and
does not contain any separate thread to execute the OpenGL operations on.
This means that `gst_gl_context_thread_add()` with a wrapped `GstGLContext`
will attempt to execute the function immediately and may fail in some way
(crashes, ignored) if the actual OpenGL context is not active in the current
thread. Even if the OpenGL context is active in the current thread, GStreamer
will not know that and needs to be told explicitly through the necessary calls
to `gst_gl_context_set_active()`.
### Application Integration
There are two types of resources required for integration between GStreamer
elements and the application, platform specific and generic OpenGL resources.
First, the platform specific resources are the `GstGLDisplay` for the
required connection to the windowing system and the `GstGLContext` for the
OpenGL context that the application is using. With these two pieces of
information, it is possible for certain OpenGL resources to be 'shared'
between GStreamer and the application subject to lifetime and synchronisation
requirements.
#### Sharing X11 Display With GStreamer
```c
static gboolean
sync_bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_NEED_CONTEXT:
{
const gchar *context_type;
GstContext *context = NULL;
gst_message_parse_context_type (msg, &context_type);
g_print("got need context %s\n", context_type);
if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
Display *x11_display; /* get this from the application somehow */
GstGLDisplay *gl_display = GST_GL_DISPLAY (gst_gl_display_x11_new_with_display (x11_display));
context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
gst_context_set_gl_display (context, gl_display);
gst_element_set_context (GST_ELEMENT (msg->src), context);
}
if (context)
gst_context_unref (context);
break;
}
default:
break;
}
return FALSE;
}
```
#### Sharing OpenGL Context With GStreamer
```c
static gboolean
sync_bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_NEED_CONTEXT:
{
const gchar *context_type;
GstContext *context = NULL;
gst_message_parse_context_type (msg, &context_type);
g_print("got need context %s\n", context_type);
if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
GstGLContext *gl_context; /* get this from the application somehow */
GstStructure *s;
context = gst_context_new ("gst.gl.app_context", TRUE);
s = gst_context_writable_structure (context);
gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, gl_context, NULL);
gst_element_set_context (GST_ELEMENT (msg->src), context);
}
if (context)
gst_context_unref (context);
break;
}
default:
break;
}
return FALSE;
}
```
Second, other OpenGL resources such as buffers or textures require wrapping
into a GStreamer compatible format. For data-specific OpenGL resources, they
are wrapped up in some form of `GstMemory`. The application can create these
resources for pushing into GStreamer or consume these resources from GStreamer.
See the resource specific sections for more details.
### `GstGLMemory`
`GstGLMemory` is a `GstMemory` subclass that holds a single plane of a video
frame in an OpenGL texture.
### `GstGLBuffer`
`GstGLBuffer` is a `GstMemory` subclass that holds data stored in an OpenGL
buffer object.
### Automatic Transfers To/From The GPU
Both `GstGLMemory` and `GstGLBuffer` implement automatic transfers to and from
OpenGL based entirely on the sequence of calls to `gst_memory_map()` /
`gst_memory_unmap()`. Combined with a special `GstMapFlags`, `GST_MAP_GL`, the
OpenGL based `GstMemory` implementations can discern where the most recent
data was written and copy the data on-demand to where it is needed on read.
Internally this is implemented using two extra `GstMemoryFlags`,
`GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD` and
`GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD` that indicate that a transfer is
needed if that specific domain (system memory or OpenGL) is mapped.
The transfer flags are automatically updated and should not be modified by
elements or the application.
## Elements
There are a number of elements that make use of the OpenGL API for their
functionality. A non-comprehensive list is provided below.
- `glimagesinkelement` - Display a video using OpenGL
- `glcolorconvert` - Convert between diferent color spaces
- `glviewconvert` - Convert between different stereo view formats
- `gltransformation` - Perfom transformations in 3D space of the 2D video plane
- `gleffects` - Various OpenGL effects
- `glvideomixerelement` - Mix video using OpenGL (roughly equivalent to compositor)
- `glcolorbalance` - Color balance filtering
- `gltestsrc` - OpenGL equivalent to videotestsrc
- `glshader` - Execute an arbitrary OpenGL shader
- `gloverlay` - Overlay an image onto a video stream
- `glupload` - Upload data into OpenGL
- `gldownload` - Download data from OpenGL