Releases: stoatworks-labs/resolume-ofx-bridge
Release list
v0.5.1
v0.5.0
v0.5.0 — OFX Bridge.app A window around the generator: pick plugins, pick a destination, press Start, watch the log. Generation moved into a shared library so the app and the ofxgen CLI produce bundles by the same code, and generated bundles now have com.apple.quarantine cleared.
v0.4.0 — OpenCL render path
Adds the OFX OpenCL render path, verified, and a CUDA path that is explicitly not.
OpenCL — working
| CPU | Metal | OpenCL | |
|---|---|---|---|
| 1080p | 2.22 ms | 0.42 ms | 0.53 ms |
| 4K | 2.95 ms | 0.64 ms | 1.07 ms |
4.2× faster than the CPU path at 1080p. Metal still wins when a plugin offers both — OpenCL on macOS is a deprecated compatibility layer over Metal and carries an extra hop.
Where Metal shares an IOSurface, clCreateFromGLBuffer takes a GL buffer rather than a texture, so the shared object is a pixel buffer object. glReadPixels into a pixel-pack buffer and glTexSubImage2D out of a pixel-unpack buffer both stay on the GPU, so despite the extra hop no pixel reaches the CPU.
Verified with a purpose-built GPU-only test plugin — no OpenCL OFX plugin is publicly available either. Gain at 0.5 halves every channel including alpha; at 1.0 it is bit-exact identity, which rules out a blit-through that would otherwise look like success.
In production OpenCL matters on Windows and Linux, where Resolve uses it on AMD hardware. It is testable here only because macOS still ships a functional (if deprecated) OpenCL.
CUDA — written, never run
This path is UNVERIFIED. It has never been compiled against the CUDA toolkit and never executed.
CUDA needs an NVIDIA GPU. macOS has not supported one since 10.13 and Apple Silicon never has, so there is no hardware here to compile or run it on — and no way to write a CUDA test plugin that could be executed.
What exists: detection works (ofxprobe reports cuda: yes and flags the bridge as unverified), and Effect::renderCuda is written from the OFX specification. What does not: the GL↔CUDA interop, deliberately not written blind, because there would be no way to tell whether it was right.
The host does not advertise CUDA support unless built with -DOFXBRIDGE_ENABLE_CUDA. A CUDA plugin is therefore declined cleanly rather than failing confusingly mid-render.
Every other path in this project is proven against a real plugin. This is the one exception, and it says so in the probe output, the headers, the code and the docs.
Unchanged
macOS universal, unsigned, Filter context only, and still never loaded into Resolume itself. GPU self-tests skip on CI (hosted runners have no GPU) — all GPU numbers come from a real M4 Max.
See CHANGELOG.md and docs/04-gpu-acceleration.md.
v0.3.1 — OFX Metal render path
Adds the OFX Metal render path — the one Resolve-targeted plugins on macOS actually use.
Status: working prototype. Verified on real hardware by the harnesses in this repo. It has still not been loaded into Resolume itself. See docs/03-verification.md.
What this changes
| CPU path | Metal path | ||
|---|---|---|---|
| 1080p | 2.14 ms | 0.40 ms | 5.4× |
| 4K | 3.20 ms | 0.90 ms | 3.6× |
The speed is the smaller half of the story. The larger half: GPU-only plugins can now run at all. Before this they didn't render slowly — they refused to render.
How it works
Metal passes id<MTLBuffer> — linear memory, not a texture — so something has to bridge Resolume's GL texture to a Metal buffer and back. IOSurface does it with no copy: one surface backs both a GL texture (CGLTexImageIOSurface2D) and an MTLBuffer (newBufferWithBytesNoCopy over its base address). Both views address the same physical memory, so on Apple Silicon a GL blit into the surface is immediately visible to Metal.
Two on-GPU blits per frame, zero CPU copies.
The test plugin
This release includes testplugins/metal-gain/, a Metal OFX plugin written for the purpose. No such plugin is publicly available: every OpenFX example is CPU or OpenGL, and the commercial plugins that do implement Metal are precisely the ones that refuse to load in an unrecognised host.
It's deliberately GPU-only — it returns kOfxStatErrImageFormat if the host hasn't enabled Metal — so a host that silently falls back to CPU is caught rather than flattered.
Verified
Gain at 0.5 halves every channel including alpha (128,128,128,255 → 64,64,64,127); at 2.0 it saturates; at its default of 1.0 it's bit-exact identity, which rules out a blit-through that would otherwise look like success. The demo image is visually identical to the CPU result, confirming channel order survives the BGRA IOSurface round trip — a uniform gain alone would hide a channel swap.
All GPU measurements come from a real M4 Max, not from CI. GitHub's hosted macOS runners have no GPU (Apple Software Renderer), so the GPU paths skip there with a notice. v0.3.1 fixes a check that passed on a black frame because it only looked at the exit code; the self-tests now assert pixel values.
Still missing
CUDA and OpenCL — what Resolve uses on Windows and Linux, platforms this project has never been compiled for.
Install unchanged: unzip, ./ofxgen generate --out ~/Documents/Resolume\ Arena/Extra\ Effects, clear quarantine. See CHANGELOG.md.
v0.3.0
v0.3.0 — OFX Metal render path GPU rendering via Metal, the path Resolve-targeted plugins on macOS use. 5.4x faster at 1080p, and GPU-only plugins can now run at all. Includes a purpose-built Metal OFX test plugin, since none is publicly available.
v0.2.0 — OFX OpenGL render path
Adds the OFX OpenGL render path: a plugin that advertises it is handed the GL texture directly, and no pixel crosses to the CPU. Negotiated per plugin and recorded in the manifest; the CPU path remains the fallback. On the GL path no CPU frames are allocated at all — 66 MB saved per instance at 4K.
Status: working prototype. Verified against real OFX plugins by the harnesses in this repo, and re-run in CI on every release. It has still not been loaded into Resolume itself. See docs/03-verification.md.
Where a frame actually goes
Before building this I measured, and it changed the plan. On an M4 Max with the Gain example, pipelined:
| per frame | of a 60fps budget | |
|---|---|---|
| 1080p | 2.25 ms | 13% |
| 4K | 2.94 ms | 18% |
The transfer costs ~2.2 ms at both resolutions — it barely grows when the pixel count goes up 4×, so it's bound by synchronisation latency, not bandwidth. That's unified memory; the same code on a discrete GPU crossing PCIe twice per frame would look far worse.
So on Apple Silicon the CPU round trip is not the main cost. The two things that matter are a heavy plugin's own CPU render (a blur or denoise is 10–100 ms and blows the budget by itself), and GPU-only plugins, which today fail outright rather than run slowly.
New: OFXBRIDGE_TIMING=1 for per-stage timing, and ffgltest --bench / --size to measure properly.
The limitation you should know about
An OFX plugin using the OpenGL render path can only work inside Resolume if it draws with core-profile GL.
Resolume uses a core profile (FFGL 2.x shaders are #version 410 core), and macOS offers no compatibility profile above 2.1. The OpenFX OpenGL example draws with immediate mode (glBegin/glVertex2f), so it renders correctly under the new --legacy-gl flag and produces GL_INVALID_OPERATION and a black frame in the profile Resolume actually uses. No host-side change can fix that — it's a property of the plugin.
There is currently no core-profile OFX GL plugin available to test against, so that combination remains unproven.
Why this may still not help you much
Resolve doesn't use the OFX OpenGL render path, so plugins written for Resolve — the stated audience — generally don't implement it. Combined with the core-profile constraint, the set of plugins this accelerates today is narrow.
It was still worth building: the negotiation, manifest plumbing, per-path clip handling and context lifecycle are all reused by Metal, which is the path that actually matters for Resolve plugins on macOS. That's next.
Full detail: docs/04-gpu-acceleration.md.
Install and limits unchanged from v0.1.0 — macOS universal, unsigned (clear quarantine after copying), Filter context only. See CHANGELOG.md.
v0.1.0 — OpenFX plugins in Resolume
Run OpenFX plugins — the format DaVinci Resolve uses — inside Resolume Arena/Avenue as native FFGL effects.
ofxgen scans your OFX plugin folders and writes one FFGL bundle per plugin, each exposing that plugin's real parameters, with their real names, units, ranges and groupings, in Resolume's own UI.
Status: working prototype. The OFX host, parameter mapping and pixel path are verified against real OFX plugins by automated harnesses in this repo, including a headless OpenGL test that drives a generated plugin exactly as a host would. It has not been loaded into Resolume itself, and has never been used on a live show. See docs/03-verification.md for exactly what is and isn't tested.
Install
Unzip, then:
./ofxgen generate --out ~/Documents/Resolume\ Arena/Extra\ Effects
xattr -dr com.apple.quarantine ~/Documents/Resolume\ Arena/Extra\ EffectsThen rescan effects in Resolume. The binaries are unsigned, and a quarantined plugin makes Resolume skip it rather than prompt — hence the xattr step.
No compiler needed. Each generated bundle is a copy of one prebuilt binary plus a JSON manifest that the plugin reads when the host loads it.
Why one bundle per plugin instead of a dropdown
FFGL fixes a parameter's type, range and default when the plugin loads — it can only change labels and visibility afterwards. A single wrapper with a plugin dropdown would have to flatten every OFX parameter into anonymous 0–1 sliders. Generating a bundle per plugin keeps each parameter exactly as its author declared it.
What's in the box
ofxgen generate |
scan for OFX plugins and write FFGL bundles |
ofxgen list |
show what was found, and why anything was skipped |
ofxgen verify |
load a generated bundle as a host would and print what it advertises |
ofxprobe |
dump a plugin's parameters; --render pushes a frame through it on the CPU |
Limits worth knowing before you download
- macOS universal only. The Windows and Linux code paths exist but have never been compiled or run, so no binary is shipped for them.
- CPU rendering only — a full GPU→CPU→GPU round trip per frame. Comfortable at 1080p; 4K will hurt. The OFX GPU render extensions (Metal/CUDA/OpenCL) are not implemented.
- Only the OFX Filter context is hosted, since that's what maps onto an effect slot in a Resolume clip. Generator, Transition and General-only plugins are reported and skipped.
- Most commercial plugins will refuse to load. The bridge identifies itself honestly as its own host, and many licensed OFX plugins only run in hosts they recognise. That's the vendor's decision and isn't worked around here.
Trying it with no OFX plugins installed
You probably have none, even with Resolve — Resolve compiles its own ResolveFX into the app rather than installing loadable bundles. Build a corpus from the OpenFX examples with scripts/build-test-plugins.sh in the repo.
Full notes in CHANGELOG.md.