what it is.
my mac is an m2 with 8gb of ram, and obs’s experimental metal renderer is a real performance win on a machine like mine. so i switched. then obs-retro-effects, the plugin I use for a variety of effects on my stream. it’s FiniteSingularity’s plugin, not mine. i had claude take it apart. most of it works on metal now, and the response times have increased with lower memory consumption.
the two background-removal plugins i tried were too slow or crashed on metal, so i built one on apple’s vision framework. this is by far the best background remover plugin I’ve used for OBS.
building it turned up two bugs in obs itself!
notes from claude
· my side of itthe part i found interesting is that obs logs "errors: (none)" while metal is rejecting the shader outright.
obs converts each shader from its hlsl dialect to metal with its own translator, and when the output fails to compile, the log still says errors: (none). guessing from the source went nowhere. the first fix was universal: every effect’s vertex shader returned its own input, which the translator splits into two incompatible types, so everything went black. analog glitch took longer. i guessed wrong twice, then attached a debugger to a throwaway copy of obs and captured the metal code it actually generated. the translator keeps one function per name, so a vector overload of mod simply vanished. nine failure classes like that are written down now, and they became a skill i load whenever obs shaders come up.
crt worked but cost too much. timing each pass on the m2 found one phosphor-mask pass taking 10.77 ms of a 16.67 ms frame: a lookup table built from a slider value, so every pixel kept its own copy on the gpu’s stack. bit-packing the rows and doing arithmetic instead took it to 0.277 ms. because of sean’s no-visual-changes rule, i ran old and new side by side on the gpu across all 25 mask layouts and asserted zero difference. that check caught what review didn’t: under metal’s fast math, a vector fmod(3, 2) returns 0.99999994.
vacancy’s surprise was the readback. copying a frame back to the cpu cost about the same at 960×540 as at 320×180, roughly 8.5 ms, because on metal the copy waits for the whole gpu queue to drain. only doing it less often helps. its crash was tidier: removing a filter mid-render faulted at address 0x174, which is exactly where a null pointer’s info.output_flags sits. the sibling function in libobs guards for that case and this one doesn’t. that one is ready to file. the second, a missing bounds check in metal’s texture staging, is real on paper but i’ve never triggered it. neither is filed.
i got one wrong. i told sean an open multiview was multiplying the crt cost. it wasn’t open, and the mask pass alone explained his slowdown, so i took it back. a fix counts here once sean has seen it work in obs on his machine. digital glitch is still waiting on that.