#vulkan #generator #open-xr #xr #vk

bin+lib vkgen

从 Vulkan/OpenXR 注册表中生成 Rust 源代码

13 个稳定版本

2.2.1 2021 年 7 月 9 日
2.2.0 2021 年 3 月 9 日
2.0.3 2020 年 4 月 4 日
2.0.2 2020 年 2 月 25 日
0.1.9 2019 年 2 月 9 日

#96图形 API

Download history 24/week @ 2024-03-27 51/week @ 2024-04-03

79 每月下载量

MIT 许可证

125KB
3K SLoC

vkgen

从 Vulkan/OpenXR 注册表中生成 Rust 源代码。

仓库

一般信息

许可证

本软件遵循 MIT 许可

依赖和需求

Rust 版本

已测试生成代码的最新 Rust 版本为 1.40 和 1.42-nightly。

Cargo Crates

log 用于日志记录。

libloading 用于加载 Vulkan/OpenXR 共享库,但生成的代码可以轻松修改以使用其他内容。

生成器本身仅使用 serde 解析注册表。

环境(共享库等)

编译和运行生成的代码不需要 C/C++ 头文件/源代码或任何二进制文件,只需 Vulkan/OpenXR 共享库。(Linux 上的 libvulkan.so 和 Win32 上的 vulkan-1.dll

其他

使用生成的代码不需要其他依赖项,如果代码无法编译或运行时崩溃,并且可以排除 API 使用错误,请在 Gitlab 上提交问题。

用法

$ vkgen <input file (optional)> [options]

如果没有指定输入文件,则将从 stdin 读取注册表。

选项

  • --help-h 显示帮助页面
  • --out=<输出文件>-o=<输出文件> 指定输出文件。如果没有指定输出文件,则生成的代码将写入 <input file>.rs
  • --out-cargo=<输出货物文件>-oc=<输出货物文件>指定输出货物文件。如果没有指定输出文件,生成的代码将写入<输入文件>.toml

示例

$ 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

更改库加载方法

如果您不想使用libloading,请从生成的toml文件中删除依赖项,并编辑vkInit/xrInit以使用您首选的方法加载函数指针。

详细信息

函数加载

当静态函数(不需要传递可分发的句柄的函数)首次被调用时,Vulkan/OpenXR共享库在vkInit/xrInit中加载。要使用非静态函数,需要将可分发的句柄用VkXxxImpl::new(handle)XrXxxImpl::new(handle)包装。

包装结构体

方法

为所有可分发的句柄生成包装结构体,包含一个函数表或指向其父句柄的函数表。当调用包装方法时,将调用函数表中的等效函数。包装方法的签名仅在切片参数上与表中的不同,其中表中的函数接受指针和长度,而包装方法接受切片,以提供一些安全性。不提供其他安全保护。

方法表

特殊的包装器VkInstanceImplVkDeviceImplXrInstanceImpl将在创建时填充一个表,其中包含通过vkGetInstanceProcAddrvkGetDeviceProcAddrxrGetInstanceProcAddr获得的函数指针。

其他

所有包装结构体都实现了Debug用于调试目的显然,以及Deref/DerefMut,以访问包装的句柄。表条目不是pub,因此不能访问,只能通过包装方法。

结构体和枚举

所有结构体和枚举都实现了CopyCloneDefaultDebug以方便使用。

常见错误

使用指向数组的指针成员初始化结构体可能会导致未定义的行为

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

枚举位字段

由于一些枚举代表可以组合的位字段中的位,因此变体必须转换为u32才能使用。

VK_SOME_ENUM_VARIANT1_BIT as u32 | VK_SOME_ENUM_VARIANT2_BIT as u32

结构体位字段

Vulkan注册表的较新版本包含具有C位字段的结构的版本。由于Rust不支持此类位字段,这些结构体生成不正确,因此不应使用。截至2021年3月,只有VkAccelerationStructureInstanceKHR受到影响。

VkResult

枚举VkResultXrResult实现了Errorops::Try,这意味着它们可以使用?运算符。要利用此功能,需要一个夜间编译器,并且必须启用try_trait功能。

#![feature("try_trait")]
...
VkInstanceImpl::create(&info, allocator, &mut instance)?; // <-- returns early if the result was an error

版本和扩展

几乎每个命令都有一个 #[cfg(feature = "VK_VERSION_X_X")]#[cfg(feature = "VK_SOME_FEATURE_KHR"")] 属性,这意味着要使用此命令,必须启用某个功能。Vulkan API 的版本 VK_VERSION_1_0VK_VERSION_1_1VK_VERSION_1_2 默认启用。

常量

常量的类型将通过查看其值来推断。如果生成器无法推断类型,则将使用 u32。这可能会导致具有错误类型的常量,从而阻止它们传递给函数或用于结构体。在这种情况下,请在 Gitlab 上提交一个问题。

使用字符串

Vulkan/OpenXR 中的所有字符串都是空终止的 *const u8。包装器不会在 Rust 和 Vulkan 的字符串表示之间进行转换,所有传递给或从函数或结构体返回的字符串都必须由用户转换。

示例

以下简单示例演示了如何输出实例版本(1.1.0)并创建一个实例。vk.rs 是包含生成的 Rust 源代码的文件。在第一次方法调用时(在这种情况下为 VkInstanceImpl::enumerateVersion)将加载共享库。

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

依赖项

~0.4–1MB
~23K SLoC