I have started with Vulkan and just did the Validation Layers.
In here, they add a static function static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(...)
to the .cpp file, then simply assign
createInfo.pfnUserCallback = debugCallback;
How (/why) does this work? I know of callbacks of course, but I tried to add this function to my renderer class instead of just "locally"(?) in the .cpp file, first in the header file:
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
void* pUserData);
And then in the .cpp file:
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanRenderer::DebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
void* pUserData)
{
//...
}
And finally using assigning it like this:
createInfo.pfnUserCallback = &VulkanRenderer::DebugCallback;
My version does in fact not work, so I probably am thinking about the wrong thing here. But I don't know what they did in the tutorial or even what to search for to understand this.
I do know what code to write to make it work like they have it, I just don't know why.
When DebugCallback
is a non-static member function, then &VulkanRenderer::DebugCallback
is a member function pointer.
A member function pointer is not a function pointer, but pfnUserCallback
wants a function pointer.
Member function pointers and function pointers can't work the same because a non-static member function needs to know on which object it is being called, which isn't the case for a free function.
Now, Vulkan is a C API, not a C++ API, so it isn't aware of member functions at all and also uses a C idiom to implement the callback interface instead of a C++ one.
The C idiom however still allows passing information about the object on which you want to call your member function. That's what pUserData
is for. It will be passed through unchanged to your callback and the callback can use it to e.g. do a member function call:
createInfo.pUserData = this;
createInfo.pfnUserCallback = [](auto a, auto b, auto c, void* pUserData){ return static_cast<VulkanRenderer*>(pUserData)->DebugCallback(a, b, c); };
(capture-less generic lamdbas can be converted into function pointers for specific argument types.)
(I don't actually know much about Vulkan. If VKAPI_CALL
and VKAPI_ATTR
are required on the function that will be called by Vulkan, then a lambda might not work. In that case use a static member function with a name on which you can apply the macros instead of an nameless lambda on which you likely can't.)
Alternatively, if DebugCallback
doesn't need to be non-static, you can of course also declare it as static
member function. Then your approach will also work. A static member function doesn't need any additional information about an object on which it is called and therefore forms normal function pointers like free functions.