This is a Vulkan binding generated from vk.xml. This generator is written from scratch, including the xml parser. Tested on Vulkan 1.4.335 and jai version beta 0.2.026.
This has no relation to osor_io’s Vulkan jai binding, osor_vulkan.
The primary reason for me to write this binding is because the above binding does not compile anymore.
I have generated a set of binding code in the repo. I don’t recommend directly using them. They are just for reference. You should generate your own using the following instructions.
Obtain a vk.xml. I recommend to get it from your installation of
Vulkan SDK, if you are on a mac and using homebrew, the binding is
located at /opt/homebrew/share/vulkan/registry/vk.xml. If you are on
Linux, it is usually at /usr/share/vulkan/registry/vk.xml.
Obtain a video.xml, which should locate at the same directory of
vk.xml.
You can either modify the VK_XML_PATH variable in the top of
generate.jai or you copy the vk.xml to the current directory.
Same for generate_video.jai.
Run. This single command will generate both vulkan and vulkan_video related bindings.
jai generate.jaiHowever, if you want a faster generation without waiting that few second, you can compile and run using this. Even with a default build, the generation takes sub 0.3 for running on both machine. Why? I don’t know.
jai main.jai +Autorunvideo.xml is parsed separately because it is unlikely to change like
vk.xml. But both of them is necessary to generate because vk.xml
depends on video.xml.
Clone the directory to the modules of your project, change the
directory name to Vulkan. You should be able to include it by
something like this.
#import "Vulkan";There is a stock Vulkan binding comes with the Jai compiler. However,
that binding is really outdated. And that binding was generated by
parsing vulkan.h and linking libvulkan.so.
For someone who want a dynamic loader, it sucks.
I tried to steal the binding from Odin vendor library. That route was a lot easier and work perfectly. But generating a binding from scratch means that we no longer need to depends on Ginger Bill to update.
Using dynamic loader require three steps.
// Or any library that provide the function pointer like GLFW
vk_load_global_functions(xx SDL_Vulkan_GetVkGetInstanceProcAddr());
// create instance
vk_load_instance_functions(instance);
// create device
vk_load_device_functions(device);If you want to use dynamic function along side with Vulkan_Memory_Allocator. (You can find it in the jai/examples/VR/modules.)
make_vma :: (instance: VkInstance, physical_device: VkPhysicalDevice, device: VkDevice) -> VkResult, VmaAllocator {
vma_vulkan_functions := VmaVulkanFunctions.{
vkGetInstanceProcAddr = vkGetInstanceProcAddr,
vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
create_info := VmaAllocatorCreateInfo.{
vulkanApiVersion = VK_API_VERSION_1_4,
physicalDevice = physical_device,
device = device,
instance = instance,
flags = VMA_BUFFER_DEVICE_ADDRESS,
pVulkanFunctions = *vma_vulkan_functions,
};
allocator: VmaAllocator;
result := check_vk(vmaCreateAllocator(*create_info, *allocator));
return result, allocator;
}Inspired by the stock Vulkan module, I ported the enumeration wrapper into this library. For anything that you normally need three steps to get in C, it is now single line.
For example you can write vkGetPhysicalDeviceSurfaceFormatsKHR like this.
result, formats := vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface,, temp);Instead of this.
format_count: u32;
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, *format_count, null);
formats := NewArray(format_count, VkSurfaceFormatKHR);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, *format_count, formats.data);But there is an important difference between my library and the stock library. The VkResult, if it is there, is in the first slot of the multiple return values, which is the recommended way to write a Jai module.
For enum value that start with a digit, the generator will prepend an ‘e’ in the beginning, making it valid to compile. This is consistant with Vulkan-Hpp. Maybe I could just put an underscore in front, but whatever.
I planned to add a switch to strip the vk prefix, but have not implemented it yet. Also, stripping vk prefix means that any thing depends on Vulkan also need to strip the vk prefix, like Vulkan Memory Allocator.
Currently there is a variable in generate.jai named
STRIP_VULKAN_PREFIX, it does nothing at the moment.
If you want to link the vulkan shared library instead, you may set the
DYNAMIC_LOADER to false, but you have to link it yourself in your
build.jai. I do not provide any linking in the generated
module.jai.
Most of the vendor dependent libraries definition is hardcoded by hand. I may write a function to generate them instead. But because the size of them is still manageable by human. I don’t bother to deal with it at the moment.
My original xml-parser was a bit too primitive and I decided to write a proper one with isolated lexer and parser. Guess what, the rewritten version perform 2x to 3x slower than the original one.
If you run the generation on a decent machine, it takes a few seconds, e.g. 4.2s on 9950x3d and 3s on M1 pro.
But that is properly because I made a lot more allocation calls instead of referencing all the string to the original input string.
Mar 6, 2026 update
I rewrote the xml-parser to minimize allocation, now the generation is 3.5s on 9950x3d.