{"slug": "running-minecraft-on-macos-with-zink-kosmickrisp", "title": "Running Minecraft on macOS with Zink + KosmicKrisp", "summary": "This article describes a method to run OpenGL 4.6 applications, specifically Minecraft, on macOS by translating OpenGL calls through a chain of graphics APIs: OpenGL → Vulkan → Metal. The process uses Mesa's Zink driver to convert OpenGL to Vulkan, and the KosmicKrisp driver to translate Vulkan to Metal, bypassing Apple's deprecated OpenGL 4.1 cap. The author details the build and configuration steps, including a workaround that adds window support to Mesa's \"surfaceless\" EGL platform specifically for macOS.", "body_md": "# Running Minecraft on macOS with Zink + KosmicKrisp\n\n<img width=\"966\" height=\"620\" alt=\"Screenshot 2025-12-31 at 11 15 00 AM\" src=\"https://gist.github.com/user-attachments/assets/b14c60cb-0e96-4e37-b42b-4d933601c7e5\" />\n\n**TL;DR:** I got OpenGL 4.6 apps (Minecraft) running on macOS by translating OpenGL → Vulkan → Metal using Mesa's Zink driver and the KosmicKrisp Vulkan implementation.\n\n```\nMinecraft (OpenGL 4.6) → Zink → Vulkan → KosmicKrisp → Metal → GPU\n```\n\n---\n\n## Build Instructions\n\n### Prerequisites\n\n```bash\n# Core build tools\nbrew install meson ninja python3\n\n# Mesa dependencies\nbrew install bison flex llvm glslang spirv-tools molten-vk\n\n# Add LLVM and bison to PATH (Apple's versions are outdated)\nexport PATH=\"/opt/homebrew/opt/llvm/bin:/opt/homebrew/opt/bison/bin:$PATH\"\n```\n\n### Mesa\n\n```bash\ngit clone https://github.com/lucamignatti/mesa.git\ncd mesa\n\n# Create native file for Homebrew bison\ncat > native.ini << 'EOF'\n[binaries]\nbison = '/opt/homebrew/opt/bison/bin/bison'\nEOF\n\n# Configure\nmeson setup build --native-file native.ini \\\n  -Dprefix=$HOME/mesa-native \\\n  -Dbuildtype=release \\\n  -Dplatforms=macos \\\n  -Degl-native-platform=surfaceless \\\n  -Degl=enabled \\\n  -Dgallium-drivers=zink \\\n  -Dvulkan-drivers=kosmickrisp \\\n  -Dgles1=enabled \\\n  -Dgles2=enabled \\\n  -Dglx=disabled \\\n  -Dgbm=disabled \\\n  -Dmoltenvk-dir=/opt/homebrew/opt/molten-vk\n\n# Build and install\nninja -C build\nninja -C build install\n\n# Configure DRI\nmkdir -p $HOME/mesa-native/lib/dri\ncd $HOME/mesa-native/lib/dri  \nln -sf ../libgallium-26.0.0-devel.dylib zink_dri.so\nln -sf ../libgallium-26.0.0-devel.dylib swrast_dri.so\n\n# Copy vulkan loader\ncp /opt/homebrew/lib/libvulkan.1.dylib $HOME/mesa-native/lib/\n```\n\n### GLFW\n\n```bash\ngit clone https://github.com/lucamignatti/glfw.git\ncd glfw\nmkdir build && cd build\ncmake .. -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF -DBUILD_SHARED_LIBS=ON\nmake -j8\n```\n\nThe library is at `build/src/libglfw.3.dylib`.\n\n### Running Minecraft\n\n**Environment Variables** (set in your launcher):\n\n```bash\nDYLD_INSERT_LIBRARIES=$HOME/mesa-native/lib/libgl_interpose.dylib\nDYLD_LIBRARY_PATH=$HOME/mesa-native/lib\nLIBGL_DRIVERS_PATH=$HOME/mesa-native/lib/dri\nVK_DRIVER_FILES=$HOME/mesa-native/share/vulkan/icd.d/kosmickrisp_mesa_icd.aarch64.json\nEGL_PLATFORM=surfaceless\nMESA_LOADER_DRIVER_OVERRIDE=zink\nMESA_GL_VERSION_OVERRIDE=4.6\nMESA_GLSL_VERSION_OVERRIDE=460\n```\n\n**JVM Arguments**:\n\n```\n-Dorg.lwjgl.egl.libname=$HOME/mesa-native/lib/libEGL.dylib\n-Dorg.lwjgl.opengl.libname=$HOME/mesa-native/lib/libGL.dylib\n-Dorg.lwjgl.glfw.libname=/path/to/glfw/build/src/libglfw.3.dylib\n```\n\n**Known Issues**\n \n - The game initially only renders in the bottom left quarter of the window. Resize the window to fix.\n\n\n---\n\n## Getting it working\n\nApple deprecated OpenGL in 2018 and capped macOS at OpenGL 4.1. But shader packs, mods, and modern Minecraft features increasingly expect OpenGL 4.6. Apple's answer is Metal, but that doesnt help us.\n\nRecently, the KosmicKrisp driver was created as a part of mesa. This is the first compliant vulkan driver on macOS, and according to various MRs it is capable of running zink. So, we translate OpenGL to Vulkan using Mesa's Zink driver, then translate Vulkan to Metal using KosmicKrisp. Sounds simple right? It wasn't.\n\n### Making Zink support KosmicKrisp\n\nZink requires many different vulkan features to be supported to impliment correct and performant openGL, and until recently, kosmickrisp didnt support all of them. currently, it's lacking one non-critical feature: NullDescriptor. Fortunately a [current MR](https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37115) enables zink to work around this. So, we apply this MR and it's off to the races. Except that it's not.\n\n### First Problem: Where Do We Even Render?\n\nMesa's EGL implementation needs a \"platform\" to talk to the display server. On Linux, this is X11, Wayland, or DRM/KMS. macOS has none of these. I tried creating a `platform_macos.c` from scratch. Dead end. it would require weeks of work to implement the full EGL platform interface. Then I noticed something: the **surfaceless platform**. It's designed for headless rendering such as GPU compute, offscreen rendering, or CI testing. It explicitly has no window support. But it does talk to Vulkan via Kopper, Zink's WSI layer. What if we just... added window support to surfaceless? On macOS only?\n\nThis is cursed. A platform called \"surfaceless\" that creates window surfaces. But it works. I added a macOS-specific code path in `platform_surfaceless.c` that activates when Kopper detects Metal support. The \"Metal display vtbl\" includes `create_window_surface`, `swap_buffers`, and `swap_interval`. Functions that surfaceless normally doesn't have. We also force the software driver probe path, because macOS has no DRM devices to enumerate.\n\n### Second Problem: GLFW Uses the Wrong OpenGL\n\nGLFW on macOS defaults to NSGL, which is Apple's native OpenGL implementation. That's the capped 4.1 version we're trying to escape. I patched `cocoa_window.m` to route all context requests to EGL instead. This way Mesa handles the context, not Apple's deprecated driver.\n\n### Third Problem: LWJGL Looks for GL in the Wrong Place\n\nGLFW was now using EGL, but Minecraft still crashed. LWJGL (Minecraft's native library layer) loads OpenGL function pointers by calling `dlsym` on `libGL.dylib`. But our OpenGL implementation lives in EGL. there is no system `libGL.dylib` that knows about our context.\n\nI created a small interposition library (`libgl_interpose.dylib`) that uses macOS's `DYLD_INTERPOSE` mechanism to intercept all `dlsym` calls. When code asks for a function starting with `gl`, we redirect to `eglGetProcAddress` instead. Then, we inject this via `DYLD_INSERT_LIBRARIES` and a launch argument. Now when LWJGL asks for `glGenBuffers`, it gets Mesa's implementation instead of nothing.\n\n### Fourth Problem: Wrong Layer Type\n\nFirst launch. Immediate crash: `vkCreateMetalSurfaceEXT` failed. Debugging revealed the issue: GLFW passes us an `NSView`'s backing layer. By default that's `NSViewBackingLayer`, which is a generic 2D compositing layer. Metal needs a `CAMetalLayer`.\n\nWe can't ask GLFW to change this without major patches. So we swap the layer ourselves. When our surface creation callback is invoked, we check the layer class using Objective-C runtime introspection. If it's `NSViewBackingLayer`, we create a fresh `CAMetalLayer`, configure it (frame, scale factor, opaque), and attach it to the view. All of this must happen on the **main thread**, CoreAnimation's requirement. We use `dispatch_sync_f()` to safely dispatch from render threads.\n\n### Fifth Problem: 1×1 Swapchain\n\nMinecraft launched! Into a 1×1 pixel window. \n\nThe Kopper interface has a `GetDrawableInfo()` callback to query window dimensions. Our implementation queried `[layer drawableSize]`. But `CAMetalLayer.drawableSize` isn't populated until the next CoreAnimation layout pass, which hasn't happened yet because we just created the layer. So, we explicitly set `drawableSize` when creating the layer, matching the view bounds times the scale factor. Now the first query returns sensible values.\n\n### Sixth Problem: Random SIGBUS Crashes\n\nMinecraft would sometimes crash with SIGBUS in CoreAnimation internals when querying drawable size. Completely non-deterministic.\n\nAfter adding backtraces, I identified the pattern: crashes happened when `GetDrawableInfo()` was called from a render thread while CoreAnimation was doing layout on the main thread. CoreAnimation is not thread-safe. We wrapped all size queries in `dispatch_sync_f()` to the main thread.\n\n### Seventh Problem: Next-Launch Crashes\n\nThe first run works! the second run has crash in Metal shader compilation with invalid pointers though.\n\nI discovered that KosmicKrisp caches compiled shaders to disk. The cache included Metal Pipeline State Object pointers, but those are process-specific. On the next launch, they're garbage pointing to deallocated GPU resources. So, I disable shader disk caching in KosmicKrisp. Performance loss is negligible for Minecraft anyway.\n\n### Eighth Problem: The Gray Screen\n\nMinecraft loaded. The main menu background though, was gray. The world was also gray. Only UI elements where visible. oddly though, the 3d world was visible from behind f3 text and the chat box.\n\nI added logging throughout the blit path. The RGB channels had correct values, but the gray areas all had `alpha = 0`. Minecraft renders the 3D world with alpha=0, it doesn't need transparency for terrain. On typical OpenGL, this is fine. But Metal's compositor respects alpha. Alpha=0 means \"fully transparent\". To fix this, I force `alpha = 1.0` in the blit shaders.\n\n### Ninth Problem: Black Flashing\n\nMain menu rendered correctly! Loading a world... black frames. Every few frames where pure black.\n\nZink has two blit paths:\n1. **vk_meta_blit** (native): Uses Vulkan blit commands. Fast.\n2. **util_blitter** (Gallium fallback): Software-assisted, and more compatible.\n\nFor format conversions (RGBA↔BGRA when blitting to the swapchain), Zink was using `vk_meta_blit`. On KosmicKrisp, this exhibited intermittent black frames. The root cause is still unknown. possibly a synchronization issue in KosmicKrisp's blit. Regardless, I force format-conversion blits to use `util_blitter` instead. The performance penalty is about ~2-3%, which is acceptable for stable frames. Both paths now force alpha=1.\n\n### Tenth Problem: Dead-end Window Resizing\n\nThe game finally launched, looked right, and ran smoothly. But there was one final annoyance. Resizing the window didnt resize the view, and broke everything. The internal rendering resolution remained stuck at whatever size the window was when the game first appeared, leading to either black borders or extreme stretching.\n\nZink's Kopper layer has an `update` mechanism that is supposed to handle window resizing. However, this path was hardcoded to only query surface capabilities for X11 platform surfaces. For every other surface type, including our new Metal surfaces, it just returned the current resource dimensions, effectively telling the driver that the size had never changed.\n\nI extended `zink_kopper_update` and the DRI frontend to handle `KOPPER_METAL` surfaces. Now, every frame, Zink queries the current actual size of the `CAMetalLayer` via Vulkan surface capabilities. If there's a mismatch, it triggers a swapchain recreation. After this change Minecraft responds correctly to window resizes, maintaining the correct aspect ratio and resolution as you resize.\n\n### It Works\n\n70fps, shader packs load, modern mods work, and f3 reports OpenGL 4.6 on macOS, running entirely through Mesa, Vulkan, and Metal.\n\nThe surfaceless platform has surfaces, the blit is emulated and lies, we swap layer types at runtime, and theres no shader cache. It's held together with the most hackiest of hacks.\n\nBut it works. mostly. There are still bugs in rendering deep in kosmickrisp somewhere (youll notice the hotbar, for example, looks strange). not all shaders work as kosmickrisp doesnt expose all needed features for zink to run them, and voxy doesnt have the indirect GL features it needs as kosmickrisp doesnt support them yet, and performance is lacking. But maybe one day not to far away, everything will work. This at least proves that it can. On another note, it's not impossible this is the first time ever openGL 4.6 has run on macOS, which is cool.", "url": "https://wpnews.pro/news/running-minecraft-on-macos-with-zink-kosmickrisp", "canonical_source": "https://gist.github.com/lucamignatti/5312f5e937de2ba44256ecba6de54cc2", "published_at": "2025-12-31 17:15:48+00:00", "updated_at": "2026-05-23 20:36:29.767522+00:00", "lang": "en", "topics": ["developer-tools", "open-source", "hardware"], "entities": ["Mesa", "Zink", "KosmicKrisp", "Minecraft", "Metal", "Vulkan", "OpenGL", "Homebrew"], "alternates": {"html": "https://wpnews.pro/news/running-minecraft-on-macos-with-zink-kosmickrisp", "markdown": "https://wpnews.pro/news/running-minecraft-on-macos-with-zink-kosmickrisp.md", "text": "https://wpnews.pro/news/running-minecraft-on-macos-with-zink-kosmickrisp.txt", "jsonld": "https://wpnews.pro/news/running-minecraft-on-macos-with-zink-kosmickrisp.jsonld"}}