# vkgen Generates Rust source code from the Vulkan/OpenXR registry. [Repository](https://gitlab.com/TobiP64/vkgen) ## General Information ### Licence This software is [MIT licenced](https://mit-license.org/). ### Dependencies and Requirements ##### Rust Version The latest Rust version the generated code was tested with is 1.40 and 1.42-nightly. ##### Cargo Crates `log` is used for logging. `libloading` is used to load the Vulkan/OpenXR shared library, but the generated code can easily be modified to use something different. The generator itself only uses `serde` for parsing the registry. ##### Environment (Shared Libraries etc.) No C/C++ headers/source code or any binaries are required to compile and run the generated code, only the Vulkan/OpenXR shared library. (`libvulkan.so` on Linux and `vulkan-1.dll` on Win32) ##### Other No other dependencies are required to use the generated code, if the code fails to compile or crashes at runtime nonetheless and API misuse can be ruled out, please submit an issue on Gitlab. ## Usage ``` $ vkgen [options] ``` If no input file is specified, the registry will be read from `stdin`. ### Options - `--help`, `-h` display the help page - `--out=`, `-o=` specify the output file If no output file is specified, the generated code will be written to `.rs` - `--out-cargo=`, `-oc=` specify the output cargo file If no output file is specified, the generated code will be written to `.toml` ### Examples ``` $ vkgen ./vk.xml # specify output files $ vkgen ./vk.xml -out=vk.rs -out-cargo=vk.toml # download the Vulkan registry and pipe it to vkgen $ wget -qO- https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/master/registry/vk.xml | vkgen # download the OpenXR regsitry and pipe it to vkgen $ wget -qO- https://raw.githubusercontent.com/KhronosGroup/OpenXR-SDK-Source/master/specification/registry/xr.xml | vkgen ``` ### Alter library loading method If you do not want to use libloading, remove the dependency from the generated toml file and edit `vkInit`/`xrInit` to load the function pointers with your preferred method. ## Details ### Function Loading When a static function (functions that do not require a dispatchable handle to be passed) is called for the first time, the Vulkan/OpenXR shared library is loaded in `vkInit`/`xrInit`. To use non static functions, a dispatchable handle needs to be wrapped with `VkXxxImpl::new(handle)` or `XrXxxImpl::new(handle)`. ### Wrapper Structs #### Methods Wrapper structs are generated for all disptachable handles, containing a function table or pointing to the function table of their parent handle. When a wrapper method is called, the equivalent function in the function table is dispatched. The wrapper methods' signatures only differ from the ones in the table in slice parameters that can not be `null`, where the functions in the table take a pointer and a length, but the wrapper methods take a slice, to provide some safety. No other safety guards are provided. #### Method Tables The special wrappers `VkInstanceImpl`, `VkDeviceImpl` and `XrInstanceImpl` will populate a table with function pointers obtained via `vkGetInstanceProcAddr`, `vkGetDeviceProcAddr` and `xrGetInstanceProcAddr`, respectively, on creation. #### Other All wrapper structs implement `Debug` for debugging purposes obviously and `Deref`/`DerefMut`, to access the wrapped handles. The table entries are not `pub` and thus cannot be accessed but with the wrapper methods. ### Structs and Enums All structs and enums implement `Copy`, `Clone`, `Default` and `Debug` for convenience. #### Common Mistakes Initializing a struct with a member that is a pointer to an array might cause undefined behaviour: ```rust // never do this VkSomeStruct { arrayLen: 0, pArray: [ // some elements ].as_ptr() // <-- the slice will be dropped here, as it is no longer required, // this sometimes only happens when the release target is built // and is thus very hard to debug }; // always do this let array = [ // some elements ]; VkSomeStruct { arrayLen: array.len() as _, pArray: array.as_ptr() // the slice is not dropped, because the declaration // is in the outer scope }; ``` #### Enum Bitfields Since some enums represent bits in a bitfield, that can be combined, variants have to be casted to `u32` in order to use them. ``` VK_SOME_ENUM_VARIANT1_BIT as u32 | VK_SOME_ENUM_VARIANT2_BIT as u32 ``` #### Struct Bitfields Newer versions of the Vulkan Registry contain structures with C bitfields. Since Rust does not support such bitfields, these structs are generated incorrectly and should thus not be used. As of Mar 2021, only `VkAccelerationStructureInstanceKHR` is affected. #### VkResult The enums `VkResult` and `XrResult` implement `Error` and `ops::Try`, which means they can be used with the `?` operator. To utilize this, a nightly compiler is required and the `try_trait` feature must be enabled. ``` #![feature("try_trait")] ... VkInstanceImpl::create(&info, allocator, &mut instance)?; // <-- returns early if the result was an error ``` ### Versions and Extensions Almost every command has a `#[cfg(feature = "VK_VERSION_X_X")]` or `#[cfg(feature = "VK_SOME_FEATURE_KHR"")]` attribute, which means in order to use this command a feature must be enabled. The versions of the Vulkan API, `VK_VERSION_1_0`, `VK_VERSION_1_1` and `VK_VERSION_1_2`, are enabled by default. ### Constants The type of constants will be inferred by looking at their values. If the generator fails to infer the type, `u32` will be used instead. This might result in constants with a wrong type, which prevents them from being passed to functions or used in structs. In this case, please submit an issue on Gitlab. ### Using Strings All strings in Vulkan/OpenXR are null-terminated `*const u8`s. The wrapper does not convert between Rust's and Vulkan's string representations, all strings passed to or returned from functions or structs have to be converted by the user. ## Examples This simple example demonstrates how to output the instance version (1.1.0) and create an instance. vk.rs is the file containing the generated Rust source code. The shared library will be loaded on the first method call (`VkInstanceImpl::enumerateVersion` in this case). Cargo.toml: ```toml [package] name = "vkgen_test" version = "0.1.0" authors = ["tobias"] edition = "2018" [features] default = ["VK_VERSION_1_0", "VK_VERSION_1_1"] VK_VERSION_1_0 = [] VK_VERSION_1_1 = [] ... [dependencies] libloading = "0.5.0" ``` main.rs: ```rust mod vk; use self::vk::*; use std::ptr::null; fn main() { let mut v: u32 = 0; VkInstanceImpl::enumerateVersion(&mut v); println!("vulkan instance version is {}.{}.{}", VK_VERSION_MAJOR(v), VK_VERSION_MINOR(v), VK_VERSION_PATCH(v)); let instance_info = VkInstanceCreateInfo { sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, pNext: null(), flags: 0, pApplicationInfo: &VkApplicationInfo { sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, pNext: null(), pApplicationName: "test app\0".as_ptr(), applicationVersion: VK_MAKE_VERSION(0, 0, 1), pEngineName: "test engine\0".as_ptr(), engineVersion: VK_MAKE_VERSION(0, 0, 1), apiVersion: VK_MAKE_VERSION(1, 1, 0), }, enabledLayerCount: 0, ppEnabledLayerNames: null(), enabledExtensionCount: 0, ppEnabledExtensionNames: null() }; let mut instance = VK_NULL_HANDLE; if VkInstanceImpl::create(&instance_info, null(), &mut instance) != VK_SUCCESS { panic!("something went wrong :-/"); }; let instance = unsafe { VkInstanceImpl::new(instance) }; } ```