Go to file
Marijn Suijten bfa0309c97
Preliminary cleanup pass ()
* Fix clippy::manual_strip

* Fix clippy::comparison_to_empty

* Fix clippy::needless_lifetimes

* generator: Drop unnecessary edgecase for charptr array in builder

* generator: Make array type handling more linear

* prelude: Provide Result -> VkResult<()> conversion

* Add #[must_use] to Result type

* Fix all unchecked vk::Result cases

* generator: Fix typos

* generator: Assert char ptrs have `"null-terminated"` len attribute

* prelude: Provide result_with_success helper for Result -> VKresult

* Cleanup match{success} blocks with SSR

    {let $p = $f; match $q { vk::Result::SUCCESS => Ok($r), _ => Err($z), }} ==>> {$f.result_with_success($r)}

* Simplify matching on Result::SUCCESS

Using the following regex replacement:

    let err_code =\s*(.*\((\n|[^;])*?\));\s*match err_code \{\s*vk::Result::SUCCESS => Ok\((.*)\),\s*_ => Err\(err_code\),\s*\}

    $1.result_with_success($3)

* Simplify intermediate error return

    let err_code =\s*(.*\((\n|[^;])*?\));\s*if err_code != vk::Result::SUCCESS \{\s*return Err\(err_code\);\s*\}

    $1.result()?;

* Simplify error checking with .result()? and .result_with_success()

* generator: Ignore empty basetype in typedef (forward declaration)

ANativeWindow and AHardwareBuffer were [recently changed] to the
basetype category, but without an actual basetype, consequently failing
the Ident constructor with an empty name.

Since these types are already dealt with in platform_types, and there
doesn't seem to be anything sensical to define them to right now, just
ignore these cases.

[recently changed]: 0c5351f5e9 (diff-0ff049f722e55de41ee15b2c91ef380fL179-R180)

* Suggestion: Allocate data in get_ray_tracing_shader_group_handles

* generator: Update nom to 6.0

* generator: Generalize C expression conversion to TokenStream

* generator: Simplify ReferenceType::to_tokens with quote!

* generator: Refactor to not parse our own stringified token stream

* generator: Deal with pointers to static-sized arrays

* generator: Apply static-sized array pointer to size containing `*`

* generator: setter: Interpret all references as ptr, not just p_/pp_

* generator: quote::* is already imported

* generator: Replace manual fn to_tokens with quote::ToTokens trait impl

* generator: Return str reference from constant_name

* generator: Replace unused to_type_tokens with name_to_tokens

The reference argument was always None; replace it with a direct call to
name_to_tokens.

* generator: setters: Use safe mutable references instead of raw ptrs
2020-12-12 20:56:43 +01:00
.github/workflows CI: Test/check/lint all targets in all workspaces 2020-09-02 17:08:20 -07:00
ash Preliminary cleanup pass () 2020-12-12 20:56:43 +01:00
ash-window ash-winow: Fix clippy::single-match 2020-09-02 17:08:20 -07:00
examples Only scan memory_type_count memory types in examples 2020-10-29 12:17:08 -07:00
generator Preliminary cleanup pass () 2020-12-12 20:56:43 +01:00
.gitignore pin vk-parse to crates.io 2018-08-17 12:26:32 -07:00
.gitmodules Vulkan-Headers 2018-07-31 10:51:44 +02:00
bors.toml Add github actions () 2019-10-20 17:41:13 +02:00
Cargo.toml Add interoperability functions for raw-window-handle () 2020-07-04 15:21:39 +02:00
Changelog.md Release 0.31.0 2020-05-10 21:14:01 +02:00
LICENSE-APACHE init 2016-08-14 01:13:39 +02:00
LICENSE-MIT init 2016-08-14 01:13:39 +02:00
README.md Add gpu-alloactor to the readme 2020-11-11 14:40:21 +01:00
rustfmt.toml Initial support for rustfmt 2018-11-17 19:05:28 +01:00

Ash

A very lightweight wrapper around Vulkan

LICENSE LICENSE Documentation Build Status Join the chat at https://gitter.im/MaikKlein/ash Crates.io Version

Overview

  • A true Vulkan API without compromises
  • Convenience features without limiting functionality
  • Additional type safety
  • Device local function pointer loading
  • No validation, everything is unsafe
  • Generated from vk.xml
  • Support for Vulkan 1.1, 1.2

Features

Explicit returns with Result

// 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");

Vec<T> instead of mutable slices

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.

Slices

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

Strongly typed handles

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.

Default implementation for all types

// No need to manually set the structure type
let desc_alloc_info = vk::DescriptorSetAllocateInfo {
    descriptor_pool: self.pool,
    descriptor_set_count: self.layouts.len() as u32,
    p_set_layouts: self.layouts.as_ptr(),
    ..Default::default()
};

Builder pattern

// We lose all lifetime information when we call `.build()`. Be carefull!
let queue_info = [vk::DeviceQueueCreateInfo::builder()
    .queue_family_index(queue_family_index)
    .queue_priorities(&priorities)
    .build()];

// We don't need to call build here because builders implement `Deref`.
let device_create_info = vk::DeviceCreateInfo::builder()
    .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();

Builders have an explicit lifetime, and are marked as #[repr(transparent)].

#[repr(transparent)]
pub struct DeviceCreateInfoBuilder<'a> {
    inner: DeviceCreateInfo,
    marker: ::std::marker::PhantomData<&'a ()>,
}
impl<'a> DeviceCreateInfoBuilder<'a> {
    //...
    pub fn queue_create_infos(
        mut self,
        queue_create_infos: &'a [DeviceQueueCreateInfo],
    ) -> DeviceCreateInfoBuilder<'a> {...}
    //...

Every reference has to live as long as the builder itself. Builders implement Deref targeting their corresponding Vulkan struct, so references to builders can be passed directly to Vulkan functions.

Calling .build() will discard that lifetime because Vulkan structs use raw pointers internally. This should be avoided as much as possible because this can easily lead to dangling pointers. If .build() has to be called, it should be called as late as possible. Lifetimes of temporaries are extended to the enclosing statement, ensuring they are valid for the duration of a Vulkan call occurring in the same statement.

Pointer chains

let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::builder();
let mut corner =
    vk::PhysicalDeviceCornerSampledImageFeaturesNV::builder();
;
let mut device_create_info = vk::DeviceCreateInfo::builder()
    .push_next(&mut corner)
    .push_next(&mut variable_pointers);

Pointer chains in builders differ from raw Vulkan. Instead of chaining every struct manually, you instead use .push_next on the struct that you are going to pass into the function. Those structs then get prepended into the chain.

push_next is also type checked, you can only add valid structs to the chain. Both the structs and the builders can be passed into push_next. Only builders for structs that can be passed into functions will implement a push_next.

Flags and constants as associated constants

// Bitflag
vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE
// Constant
vk::PipelineBindPoint::GRAPHICS,

Debug/Display for Flags

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

Function pointer loading

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 and Device.
  • Instance: Loads instance level functions. Needs to outlive the Devices 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.

Extension loading

Additionally, every Vulkan extension has to be loaded explicitly. You can find all extensions under ash::extensions.

use ash::extensions::khr::Swapchain;
let swapchain_loader = Swapchain::new(&instance, &device);
let swapchain = swapchain_loader.create_swapchain(&swapchain_create_info).unwrap();

Raw function pointers

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(...);

Support for extension names

use ash::extensions::{Swapchain, XlibSurface, Surface, DebugReport};
#[cfg(all(unix, not(target_os = "android")))]
fn extension_names() -> Vec<*const i8> {
    vec![
        Surface::name().as_ptr(),
        XlibSurface::name().as_ptr(),
        DebugReport::name().as_ptr()
    ]
}

Implicit handles

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

Example

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

Windows

Make sure that you have a Vulkan ready driver and install the LunarG Vulkan SDK.

Linux

Make sure that you have a Vulkan ready driver and install the LunarG Vulkan SDK. You also have to add the library and layers to your path. Have a look at my post if you are unsure how to do that.

macOS

Install the LunarG Vulkan SDK. This basically entails extracting the downloaded tarball to any location you choose and then setting a few environment variables. Specifically, if SDK_PATH is set to the root extracted SDK directory,

  • DYLD_LIBRARY_PATH = $SDK_PATH/macOS/lib
  • VK_ICD_FILENAMES = $SDK_PATH/macOS/etc/vulkan/icd.d/MoltenVK_icd.json
  • VK_LAYER_PATH = $SDK_PATH/macOS/etc/vulkan/explicit_layer.d

Triangle

Displays a triangle with vertex colors.

cd examples
cargo run --bin triangle

screenshot

Texture

Displays a texture on a quad.

cd examples
cargo run --bin texture

texture

Useful resources

Examples

Utility libraries

  • 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-alloactor - Memory allocator written in pure Rust for GPU memory in Vulkan and in the future DirectX 12
  • lahar - Tools for asynchronously uploading data to a Vulkan device.

Libraries that use ash

  • gfx-rs - gfx-rs is a low-level, cross-platform graphics abstraction library in Rust.

A thanks to