A very lightweight wrapper around Vulkan
- A true Vulkan API without compromises
- Convenience features without limiting functionality
- Additional type safety
- Device local function pointer loading
- No validation, everything is unsafe
- Lifetime-safety on structs created with the builder pattern
- Generated from
vk.xml
- Support for Vulkan
1.1
,1.2
,1.3
-
no_std
support
The Vulkan Video bindings are experimental and still seeing breaking changes in their upstream specification, and are only provided by Ash for early adopters. All related functions and types are semver-exempt 1 (we allow breaking API changes while releasing Ash with non-breaking semver bumps).
// function signature
pub fn create_instance(&self,
create_info: &vk::InstanceCreateInfo<'_>,
allocation_callbacks: Option<&vk::AllocationCallbacks<'_>>)
-> Result<Instance, InstanceError> { .. }
let instance = entry.create_instance(&create_info, None)
.expect("Instance creation error");
pub fn get_swapchain_images(&self,
swapchain: vk::SwapchainKHR)
-> VkResult<Vec<vk::Image>>;
let present_images = swapchain_loader.get_swapchain_images_khr(swapchain).unwrap();
Note: Functions don't return Vec<T>
if this would limit the functionality. See p_next
.
pub fn cmd_pipeline_barrier(&self,
command_buffer: vk::CommandBuffer,
src_stage_mask: vk::PipelineStageFlags,
dst_stage_mask: vk::PipelineStageFlags,
dependency_flags: vk::DependencyFlags,
memory_barriers: &[vk::MemoryBarrier<'_>],
buffer_memory_barriers: &[vk::BufferMemoryBarrier<'_>],
image_memory_barriers: &[vk::ImageMemoryBarrier<'_>]);
Each Vulkan handle type is exposed as a newtyped struct for improved type safety. Null handles can be constructed with
T::null()
, and handles may be freely converted to and from u64
with Handle::from_raw
and Handle::as_raw
for
interop with non-Ash Vulkan code.
let queue_info = [vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family_index)
.queue_priorities(&priorities)];
let device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_info)
.enabled_extension_names(&device_extension_names_raw)
.enabled_features(&features);
let device: Device = instance
.create_device(pdevice, &device_create_info, None)
.unwrap();
Use base.push_next(ext)
to insert ext
at the front of the pointer chain attached to base
.
let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();
let mut device_create_info = vk::DeviceCreateInfo::default()
.push_next(&mut corner)
.push_next(&mut variable_pointers);
The generic argument of .push_next()
only allows valid structs to extend a given struct (known as structextends
in the Vulkan registry, mapped to Extends*
traits).
Only structs that are listed one or more times in any structextends
will implement a .push_next()
.
// Bitflag
vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE
// Constant
vk::PipelineBindPoint::GRAPHICS,
let flag = vk::AccessFlags::COLOR_ATTACHMENT_READ
| vk::AccessFlags::COLOR_ATTACHMENT_WRITE;
println!("Debug: {:?}", flag);
println!("Display: {}", flag);
// Prints:
// Debug: AccessFlags(110000000)
// Display: COLOR_ATTACHMENT_READ | COLOR_ATTACHMENT_WRITE
Ash also takes care of loading the function pointers. Function pointers are split into 3 categories.
- Entry: Loads the Vulkan library. Needs to outlive
Instance
andDevice
. - Instance: Loads instance level functions. Needs to outlive the
Device
s it has created. - Device: Loads device local functions.
The loader is just one possible implementation:
- Device level functions are retrieved on a per device basis.
- Everything is loaded by default, functions that failed to load are initialized to a function that always panics.
- Do not call Vulkan 1.1 functions if you have created a 1.0 instance. Doing so will result in a panic.
Custom loaders can be implemented.
Additionally, every Vulkan extension has to be loaded explicitly. You can find all extensions directly under ash::*
in a module with their prefix (e.g. khr
or ext
).
use ash::khr;
let swapchain_loader = khr::swapchain::Device::new(&instance, &device);
let swapchain = swapchain_loader.create_swapchain(&swapchain_create_info).unwrap();
Raw function pointers are available, if something hasn't been exposed yet in the higher level API. Please open an issue if anything is missing.
device.fp_v1_0().destroy_device(...);
use ash::{ext, khr};
#[cfg(all(unix, not(target_os = "android")))]
fn extension_names() -> Vec<*const i8> {
vec![
khr::surface::NAME.as_ptr(),
khr::xlib_surface::NAME.as_ptr(),
ext::debug_utils::NAME.as_ptr(),
]
}
Handles from Instance or Device are passed implicitly.
pub fn create_command_pool(&self,
create_info: &vk::CommandPoolCreateInfo<'_>)
-> VkResult<vk::CommandPool>;
let pool = device.create_command_pool(&pool_create_info).unwrap();
The default loaded
cargo feature will dynamically load the default Vulkan library for the current platform with Entry::load
, meaning that the build environment does not have to have Vulkan development packages installed.
If, on the other hand, your application cannot handle Vulkan being missing at runtime, you can instead enable the linked
feature, which will link your binary with the Vulkan loader directly and expose the infallible Entry::linked
.
Ash can be used in no_std
environments (with alloc
) by disabling the std
feature.
You can find the examples here.
All examples currently require: the LunarG Validation layers and a Vulkan library that is visible in your PATH
. An easy way to get started is to use the LunarG Vulkan SDK
Make sure that you have a Vulkan ready driver and install the LunarG Vulkan SDK.
Install a Vulkan driver for your graphics hardware of choice, and (optionally) the Validation Layers via your package manager:
- Arch Linux: https://wiki.archlinux.org/title/Vulkan.
- Gentoo: https://wiki.gentoo.org/wiki/Vulkan.
- Ubuntu/Debian: Besides installing a compatible graphics driver, install
vulkan-validationlayers
(Debian) for the Validation Layers. - Other distros: consult your distro documentation and/or package repository for the preferred method to install and use Vulkan.
Install the LunarG Vulkan SDK. The installer puts the SDK in $HOME/VulkanSDK/<version>
by default. You will need to set the following environment variables when running cargo:
VULKAN_SDK=$HOME/VulkanSDK/<version>/macOS \
DYLD_FALLBACK_LIBRARY_PATH=$VULKAN_SDK/lib \
VK_ICD_FILENAMES=$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json \
VK_LAYER_PATH=$VULKAN_SDK/share/vulkan/explicit_layer.d \
cargo run ...
Displays a triangle with vertex colors.
cargo run -p ash-examples --bin triangle
Displays a texture on a quad.
cargo run -p ash-examples --bin texture
- vulkan-tutorial-rust - A port of vulkan-tutorial.com.
- ash-sample-progression - A port of the LunarG examples.
- ash-nv-rt A raytracing example for ash.
- vk-sync - Simplified Vulkan synchronization logic, written in rust.
- vk-mem-rs - This crate provides an FFI layer and idiomatic rust wrappers for the excellent AMD Vulkan Memory Allocator (VMA) C/C++ library.
- gpu-allocator - GPU Memory allocator written in pure Rust for Vulkan and DirectX 12.
- lahar - Tools for asynchronously uploading data to a Vulkan device.
- gfx-rs - gfx-rs is a low-level, cross-platform graphics abstraction library in Rust.
Footnotes
-
generator
complexity makes it so that we cannot easily hide these bindings behind a non-default
feature flag, and they are widespread across the generated codebase. ↩