j
jaipkg.dev
packages / binding / vulkan-jai-binding

vulkan-jai-binding

8468fefbinding

Vulkan binding generated from vk.xml for Vulkan 1.4 and later

No license · updated 3 months ago

readme

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.

Usage

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.jai

However, 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 +Autorun

video.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";

Difference between stock Vulkan binding

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

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;
}

Enumeration wrapper

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.

Enum naming

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.

Roadmap

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.

Fun facts

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.