Friday, December 10, 2021

A New Vulkan-Go Bridge

I got frustrated with the state of the vulkan-go bridge. Mostly that it was seemingly unsupported. Additionally, the c-for-go library made everything difficult to update. As a result, I spent a week and wrote my own vulkan bindings generator.

Instead of starting with the Vulkan headers, I started with the vk.xml specification of the Vulkan API. This provides more semantic information on the API: parents, dependencies, extensions, versions, etc. It also provides an easy way to avoid parsing and understanding everything about C, which was the main reason vulkan-go had to leverage c-for-go.

With my new project in place, I started with building out the XML model in Vulkan. It covers the most important parts, but I did skip some of the comment sections, or aspects of the specification that didn't seem to apply to my immediate needs.

The rest of the project evolved naturally, starting with generating a version that consumed the vulkan prototypes, and eventually generating a version that dynamically loaded the Vulkan library.

Dynamic Loading, Handles, and ProcAddr.


Once I had the dynamic loading working, I wanted to start doing per-instance and per-device proc address loading. While the task it self was easy enough, it created a split-brain like aspect, where I had handles returned by the API, but handles + procAddrs needed by the program.

I haven't yet found a simple programatic way to hide the distinction between the two from the developer, so at the moment, the code is filled with calls to convert newly created handles into handles+procAddrs.

I'm also a novice on dynamic library loading except on a Mac, so I've limited the first implementation to that. I'd love some support from people familiar with dynamic loading on other operating systems.

Explicit OO vs Implicit OO


The Vulkan API, similar to the GLFW API is basically an OO API built within the constraints of C. For most commands you can consider the first parameter to be the self or this object. When I shifted to needing two parameters (the handle and the proc addresses), I went ahead and combined those into a struct and moved the methods into a more explicit OO model where the struct is the receiver for the method.

This is somewhat similar to the approach used by GLFW's go bridge. 

What Isn't Generated?


What is generated is what is specified in the vulkan spec. However, the vulkan spec alone is not very go friendly. So after the vulkan bits are generated, I added some utility and simplification methods to the generated objects.

Most of the logic in these is converting strings, dealing with bools, and dealing with the VkResult object.

Next Steps


Now that I have the library generated, I see the following as my next steps:

  • Update my old tutorial
  • Add WSI support
  • Add OS specific output files with associated build tags.
  • Inject Vulkan Documentation into the output files
  • Find a way to hide the conversion from Handles to Facades.

No comments:

Post a Comment