Expand description
This crate provides a convenient framework to develop Vulkan layers in Rust on top of the ash crate. If you are not familiar how to write a Vulkan layer, this C++ tutorial by Baldur Karlsson is a good reference to start with.
Key features provided by this crate includes:
-
Support the look-up map fashion of implementing a Vulkan layer.
The look-up maps for
VkDeviceandVkInstanceare handled by this crate. -
Implement
vkGet*ProcAddrautomatically.This is a non-trivial work to comply with the spec, because of extensions and the required/supported Vulkan API level. See the spec of
vkGetInstanceProcfor details. -
Handle dispatch tables,
vkCreateInstanceandvkCreateDevice.This mainly includes using the
vkGet*ProcAddrfunction pointer correctly, and advancing theVkLayer*CreateInfo::u::pLayerInfolink list. One common mistake in layer implementation invkCreateDeviceis to callgetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice")to obtain the function pointer tovkCreateDevice. According to the spec,getInstanceProcAddrshould returnNULL. This framework helps you avoid bugs like this.
Note that object wrapping is not supported by this crate, and we don’t plan such support, because object wrapping requires us to intercept ALL Vulkan commands related to one handle type, so it can’t handle unknown commands. In addition, object wrapping is more complicated because it is required to call loader callback on every dispatchable handle creation and destruction.
§Overview
The primary types in this crate are the Layer trait and the Global struct. The user
implements the Layer trait for a type T, and the Global<T> will include all necessary
functions needed to export from this dynamic link library. With the help of the
declare_introspection_queries macro, the user can export those functions in oneline.
use ash::{self, vk};
use once_cell::sync::Lazy;
use std::sync::Arc;
use vulkan_layer::{
declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo,
StubGlobalHooks, StubInstanceInfo,
};
// Define the layer type.
#[derive(Default)]
struct MyLayer(StubGlobalHooks);
// Implement the Layer trait.
impl Layer for MyLayer {
type GlobalHooksInfo = StubGlobalHooks;
type InstanceInfo = StubInstanceInfo;
type DeviceInfo = StubDeviceInfo;
type InstanceInfoContainer = StubInstanceInfo;
type DeviceInfoContainer = StubDeviceInfo;
fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
&*GLOBAL
}
fn manifest() -> LayerManifest {
Default::default()
}
fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
&self.0
}
fn create_instance_info(
&self,
_: &vk::InstanceCreateInfo,
_: Option<&vk::AllocationCallbacks>,
_: Arc<ash::Instance>,
_next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
) -> Self::InstanceInfoContainer {
Default::default()
}
fn create_device_info(
&self,
_: vk::PhysicalDevice,
_: &vk::DeviceCreateInfo,
_: Option<&vk::AllocationCallbacks>,
_: Arc<ash::Device>,
_next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
) -> Self::DeviceInfoContainer {
Default::default()
}
}
// Define the global type from the layer type.
type MyGlobal = Global<MyLayer>;
// Export C functions.
declare_introspection_queries!(MyGlobal);The user can provide their own types that implement different traits to intercept different commands and custom the layer behavior:
GlobalHooks,InstanceHooks,DeviceHooks: Implements the interception to different Vulkan commands based on the dispatch types.GlobalHooksInfo,InstanceInfo,DeviceInfo: Container ofGlobalHooks,InstanceHooks,DeviceHooks. Provide the list of commands to intercept for the framework. Can be automatically implemented through theauto_deviceinfo_impl,auto_globalhooksinfo_impl,auto_instanceinfo_implmacros from theGlobalHooks,InstanceHooks,DeviceHooksimplementation respectively.Layer: Provide the metadata of a layer throughLayerManifest, e.g. the name, version, exported extensions of the layer. Provide the storage for theGlobalobject. Container ofGlobalHooksInfotype. The factory type of theInstanceInfo,DeviceInfotypes.
§Usage
You can check a live example in examples/hello-world.
First, create a Rust lib crate.
$ mkdir vulkan-layer-rust-example
$ cd vulkan-layer-rust-example
$ cargo init --lib
Created library packageSecond, modify the crate type to cdylib and set the panic behavior to abort in Cargo.toml.
[lib]
crate-type = ["cdylib"]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"We need to set panic to abort to avoid unwinding from Rust to the caller(most likely C/C++), because unwinding into other language from Rust is undefined behavior.
If you want to try the layer on Android, also modify the crate name to
VkLayer_vendor_rust_example, because Android requires that the layer shared object library
follow a specific name convention.
[lib]
name = "VkLayer_vendor_rust_example"Third, set up the dependency in Cargo.toml. In my case, I checkout the project repository in
the same directory where the vulkan-layer-rust-example folder lives.
[dependencies]
vulkan-layer = { path = "../vk-layer-for-rust/vulkan-layer" }Other dependencies.
cargo add ash once_cellFourth, implement the layer trait in lib.rs.
use ash::{self, vk};
use once_cell::sync::Lazy;
use std::sync::Arc;
use vulkan_layer::{
declare_introspection_queries, Global, Layer, LayerManifest, StubDeviceInfo,
StubGlobalHooks, StubInstanceInfo,
};
#[derive(Default)]
struct MyLayer(StubGlobalHooks);
impl Layer for MyLayer {
type GlobalHooksInfo = StubGlobalHooks;
type InstanceInfo = StubInstanceInfo;
type DeviceInfo = StubDeviceInfo;
type InstanceInfoContainer = StubInstanceInfo;
type DeviceInfoContainer = StubDeviceInfo;
fn global_instance() -> impl std::ops::Deref<Target = Global<Self>> + 'static {
static GLOBAL: Lazy<Global<MyLayer>> = Lazy::new(Default::default);
&*GLOBAL
}
fn manifest() -> LayerManifest {
let mut manifest = LayerManifest::default();
manifest.name = "VK_LAYER_VENDOR_rust_example";
manifest.spec_version = vk::API_VERSION_1_1;
manifest.description = "Rust test layer";
manifest
}
fn global_hooks_info(&self) -> &Self::GlobalHooksInfo {
&self.0
}
fn create_instance_info(
&self,
_: &vk::InstanceCreateInfo,
_: Option<&vk::AllocationCallbacks>,
_: Arc<ash::Instance>,
_next_get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr,
) -> Self::InstanceInfoContainer {
Default::default()
}
fn create_device_info(
&self,
_: vk::PhysicalDevice,
_: &vk::DeviceCreateInfo,
_: Option<&vk::AllocationCallbacks>,
_: Arc<ash::Device>,
_next_get_device_proc_addr: vk::PFN_vkGetDeviceProcAddr,
) -> Self::DeviceInfoContainer {
println!("Hello from the Rust Vulkan layer!");
Default::default()
}
}Fifth, export functions through the declare_introspection_queries macro
declare_introspection_queries!(Global::<MyLayer>);Sixth, build and check the exported symbol of the build artifacts, and we should see Vulkan introspection APIs are exported.
On Windows, use
dumpbin
$ dumpbin /exports .\target\debug\VkLayer_vendor_rust_example.dll
Microsoft (R) COFF/PE Dumper Version 14.36.32537.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file .\target\debug\VkLayer_vendor_rust_example.dll
File Type: DLL
Section contains the following exports for VkLayer_vendor_rust_example.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
6 number of functions
6 number of names
ordinal hint RVA name
1 0 000F1840 vkEnumerateDeviceExtensionProperties = vkEnumerateDeviceExtensionProperties
2 1 000F1820 vkEnumerateDeviceLayerProperties = vkEnumerateDeviceLayerProperties
3 2 000F1800 vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties
4 3 000F17E0 vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties
5 4 000F1890 vkGetDeviceProcAddr = vkGetDeviceProcAddr
6 5 000F1870 vkGetInstanceProcAddr = vkGetInstanceProcAddr
Summary
1000 .data
11000 .pdata
37000 .rdata
2000 .reloc
171000 .textOn Linux, use objdump.
$ objdump -TC target/debug/libVkLayer_vendor_rust_example.so
target/debug/libVkLayer_vendor_rust_example.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
(omit some irrelevant symbols...)
00000000000fad20 g DF .text 0000000000000022 Base vkEnumerateDeviceExtensionProperties
00000000000face0 g DF .text 000000000000001c Base vkEnumerateInstanceExtensionProperties
00000000000fad50 g DF .text 0000000000000018 Base vkGetInstanceProcAddr
00000000000facc0 g DF .text 0000000000000018 Base vkEnumerateInstanceLayerProperties
00000000000fad00 g DF .text 000000000000001c Base vkEnumerateDeviceLayerProperties
00000000000fad70 g DF .text 0000000000000018 Base vkGetDeviceProcAddrSeventh, create the layer manifest file named rust_example_layer.json right beside the built
artifact. If targeting Android, the json manifest file is not needed.
For Windows,
{
"file_format_version" : "1.2.1",
"layer": {
"name": "VK_LAYER_VENDOR_rust_example",
"type": "INSTANCE",
"library_path": ".\\VkLayer_vendor_rust_example.dll",
"library_arch" : "64",
"api_version" : "1.1.0",
"implementation_version" : "0",
"description" : "Rust test layer"
}
}For Linux,
{
"file_format_version" : "1.2.1",
"layer": {
"name": "VK_LAYER_VENDOR_rust_example",
"type": "INSTANCE",
"library_path": "./libVkLayer_vendor_rust_example.so",
"library_arch" : "64",
"api_version" : "1.1.0",
"implementation_version" : "0",
"description" : "Rust test layer"
}
}This json file will define an explicit layer named VK_LAYER_VENDOR_rust_example.
Eighth, use VkConfig
(i.e. Vulkan Configurator) to force enable this explicit layer, and launch the vkcube
application through VkConfig. In the log view, we should see the
"Hello from the Rust Vulkan layer!" log line. For Android, follow
this instruction to enable
the layer. println! won’t write to logcat, so one needs to change the layer implementation
to write to the logcat.
§Global initialization and clean up
See the document of Layer for details.
§Extensions
TODO
§Synchronization
TODO
§Safety
TODO
§API stability
TODO
Modules§
- test_
utils _test - Utilities used in the integration tests of this crate.
- unstable_
api unstable - Vulkan layer utilities that are used in the integration tests to implement mock layers. The utilities may benefit the layer implementation, but there is no stability guarantee on those APIs.
Macros§
- declare_
introspection_ queries - Declare the required introspection queries for Android given an instantiated
vulkan_layer::Globaltype.
Structs§
- Extension
Properties - A wrapper for
VkExtensionProperties. Structure specifying an extension properties. - Global
- A struct that implements all necessarily functions for a layer given a type that implements
Layer. - Layer
Manifest - A Rust bindings of the layer manifest file.
- Stub
Device Info - A stub struct that intercept no commands, which implements
DeviceHooksandDeviceInfo. - Stub
Global Hooks - A stub struct that intercept no commands, which implements
GlobalHooksandGlobalHooksInfo. - Stub
Instance Info - A stub struct that intercept no commands, which implements
InstanceHooksandInstanceInfo. - Vulkan
Base InStruct Chain - A chain of
VkBaseInStructure. - Vulkan
Base OutStruct Chain - A chain of
VkBaseOutStructure.
Enums§
- Extension
- Layer
Result - The return value of an intercepted Vulkan command by the layer implementation.
- Layer
Vulkan Command
Traits§
- Device
Hooks - Device
Info - A trait for the layer implementation to provide metadata of
DeviceHooksfor the layer framework. - Global
Hooks - A trait for the layer implementation to provide the implementation of the intercepted global commands.
- Global
Hooks Info - A trait for the layer implementation to provide metadata of
GlobalHooksfor the layer framework. - Instance
Hooks - Instance
Info - A trait for the layer implementation to provide metadata of
InstanceHooksfor the layer framework. - Layer
- The
Layertrait provides all layer implementation information for the layer framework.
Functions§
- fill_
vk_ ⚠out_ array - Clone the slice to a C style out array with proper
VkResultreturn value.
Type Aliases§
- VkLayer
Device Link - A list node that contains the next entity’s
vkGetInstanceProcAddrandvkGetDeviceProcAddrused by a layer. One possible payload ofVkLayerDeviceCreateInfo - VkLayer
Instance Link - A list node that contains the next entity’s vkGetInstanceProcAddr used by a layer. One
possible payload of
VkLayerInstanceCreateInfo.
Attribute Macros§
- auto_
deviceinfo_ impl - Derive the implementation of the
vulkan_layer::DeviceInfotrait from the implementation of thevulkan_layer::DeviceHookstrait. - auto_
globalhooksinfo_ impl - Derive the implementation of the
vulkan_layer::GlobalHooksInfotrait from the implementation of thevulkan_layer::GlobalHookstrait. - auto_
instanceinfo_ impl - Derive the implementation of the
vulkan_layer::InstanceInfotrait from the implementation of thevulkan_layer::InstanceHooks.