Search guides and the APICtrl K
  1. API reference
  2. Native GUI

gfx/opengl.h

OpenGL inside a native editor: a context attached to a component, the renderer callbacks, shaders, textures, framebuffers, and the component's own software-rendered painting composited over the GL picture.

#include <sonore/gfx/opengl.h>
On this page

Design notes

Why this exists when everything else here is software

The toolkit rasterises on the CPU on purpose -- identical pixels on every machine, nothing to fail to initialise -- and that is still the default and still the right answer for knobs and meters. It is the wrong answer for a 3D spectrum waterfall, a 60 fps oscilloscope with phosphor decay, or a shader-driven visualiser, which is what a plugin reaches for GL to draw. So GL is here as an OPT-IN: a component asks for a context, and nothing else in the editor changes.

How it fits into an editor that is one bitmap

The context makes a child surface (opengl_api.h) the size of its component, over it, and follows it as the layout moves. The child is transparent to the mouse, so the component tree still gets every event.

What the component PAINTS still matters: its paint() and its children -- labels, a legend, a knob over the scope -- are rasterised in software into a transparent Bitmap at the device scale, uploaded as a texture, and drawn over the GL picture after renderOpenGL() returns, with premultiplied blending. A plugin mixes GL content and ordinary widgets in one rectangle without knowing which is which. setComponentPaintingEnabled(false) turns it off for a component that draws nothing of its own.

That image is repainted on the message thread at most thirty times a second, compared with the last one, and uploaded only when it CHANGED. It is repainted on a clock rather than on repaint() because this tree records damage on the root, where the editor's own backing store clears it the moment it has drawn it -- there is nothing left for a second consumer to read. The cost is one software paint of the component per tick; a GL component's own paint is usually a few labels.

Threads

By default rendering runs on a thread of its own, paced by the swap interval -- the reference's behaviour, and the right one for a plugin, since a vsynced swap on the host's UI thread would stall the host's whole interface for up to a frame. setRenderThreadEnabled(false) renders on the message thread instead, driven by the surface's own timer, for a renderer that must touch the component tree or that simply wants no second thread.

Either way the three renderer callbacks arrive on ONE thread, in order: newOpenGLContextCreated once, renderOpenGL per frame, openGLContextClosing once -- and the closing call always happens while the surface still exists, including when the HOST destroys the editor window under us (the surface hears its own destruction and shuts the renderer down inside it). After closing returns there are no more GL calls on that context, ever.

Ownership

GL objects belong to the context that made them and can only be deleted with that context current. OpenGLShaderProgram, OpenGLTexture and OpenGLFrameBuffer release themselves in their destructors when their context is current on the destroying thread, and otherwise simply forget the handle -- deleting through no context is a crash on some drivers, and the objects die with the context anyway. Release them in openGLContextClosing.

Types 9

What a context calls to draw.

All three on the context's GL thread, with the context current: created once before the first frame, render once per frame, closing once after the last -- the place to delete every GL object the renderer made.

A GL context attached to a component in a native editor.

Configure it (renderer, pixel format, version, threading), then attachTo the component and the native window it is shown in -- the editor's own handle, NativeWindow::handle() or NativeEditor::handle(). detach() before the component goes away; the destructor detaches too, which covers a context held as a member of the component it draws.

On X11 there is no clock the surface can own: call tick() from the editor's own tick, as the native editor's peer does for itself. Windows and macOS tick the surface from a timer of its own.

Small things a renderer reaches for, gathered where the reference keeps them.

Vertex and fragment shaders linked into a program, with the driver's error log when they do not.

Built on the GL thread, with the context current. Shaders are compiled as written; for a core profile, pass them through OpenGLHelpers::translate*ToV3 first, or write #version 150 directly.

A 2D texture holding a Bitmap's pixels.

The Bitmap's premultiplied RGBA goes up as it is -- no conversion, no un-premultiplying -- so it composites with GL_ONE, GL_ONE_MINUS_SRC_ALPHA exactly as the software painter composites it. Its FIRST ROW is the top of the image, so texture coordinate t = 0 is the top; copyTexture's flag for this is false.

Render to texture: a framebuffer object with an RGBA8 texture behind it, and optionally a depth and stencil buffer.

Pixels read back with readPixels arrive in a Bitmap the right way UP -- GL stores rows bottom-first, and the flip happens here, once, so a caller comparing against a software-rendered Bitmap compares like with like.

Images that live on the GPU, and the way between them and a Bitmap.

The reference's version of this makes Image objects whose pixels are a framebuffer, so its Graphics can draw into them with GL. Here there is one rasteriser and it is the software one, so a GPU image is an OpenGLFrameBuffer, and "drawing into it with Graphics" is honestly what it is: paint() reads the pixels back, lets the ordinary Graphics draw on them, and uploads the result. The picture is identical to drawing the Bitmap directly -- which is the point of having one rasteriser.

A fragment shader that fills a rectangle -- per-pixel effects the software painter could not afford: a plasma background, a live spectrogram colour map, a glow that follows the audio.

The shader is the body of a GLSL 1.10 fragment shader. These are declared for it, and it writes gl_FragColor as PREMULTIPLIED alpha, like everything else this SDK composites:

varying vec2 pixelPos;     // logical pixels, y down, of the target
uniform vec4 screenBounds; // the rectangle being filled: x, y, w, h
uniform float pixelAlpha;  // the opacity fillRect was given

It is translated for a core profile automatically.

class OpenGLAppComponent : public Component, public OpenGLRenderer

A component that is a GL view: it owns its context, renders continuously, and turns the three renderer callbacks into initialise / render / shutdown.

Call attachToNativeWindow once the component is in an editor. A subclass MUST call shutdownOpenGL() in its own destructor: the render thread calls render() and shutdown(), which are the subclass's, and by the time this base destructor runs the subclass part no longer exists.

↑↓ to move↵ to openEvery public name in the headers, and every guide