demo_vk/graphics/vulkan/
spirv.rs

1use {
2    crate::{
3        graphics::vulkan::{raii, VulkanContext},
4        trace,
5    },
6    anyhow::{bail, Result},
7    ash::vk,
8};
9
10/// Creates a Vulkan shader module from the provided SPIR-V code.
11///
12/// SPIR-V is expected to be a valid array of u32 words. If the provided bytes
13/// cannot be reinterpreted as words, this method will return an error.
14pub fn spirv_module(
15    ctx: &VulkanContext,
16    shader_bytes: &[u8],
17) -> Result<raii::ShaderModule> {
18    let words = spirv_words(shader_bytes)?;
19    raii::ShaderModule::new(
20        "ShaderCompiler SPIR-V Module",
21        ctx.device.clone(),
22        &vk::ShaderModuleCreateInfo {
23            code_size: words.len() * 4,
24            p_code: words.as_ptr(),
25            ..Default::default()
26        },
27    )
28}
29
30/// Convert an unaligned slice of bytes into an aligned chunk of u32 words.
31///
32/// This is needed because SPIRV is expected to always take the form of 32
33/// bytes. It is not always safe to simply reinterpret a slice of u8's due to
34/// alignment.
35pub fn spirv_words(shader_bytes: &[u8]) -> Result<Vec<u32>> {
36    if !shader_bytes.len().is_multiple_of(4) {
37        bail!(trace!(
38            "Invalid length for compiled SPIRV bytes! {}",
39            shader_bytes.len()
40        )());
41    }
42    let shader_words: Vec<u32> = shader_bytes
43        .chunks(4)
44        .map(|w| u32::from_le_bytes([w[0], w[1], w[2], w[3]]))
45        .collect();
46
47    Ok(shader_words)
48}