Creating a Debug Messenger

When writing OpenXR application code, it's inevitable that we'll make the occasional mistake. Since OpenXR is rigorously specified, correct usage of OpenXR procedures can, to an extent, be programmatically verified.

Doing this by default would, however, impose too large a performance penalty on applications. So, during development, we can optionally enable debug utils to help us write correct code. This is done with the XR_EXT_debug_utils extension. To enable the extension, we must modify our call to xrCreateInstance to include the name of the extension. Since this is, for now, our only extension, we'll put the extension count behind a preprocessor check, to only enable it during debug builds.

#ifndef NDEBUG
const char *enabled_extensions[] = {XR_EXT_DEBUG_UTILS_EXTENSION_NAME};
uint32_t extension_count = 1;
#else
const char **enabled_extensions = NULL;
uint32_t extension_count = 0;
#endif

XrInstanceCreateInfo instance_info = {
    .type = XR_TYPE_INSTANCE_CREATE_INFO,
    .applicationInfo = application_info,
    .enabledExtensionCount = extension_count,
    .enabledExtensionNames = enabled_extensions,
};

With the extension enabled, we can now create a Debug Utils Messenger, which will be referenced by a XrDebugUtilsMessengerEXT handle. To create one, we must provide the OpenXR runtime with a callback, which will be invoked whenever a debug message is produced. Inside this callback, we can choose to do whatever we wish. Here, we will print the information with printf, another option would be writing messages to a logfile.

The function signature of the callback is declared in the OpenXR headers as PFN_xrDebugUtilsMessengerCallbackEXT. We can copy that signature to define our function. In C++, extern "C" should be prepended to the declaration to ensure it has C linkage.

XrBool32 openxr_debug_callback(
        XrDebugUtilsMessageSeverityFlagsEXT              messageSeverity,
        XrDebugUtilsMessageTypeFlagsEXT                  messageTypes,
        const XrDebugUtilsMessengerCallbackDataEXT*      callbackData,
        void* userData) {
    printf("XR DEBUG MESSAGE: %s\n", callbackData->message;
    return XR_FALSE;
}

The return value of this callback is an XrBool32 indicating whether the calling layer should abort the call of the function that produced the debug callback. This can be useful to abort execution if, for example, messageSeverity contains the XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT bit. Here, we simply always continue execution.