demo_vk/graphics/vulkan/
spirv.rs

1use {
2    crate::{
3        graphics::vulkan::{raii, VulkanContext},
4        unwrap_here,
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 = unwrap_here!(
19        "Repack SPIR-V bytes into words",
20        spirv_words(shader_bytes)
21    );
22    raii::ShaderModule::new(
23        "ShaderCompiler SPIR-V Module",
24        ctx.device.clone(),
25        &vk::ShaderModuleCreateInfo {
26            code_size: words.len() * 4,
27            p_code: words.as_ptr(),
28            ..Default::default()
29        },
30    )
31}
32
33/// Convert an unaligned slice of bytes into an aligned chunk of u32 words.
34///
35/// This is needed because SPIRV is expected to always take the form of 32
36/// bytes. It is not always safe to simply reinterpret a slice of u8's due to
37/// alignment.
38pub fn spirv_words(shader_bytes: &[u8]) -> Result<Vec<u32>> {
39    if !shader_bytes.len().is_multiple_of(4) {
40        bail!(
41            "Expected shader_bytes.len() to be a multiple of 4, but was: {}",
42            shader_bytes.len()
43        );
44    }
45    let shader_words: Vec<u32> = shader_bytes
46        .chunks(4)
47        .map(|w| u32::from_le_bytes([w[0], w[1], w[2], w[3]]))
48        .collect();
49
50    Ok(shader_words)
51}