//! Шейдерные модули — перенос `simv::vk::Shader`. //! //! Главное отличие от C++-версии: SPIR-V не ищется на диске, а зашит в исполняемый //! файл на этапе сборки (см. `build.rs`). Вместе с `FindSpvPath` исчезли и //! POST_BUILD-копирование каталога `spirv/`, и требование запускать редактор из //! каталога бинаря, и целый класс ошибок «шейдер не найден» во время выполнения: //! отсутствующий шейдер теперь ломает сборку, а не запуск. use std::io::Cursor; use ash::vk; use crate::error::{Ctx, Result}; /// Скомпилированный SPIR-V, вшитый в бинарь. Пути соответствуют раскладке /// `shaders/editor/` в корне репозитория. pub mod spirv { pub const MESH_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/mesh.vert.spv")); pub const MESH_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/mesh.frag.spv")); pub const GRID_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/grid.vert.spv")); pub const GRID_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/editor/grid.frag.spv")); } /// Владеющая обёртка над `VkShaderModule`: уничтожается на выходе из области /// видимости, в том числе если сборка конвейера завершилась ошибкой. pub struct ShaderModule { device: ash::Device, handle: vk::ShaderModule, } impl ShaderModule { pub fn new(device: &ash::Device, spirv_bytes: &[u8]) -> Result { // `read_spv` заодно снимает вопрос выравнивания: `include_bytes!` даёт срез // байтов, а Vulkan хочет 32-битные слова. let words = ash::util::read_spv(&mut Cursor::new(spirv_bytes)) .map_err(|e| crate::error::VkError::Other(format!("битый SPIR-V: {e}")))?; let ci = vk::ShaderModuleCreateInfo::default().code(&words); let handle = unsafe { device.create_shader_module(&ci, None) }.ctx("vkCreateShaderModule")?; Ok(Self { device: device.clone(), handle, }) } pub fn handle(&self) -> vk::ShaderModule { self.handle } } impl Drop for ShaderModule { fn drop(&mut self) { unsafe { self.device.destroy_shader_module(self.handle, None) }; } }