vkgen 1.2.2

Generates Rust source code from the Vulkan registry
vkgen-1.2.2 is not a library.
Visit the last successful build: vkgen-2.2.1

vkgen

Generates Rust source code from the Vulkan registry.

Repository (Github)

General Information

Licence

This software is MIT licenced.

Dependencies

Rust Version

The latest Rust version the generated code was tested with is 1.40 and 1.42-nightly.

Cargo Crates

libloading is the default way of loading the Vulkan shared library, but the generated code can easily be modified to use something different. (see usage step 3a)

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 shared library. (libvulkan.so on Linux and vulkan-1.dll on Win32) Sometimes on Linux the library is named libvulkan.so.1.1 or similar, causing the program to crash. One way to fix this is to create a link or copy the library with the correct name.

Other

No other dependencies are required to use the generated code, if the code fails to compile or crashes at runtime nonetheless and an API misuse can be ruled out, please submit an issue on github.

Details

Function Loading

When a static function is called (functions that do not require a dispatchable handle) the vulkan shared library is automatically loaded. To use non static functions, a dispatchable handle needs to be wrapped with VkXxxImpl::new(handle).

Wrapper Structs

Wrapper structs are generated for all disptachable handles, which contain a function table or point to the function table of the 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.

The special wrappers VkInstanceImpl and VkDeviceImpl will populate a table with function pointers obtained via vkGetInstanceProcAddr and vkGetDeviceProcAddr on creation.

All wrapper structs implements 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 and Debug.

Common Mistakes

Initializing a struct with a member that is a pointer to an array might cause undefined behaviour:

Never do this:

VkSomeStruct {
    arrayLen: 0,
    pArray:   [
        // some elements
    ].as_ptr() // <-- the slice will be dropped here, as it is no longer required,
               //     this happens only when the release target is built and thus
               //     is 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
}

VkResult

The enum VkResult implements Error and ops::Try, which means it 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 method/struct/enum has a #[cfg(feature = "VK_SERSION_X_X")] or #[cfg(feature = "VK_SOME_FEATURE_KHR"")] attribute, which means in order to use any method/struct/enum a feature must be enabled. The versions of the Vulkan API, VK_VERSION_1_0 and VK_VERSION_1_1, are enabled by default.

Some extensions are marked as unsupported in the registry, which will be skipped.

Constants and Type Aliases

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

Usage

Step 1

Download vk.xml from the official Vulkan-Headers repository

Step 2

Generate Rust source code from vk.xml

$ vkgen <input file> <flags>

Parameters
  • input file is the Vulkan registry file to be parsed
  • flags see Flags
Flags
  • --verbose, -v print all warnings
Output
  • <input file>.rs contains the generated source code
  • <input file>.toml contains a list of all versions and extensions
Example

$ vkgen ./vk.xml

Parses a file named vk.xml containing the Vulkan registry and outputs the generated code to vk.rs and a list of all versions and extensions to vk.toml.

Step 3

Copy the Rust file into your project folder and add the versions and extensions located in the toml file to your project's Cargo.toml:

...
[features]
default = ["VK_VERSION_1_0", "VK_VERSION_1_1"]
...
VK_VERSION_1_0 = []
VK_VERSION_1_1 = []
...
[dependencies]
libloading = "*"
...

Step 3a (optional)

If you do not want to use libloading, remove the dependency and edit vkInit to load the function pointers with your preferred method.

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:

[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:

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