shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Utility functions for CORE tier
//!
//! Helper functions that don't fit into specific modules.

use ash::vk;

/// Convert bytes to a slice of u32 for SPIR-V
///
/// SPIR-V bytecode must be aligned to 4-byte boundaries.
#[inline]
pub fn bytes_to_u32_vec(bytes: &[u8]) -> Vec<u32> {
    let mut words = Vec::with_capacity(bytes.len() / 4);
    for chunk in bytes.chunks(4) {
        let word = u32::from_le_bytes([
            chunk[0],
            chunk.get(1).copied().unwrap_or(0),
            chunk.get(2).copied().unwrap_or(0),
            chunk.get(3).copied().unwrap_or(0),
        ]);
        words.push(word);
    }
    words
}

/// Check if a format supports a given feature
#[inline]
pub fn format_supports_feature(
    properties: vk::FormatProperties,
    tiling: vk::ImageTiling,
    features: vk::FormatFeatureFlags,
) -> bool {
    match tiling {
        vk::ImageTiling::LINEAR => properties.linear_tiling_features.contains(features),
        vk::ImageTiling::OPTIMAL => properties.optimal_tiling_features.contains(features),
        _ => false,
    }
}

/// Common Vulkan timeout constants (in nanoseconds)
pub mod timeouts {
    /// No timeout - wait indefinitely
    pub const INFINITE: u64 = u64::MAX;

    /// 1 second timeout
    pub const ONE_SECOND: u64 = 1_000_000_000;

    /// 100 millisecond timeout
    pub const ONE_HUNDRED_MS: u64 = 100_000_000;

    /// 1 millisecond timeout
    pub const ONE_MS: u64 = 1_000_000;
}

/// Common memory property flag combinations
pub mod memory_properties {
    use ash::vk;

    /// Device-local memory (fastest, GPU only)
    pub const DEVICE_LOCAL: vk::MemoryPropertyFlags = vk::MemoryPropertyFlags::DEVICE_LOCAL;

    /// Host-visible and coherent (CPU accessible, slower)
    pub const HOST_VISIBLE_COHERENT: vk::MemoryPropertyFlags = vk::MemoryPropertyFlags::from_raw(
        vk::MemoryPropertyFlags::HOST_VISIBLE.as_raw()
            | vk::MemoryPropertyFlags::HOST_COHERENT.as_raw(),
    );

    /// Host-visible, cached (CPU accessible, faster reads)
    pub const HOST_VISIBLE_CACHED: vk::MemoryPropertyFlags = vk::MemoryPropertyFlags::from_raw(
        vk::MemoryPropertyFlags::HOST_VISIBLE.as_raw()
            | vk::MemoryPropertyFlags::HOST_CACHED.as_raw(),
    );
}

/// Common buffer usage flag combinations
pub mod buffer_usage {
    use ash::vk;

    /// Vertex buffer
    pub const VERTEX: vk::BufferUsageFlags = vk::BufferUsageFlags::VERTEX_BUFFER;

    /// Index buffer
    pub const INDEX: vk::BufferUsageFlags = vk::BufferUsageFlags::INDEX_BUFFER;

    /// Uniform buffer
    pub const UNIFORM: vk::BufferUsageFlags = vk::BufferUsageFlags::UNIFORM_BUFFER;

    /// Storage buffer
    pub const STORAGE: vk::BufferUsageFlags = vk::BufferUsageFlags::STORAGE_BUFFER;

    /// Transfer source
    pub const TRANSFER_SRC: vk::BufferUsageFlags = vk::BufferUsageFlags::TRANSFER_SRC;

    /// Transfer destination
    pub const TRANSFER_DST: vk::BufferUsageFlags = vk::BufferUsageFlags::TRANSFER_DST;

    /// Staging buffer (CPU to GPU transfer)
    pub const STAGING: vk::BufferUsageFlags =
        vk::BufferUsageFlags::from_raw(vk::BufferUsageFlags::TRANSFER_SRC.as_raw());
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bytes_to_u32_vec() {
        let bytes = vec![0x03, 0x02, 0x23, 0x07]; // SPIR-V magic number
        let words = bytes_to_u32_vec(&bytes);
        assert_eq!(words.len(), 1);
        assert_eq!(words[0], 0x07230203);
    }

    #[test]
    fn test_bytes_to_u32_vec_padding() {
        let bytes = vec![0x01, 0x02, 0x03]; // Only 3 bytes
        let words = bytes_to_u32_vec(&bytes);
        assert_eq!(words.len(), 1);
        assert_eq!(words[0], 0x00030201);
    }

    #[test]
    fn test_format_supports_feature() {
        let props = vk::FormatProperties {
            linear_tiling_features: vk::FormatFeatureFlags::SAMPLED_IMAGE,
            optimal_tiling_features: vk::FormatFeatureFlags::COLOR_ATTACHMENT,
            buffer_features: vk::FormatFeatureFlags::empty(),
        };

        assert!(format_supports_feature(
            props,
            vk::ImageTiling::LINEAR,
            vk::FormatFeatureFlags::SAMPLED_IMAGE
        ));

        assert!(format_supports_feature(
            props,
            vk::ImageTiling::OPTIMAL,
            vk::FormatFeatureFlags::COLOR_ATTACHMENT
        ));

        assert!(!format_supports_feature(
            props,
            vk::ImageTiling::LINEAR,
            vk::FormatFeatureFlags::COLOR_ATTACHMENT
        ));
    }

    #[test]
    fn test_timeout_constants() {
        assert_eq!(timeouts::INFINITE, u64::MAX);
        assert_eq!(timeouts::ONE_SECOND, 1_000_000_000);
        assert_eq!(timeouts::ONE_MS, 1_000_000);
    }
}