Skip to content

Releases: stoatworks-labs/resolume-ofx-bridge

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 31 Jul 10:43
v0.5.1 — show home paths as ~

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 31 Jul 10:37
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

Choose a tag to compare

@github-actions github-actions released this 31 Jul 05:28

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

Choose a tag to compare

@github-actions github-actions released this 31 Jul 05:16

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,25564,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

Choose a tag to compare

@github-actions github-actions released this 31 Jul 05:13
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

Choose a tag to compare

@github-actions github-actions released this 31 Jul 05:00

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

Choose a tag to compare

@github-actions github-actions released this 31 Jul 04:27

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\ Effects

Then 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.