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

gfx/vblank.h

VBlankAttachment: a callback once per display refresh, on the UI thread.

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

Design notes

Why a plugin wants the refresh, when it has a 33 ms tick

The editor's own tick is the right clock for following automation and for meters that do not need to be smoother than the eye can track a number. It is the wrong clock for MOTION: a scope trace, a spectrum, a momentum scroll, anything that animates continuously. 30 Hz against a 60 Hz display shows every frame twice, and a tick that is not locked to the display drifts against it and judders -- a frame shown three times, then once. Drawing once per refresh, when the refresh happens, is the only way motion looks smooth.

How each platform hears the refresh

Windows
A worker thread waits on the display -- IDXGIOutput::WaitForVBlank on the primary output, or DwmFlush where DXGI is unavailable, or a sleep at the monitor's rate as a last resort -- and posts ONE message to a message-only window on the UI thread. One: a flag stays set until the UI thread has handled it, so a UI thread that falls behind sees one late frame rather than a queue of them. dxgi.dll and dwmapi.dll are loaded at run time, never linked.
macOS
CVDisplayLink, whose callback runs on its own thread, hops to the main queue with dispatch_async_f under the same one-pending rule. CoreVideo and libdispatch are looked up at run time. Compiled against the shim, never run.
X11
No display link. A timer at the monitor's refresh rate (XRandR's current rate, 60 Hz if it cannot be read), checked each time the X11 peer pumps its events -- so the callback rate is the lower of the monitor's and however often the host pumps the window. Said plainly because it is a real limit: an X11 plugin has no thread of its own on which to run a timer that may touch the UI.

What an attachment is attached to

A component, as in the reference: the callback runs only while that component is in a window (routerFor() finds one), so an editor that is closed but not yet destroyed is not animated for nobody. A null component means "always".

Types 1

Call callback once per display refresh, on the UI thread, for as long as this object lives and component is in a window.

The argument is the refresh's timestamp in seconds -- the same clock for every attachment, so two animations driven by it agree about the time.

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