I ended up instead loading Vulkan manually on MacOS and just pass that directly to glfw, so the renderer is the same for all three platforms but is only loaded once. I haven't tested Windows yet, but Linux and MacOS work great on my M2 Pro laptop at least.
On MacOS, Brutal.Vulkan (2026.10.2) asks .NET to load a native library called VulkanEx, which doesn’t exist on a normal machine. On Linux it asks for vulkan, which resolves to libvulkan.so, a file that only ships in -dev packages. Both fail with a DllNotFoundException wrapped in a TypeInitializationException the first time you touch the Vulkan API. This workaround needs no changes to BRUTAL:
C#
static readonly string[] Candidates = OperatingSystem.IsMacOS()
? ["libvulkan.1.dylib", "/opt/homebrew/lib/libvulkan.1.dylib", "/usr/local/lib/libvulkan.1.dylib"]
: OperatingSystem.IsLinux() ? ["libvulkan.so.1"] : [];
public static unsafe bool TryLoad(out nint getInstanceProcAddr)
{
// Only called after BRUTAL's own lookup has failed, and only for BRUTAL's Vulkan assembly.
AssemblyLoadContext.Default.ResolvingUnmanagedDll += (assembly, name) =>
{
if (assembly != typeof(Vulkan).Assembly) return 0;
foreach (var candidate in Candidates)
if (NativeLibrary.TryLoad(candidate, out var handle)) return handle;
return 0;
};
try
{
getInstanceProcAddr = (nint)Vulkan.GetInstanceProcAddr.Invoke;
return true;
}
catch (TypeInitializationException e) when (e.InnerException is DllNotFoundException)
{
getInstanceProcAddr = 0;
return false;
}
}
// At startup, before Glfw.Init():
if (!VulkanLoader.TryLoad(out var gipa)) { /* no Vulkan loader installed */ }
Glfw.PInvoke.InitVulkanLoader(gipa);
Glfw.Init();
For MoltenVK, also call VulkanHelpers.AddPlatformRequiredExtensions on the instance extensions (portability enumeration) and VulkanHelpers.AddMandatoryIfSupportedDeviceExtensions on the device extensions (VK_KHR_portability_subset). Both do nothing elsewhere.
On MacOS, you can use brew to install the necessary dependencies:
Code
brew install vulkan-loader molten-vk
plus vulkan-validationlayers if you want validation. Homebrew’s layer manifest uses a bare library name, so run with DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib or the layer fails to load.