ash

repository·master·Indexed 25 days ago

https://github.com/ash-rs/ash

A lightweight, high-performance Rust wrapper for the Vulkan API (version 0.38.0+1.4.352). Ash provides type-safe handles, a builder pattern for configuration structs, and support for Vulkan 1.1 through 1.4. It includes the ash-window crate for interoperability with raw-window-handle to create Vulkan surfaces across Windows, Unix, macOS/iOS, and Android. The library supports both dynamic loading and direct linking, as well as no_std environments.

Tokens
18.4K
Snippets
30
Records
125
Agent score
80%

What's inside ash

  1. Overview of Ash

    master

    Ash is a lightweight, high-performance Rust wrapper around the Vulkan API. It provides a true Vulkan API without compromises, offering additional type safety and convenience features while maintaining full functionality.

    Key characteristics:

    • Type Safety: Uses strongly typed handles and the builder pattern for lifetime-safe struct creation.
    • Performance: Supports device-local function pointer loading and provides no_std support.
    • Completeness: Generated from vk.xml, supporting Vulkan 1.1 through 1.4.
    • Safety Model: Everything is unsafe (no internal validation), prioritizing performance and direct API access.
    • Experimental Bindings: Vulkan Video bindings are experimental and semver-exempt.
  2. Use strongly typed handles for Vulkan objects

    master

    Ash exposes each Vulkan handle type as a newtyped struct to improve type safety.

    • Null Handles: Can be constructed using T::null().
    • Interop: Handles can be converted to and from u64 using Handle::from_raw and Handle::as_raw for compatibility with non-Ash Vulkan code.
  3. Extend structs using Pointer chains

    master

    To handle Vulkan's pNext pointer chains, Ash provides a .push() method on structs. This allows you to insert an extension struct at the front of the pointer chain.

    • Use base.push(ext) to insert ext at the front.
    • If ext already contains its own pointer chain, use unsafe { ext.extend() } instead.
    • The generic argument of .push() is restricted to structs that implement the appropriate Extends* traits (mapped from the Vulkan structextends registry).
    let mut variable_pointers = vk::PhysicalDeviceVariablePointerFeatures::default();
    let mut corner = vk::PhysicalDeviceCornerSampledImageFeaturesNV::default();
    
    let mut device_create_info = vk::DeviceCreateInfo::default()
        .push(&mut corner)
        .push(&mut variable_pointers);
  4. Load Vulkan function pointers

    master

    Ash manages function pointer loading across three categories to ensure proper lifecycle management:

    1. Entry: Loads the Vulkan library. Must outlive Instance and Device.
    2. Instance: Loads instance-level functions. Must outlive the Devices it created.
    3. Device: Loads device-local functions.

    Important: By default, all functions are loaded. Functions that fail 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, as this will result in a panic.

  5. Set up Vulkan environment for macOS

    master

    When using the LunarG Vulkan SDK on macOS, you must set specific environment variables for cargo run to locate the libraries and layers correctly.

    Replace <version> with your installed SDK version.

    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 ...
  6. Access Vulkan version-specific function pointers

    master

    The Device struct organizes Vulkan API functions by their core version. To access functions introduced in a specific Vulkan version, use the corresponding fp_vX_X method. This allows you to call newer features while maintaining compatibility with older versions of the API.

    Available version accessors:

    • fp_v1_4(): Access Vulkan 1.4 functions.
    • fp_v1_3(): Access Vulkan 1.3 functions.
    • fp_v1_2(): Access Vulkan 1.2 functions.
    • fp_v1_1(): Access Vulkan 1.1 functions.
  7. How `Entry` loading methods differ

    master

    Choosing between loading methods depends on your deployment requirements and build environment:

    MethodFeature RequiredSafety/BehaviorRequirement
    Entry::load()loadedunsafe. Functions invalid after Entry is dropped.Requires Vulkan runtime on target system.
    Entry::load_from(path)loadedunsafe. Functions invalid after Entry is dropped.Requires Vulkan library at specific path.
    Entry::linked()linkedSafe. Functions valid after Entry is dropped.Requires Vulkan SDK/dev packages at build time.
    Entry::from_parts_1_1(...)N/ASafe. Functions valid after Entry is dropped.Requires manual construction of function parts.
  8. Configure Ash linking and features

    master

    Ash provides different ways to link with the Vulkan loader via Cargo features:

    • loaded (default): Dynamically loads the Vulkan library for the current platform using Entry::load. The build environment does not require Vulkan development packages.
    • linked: Links your binary with the Vulkan loader directly. This exposes the infallible Entry::linked method, useful if your application cannot handle Vulkan being missing at runtime.
    • no_std: By disabling the std feature, Ash can be used in no_std environments (requires alloc).
  9. Manage Vulkan pointer chains with push and extend

    master

    Vulkan uses linked lists of structures (pointer chains) via the p_next field to pass extension-specific data. Ash provides two methods on types implementing TaggedStructure to manage these chains safely:

    1. push(&mut extension): Use this to prepend a single extension struct to the current chain. This method will panic if the extension being pushed already contains its own p_next pointer chain. It is intended for simple, standalone extension structs.

    2. extend(&mut extension_chain): Use this (requires unsafe) to insert an entire chain of structures into the current chain. This is necessary when the extension you are adding is itself the head of a pointer chain.

    Mental Model:

    • push is for single items: Root -> A becomes Root -> New -> A.
    • extend is for chains: Root -> A and New -> B becomes Root -> New -> B -> A.
  10. Match Vulkan structures with match_in_struct! and match_out_struct!

    master

    Vulkan uses tagged structures (where the s_type member identifies the struct type). Ash provides two macros to safely cast raw pointers to their concrete types during a match:

    • match_in_struct!: Used for immutable raw pointers (*const T). It rebinds the pointer to a reference (&T).
    • match_out_struct!: Used for mutable raw pointers (*mut T). It rebinds the pointer to a mutable reference (&mut T).

    Note: All match bodies must be enclosed in curly braces {} due to macro parsing limitations. You cannot use single-line expressions like info @ ash::vk::SomeStruct => expression().

    These macros work by reading the s_type field and casting the pointer to the type specified in the match arm.

    // Example using match_out_struct!
    let mut info = ash::vk::DeviceCreateInfo::default();
    let info: *mut ash::vk::BaseOutStructure = <*mut _>::cast(&mut info);
    unsafe {
        ash::match_out_struct!(match info {
            info @ ash::vk::DeviceQueueCreateInfo => {
                dbg!(&info); // Unreachable
            }
            info @ ash::vk::DeviceCreateInfo => {
                dbg!(&info);
            }
        })
    }
  11. Access Vulkan Core versions via Instance

    master

    The Instance struct organizes Vulkan functions by their core version. To access functions introduced in specific Vulkan versions, use the corresponding fp_vX_X method. This allows you to use newer features while maintaining compatibility with older Vulkan cores.

    • fp_v1_0(): Returns access to Vulkan 1.0 functions.
    • fp_v1_1(): Returns access to Vulkan 1.1 functions.
    • fp_v1_3(): Returns access to Vulkan 1.3 functions.