Skip to content

MacHdrActivator.kt

src/jvmMain/kotlin/afield/graphics/vulkan/device/MacHdrActivator.kt · 101 lines

package afield.graphics.vulkan.device

import afield.app.diagnostics.logging.lprintln

Enables Extended Dynamic Range (EDR) on the macOS CAMetalLayer that backs the GLFW/MoltenVK window. Without this call the OS compositor clamps output to SDR even when an HDR Vulkan swap-chain format (e.g. scRGB) is selected.

Implementation: the GLFW window handle is an NSWindow*. We walk the Objective-C object graph via the ObjC runtime (objc_msgSend) using LWJGL's low-level JNI call helpers, without pulling in any extra native library or annotation processor:

NSWindow → contentView (NSView) → layer (CAMetalLayer) → setWantsExtendedDynamicRangeContent: YES

All class and method lookups are done via reflection so this file compiles and loads on Windows/Linux without modification. It reaches LWJGL through five entry points: GLFWNativeCocoa.glfwGetCocoaWindow, LibObjC.sel_registerName, LibObjC.getLibrary, JNI.callPPP and JNI.callPPZV.

All ObjC messaging is wrapped in a try/catch so a failure degrades gracefully to SDR.

object MacHdrActivator

One function, activate, which SwapChain calls as it builds a swap chain on macOS with HDR on.

object MacHdrActivator {

fun activate(windowHandle: Long)

@param windowHandle The GLFW window handle returned by glfwCreateWindow.

body — 71 lines, from line 30
    fun activate(windowHandle: Long) {
        try {
            // Resolve GLFWNativeCocoa reflectively so this file compiles on all platforms.
            val cocoaClass = Class.forName("org.lwjgl.glfw.GLFWNativeCocoa")
            val getNsWindow = cocoaClass.getMethod("glfwGetCocoaWindow", Long::class.javaPrimitiveType)
            val nsWindow = getNsWindow.invoke(null, windowHandle) as Long
            if (nsWindow == 0L) {
                lprintln("warning,hdr", "[HDR] glfwGetCocoaWindow returned nil – EDR not enabled")
                return
            }

            // Resolve LibObjC reflectively so this file compiles on non-Mac platforms.
            val libObjcClass = Class.forName("org.lwjgl.system.macosx.LibObjC")
            val selRegister = libObjcClass.getMethod("sel_registerName", CharSequence::class.java)
            val getLibrary  = libObjcClass.getMethod("getLibrary")

            val library = getLibrary.invoke(null) // SharedLibrary
            val getFnAddr = library.javaClass.getMethod("getFunctionAddress", String::class.java)
            val msgSend = getFnAddr.invoke(library, "objc_msgSend") as Long
            if (msgSend == 0L) {
                lprintln("warning,hdr", "[HDR] objc_msgSend not found – EDR not enabled")
                return
            }

            val selContentView = selRegister.invoke(null, "contentView") as Long
            val selLayer       = selRegister.invoke(null, "layer") as Long
            val selSetEDR      = selRegister.invoke(null, "setWantsExtendedDynamicRangeContent:") as Long

            // Invoke ObjC messages via LWJGL's JNI helper (callPPP = two ptr args → ptr result).
            val jniClass   = Class.forName("org.lwjgl.system.JNI")
            val callPPP    = jniClass.getMethod("callPPP", Long::class.javaPrimitiveType,
                Long::class.javaPrimitiveType, Long::class.javaPrimitiveType)
            val callPPZV   = runCatching {
                jniClass.getMethod("callPPZV", Long::class.javaPrimitiveType,
                    Long::class.javaPrimitiveType, Boolean::class.javaPrimitiveType,
                    Long::class.javaPrimitiveType)
            }.getOrNull()

            // NSWindow.contentView → NSView
            val contentView = callPPP.invoke(null, nsWindow, selContentView, msgSend) as Long
            if (contentView == 0L) {
                lprintln("warning,hdr", "[HDR] contentView is nil – EDR not enabled")
                return
            }

            // NSView.layer → CAMetalLayer
            val layer = callPPP.invoke(null, contentView, selLayer, msgSend) as Long
            if (layer == 0L) {
                lprintln("warning,hdr", "[HDR] layer is nil – EDR not enabled")
                return
            }

            // [layer setWantsExtendedDynamicRangeContent: YES]
            if (callPPZV != null) {
                callPPZV.invoke(null, layer, selSetEDR, true, msgSend)
            } else {
                // Fallback: callPPJV (long arg) – BOOL is widened to register size by ABI.
                val callPPJV = jniClass.getMethod("callPPJV", Long::class.javaPrimitiveType,
                    Long::class.javaPrimitiveType, Long::class.javaPrimitiveType,
                    Long::class.javaPrimitiveType)
                callPPJV.invoke(null, layer, selSetEDR, 1L, msgSend)
            }

            lprintln("info,hdr", "[HDR] CAMetalLayer.wantsExtendedDynamicRangeContent = YES (EDR enabled)")
        } catch (e: ClassNotFoundException) {
            lprintln("warning,hdr", "[HDR] Mac EDR activation skipped: required class not found (${e.message})")
        } catch (e: Exception) {
            lprintln("warning,hdr", "[HDR] Mac EDR activation failed: ${e.message}")
        }
    }
}